flowent 0.0.4 → 0.0.5
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/README.md +1 -1
- package/backend/README.md +74 -0
- package/backend/pyproject.toml +2 -1
- package/backend/src/flowent/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/_version.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/access.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/agent.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/assistant_commands.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/cli.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/config.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/events.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/graph_runtime.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/graph_service.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/image_assets.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/logging.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/main.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/mcp_service.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/model_metadata.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/network.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/{stats_service.cpython-313.pyc → observability_service.cpython-313.pyc} +0 -0
- package/backend/src/flowent/__pycache__/registry.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/role_management.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/runtime.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/sandbox.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/security.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/settings.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/settings_management.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/state_db.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/workspace_store.cpython-313.pyc +0 -0
- package/backend/src/flowent/agent.py +364 -52
- package/backend/src/flowent/assistant_commands.py +31 -22
- package/backend/src/flowent/channels/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/channels/__pycache__/telegram.cpython-313.pyc +0 -0
- package/backend/src/flowent/channels/telegram.py +4 -4
- package/backend/src/flowent/graph_service.py +1307 -145
- package/backend/src/flowent/mcp_service.py +21 -7
- package/backend/src/flowent/model_metadata.py +4 -0
- package/backend/src/flowent/models/__init__.py +6 -2
- package/backend/src/flowent/models/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/agent.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/base.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/blueprint.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/content.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/delta.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/event.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/graph.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/history.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/llm.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/message.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/tab.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/todo.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/agent.py +1 -0
- package/backend/src/flowent/models/graph.py +44 -9
- package/backend/src/flowent/models/history.py +73 -15
- package/backend/src/flowent/models/llm.py +1 -0
- package/backend/src/flowent/models/message.py +6 -0
- package/backend/src/flowent/models/tab.py +38 -1
- package/backend/src/flowent/{stats_service.py → observability_service.py} +4 -4
- package/backend/src/flowent/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/prompts/__pycache__/common.cpython-313.pyc +0 -0
- package/backend/src/flowent/prompts/__pycache__/steward.cpython-313.pyc +0 -0
- package/backend/src/flowent/prompts/common.py +2 -2
- package/backend/src/flowent/prompts/steward.py +2 -2
- package/backend/src/flowent/providers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/anthropic.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/base_url.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/configuration.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/content.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/errors.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/gateway.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/headers.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/management.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/openai.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/openai_responses.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/registry.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/sse.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/thinking.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/configuration.py +7 -0
- package/backend/src/flowent/role_management.py +12 -0
- package/backend/src/flowent/routes/__init__.py +0 -2
- package/backend/src/flowent/routes/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/access.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/assistant.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/image_assets.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/mcp.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/meta.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/nodes.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/prompts.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/providers_route.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/roles.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/settings.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/tabs.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/ws.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/assistant.py +4 -4
- package/backend/src/flowent/routes/nodes.py +54 -6
- package/backend/src/flowent/routes/providers_route.py +1 -0
- package/backend/src/flowent/routes/roles.py +1 -1
- package/backend/src/flowent/routes/settings.py +4 -0
- package/backend/src/flowent/routes/tabs.py +29 -11
- package/backend/src/flowent/runtime.py +7 -30
- package/backend/src/flowent/security.py +23 -8
- package/backend/src/flowent/settings.py +56 -5
- package/backend/src/flowent/settings_management.py +12 -0
- package/backend/src/flowent/static/assets/AssistantPage-VBohhz4d.js +1 -0
- package/backend/src/flowent/static/assets/ChannelsPage-CIydPZA_.js +1 -0
- package/backend/src/flowent/static/assets/McpPage-CHPm2TPY.js +7 -0
- package/backend/src/flowent/static/assets/PageScaffold-DteOA8V7.js +1 -0
- package/backend/src/flowent/static/assets/PromptsPage-CSmJ3sZg.js +1 -0
- package/backend/src/flowent/static/assets/ProvidersPage-sl2jeG4e.js +3 -0
- package/backend/src/flowent/static/assets/RolesPage-DCe7W6Km.js +1 -0
- package/backend/src/flowent/static/assets/SettingsPage-Bix9e63E.js +3 -0
- package/backend/src/flowent/static/assets/ToolsPage-favNkj5C.js +1 -0
- package/backend/src/flowent/static/assets/WorkspaceCommandDialog-DRS6wiD6.js +1 -0
- package/backend/src/flowent/static/assets/WorkspacePage-KuaDjt_D.js +3 -0
- package/backend/src/flowent/static/assets/WorkspacePanels-BZxBw8M5.js +1 -0
- package/backend/src/flowent/static/assets/alert-dialog-DIBUCmqM.js +1 -0
- package/backend/src/flowent/static/assets/{dialog-BeGSweF6.js → dialog-BOvHIBrg.js} +1 -1
- package/backend/src/flowent/static/assets/index-Biio-CoI.js +10 -0
- package/backend/src/flowent/static/assets/index-CmQvO7sl.css +1 -0
- package/backend/src/flowent/static/assets/modelParams-DcEhGnu0.js +1 -0
- package/backend/src/flowent/static/assets/roles-BbIEIMeG.js +1 -0
- package/backend/src/flowent/static/assets/select-D9SwnlXF.js +1 -0
- package/backend/src/flowent/static/assets/surface-Bzr1FRG4.js +1 -0
- package/backend/src/flowent/static/assets/{ui-vendor-Dg9NNnWX.js → ui-vendor-UazN8rcv.js} +15 -15
- package/backend/src/flowent/static/index.html +3 -4
- package/backend/src/flowent/tools/__init__.py +76 -2
- package/backend/src/flowent/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/connect.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/contacts.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/create_agent.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/create_tab.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/delete_tab.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/edit.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/exec.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/fetch.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/idle.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/list_roles.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/list_tabs.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/list_tools.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/manage_prompts.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/manage_providers.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/manage_roles.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/manage_settings.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/mcp.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/read.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/send.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/set_permissions.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/sleep.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/todo.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/connect.py +10 -66
- package/backend/src/flowent/tools/contacts.py +1 -1
- package/backend/src/flowent/tools/create_agent.py +9 -88
- package/backend/src/flowent/tools/create_tab.py +7 -5
- package/backend/src/flowent/tools/exec.py +3 -2
- package/backend/src/flowent/tools/list_roles.py +29 -4
- package/backend/src/flowent/tools/list_tabs.py +4 -0
- package/backend/src/flowent/tools/list_tools.py +5 -1
- package/backend/src/flowent/tools/manage_settings.py +18 -0
- package/backend/src/flowent/tools/send.py +21 -3
- package/backend/src/flowent/tools/set_permissions.py +21 -6
- package/backend/tests/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_access_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_assistant_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_frontend_mounting.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_mcp_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_meta_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_nodes_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_prompts_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_roles_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_tabs_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/test_assistant_api.py +1 -1
- package/backend/tests/integration/api/test_nodes_api.py +257 -21
- package/backend/tests/integration/api/test_roles_api.py +3 -2
- package/backend/tests/integration/api/test_tabs_api.py +312 -11
- package/backend/tests/unit/__pycache__/test_access.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/__pycache__/test_graph_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/__pycache__/test_network.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/__pycache__/test_state_sqlite_storage.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/__pycache__/test_workspace_store.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/agent/__pycache__/test_agent_public_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/agent/__pycache__/test_agent_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/agent/test_agent_public_api.py +162 -71
- package/backend/tests/unit/agent/test_agent_runtime.py +285 -69
- package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/prompts/__pycache__/test_prompts.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/prompts/test_prompts.py +3 -2
- package/backend/tests/unit/providers/__pycache__/test_anthropic_provider.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_errors.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_extract_delta_parts.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_openai_provider.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_openai_responses.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_provider_gateway.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_think_tag_parser.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/routes/__pycache__/test_prompts_routes.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/routes/__pycache__/test_providers_route.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/routes/__pycache__/test_roles_routes.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/routes/__pycache__/test_settings_routes.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/routes/test_providers_route.py +2 -0
- package/backend/tests/unit/routes/test_roles_routes.py +109 -0
- package/backend/tests/unit/routes/test_settings_routes.py +4 -0
- package/backend/tests/unit/runtime/__pycache__/test_bootstrap_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/runtime/test_bootstrap_runtime.py +8 -18
- package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/security/__pycache__/test_security.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/security/test_security.py +16 -2
- package/backend/tests/unit/settings/__pycache__/test_settings_roles.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/settings/test_settings_roles.py +40 -0
- package/backend/tests/unit/test_state_sqlite_storage.py +67 -1
- package/backend/tests/unit/tools/__pycache__/test_connect_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_create_agent_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_delete_tab_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_edit_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_exec_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_fetch_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_manage_prompts_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_manage_providers_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_manage_roles_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_manage_settings_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_read_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_set_permissions_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_todo_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_tool_registry.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/test_connect_tool.py +2 -3
- package/backend/tests/unit/tools/test_create_agent_tool.py +9 -97
- package/backend/tests/unit/tools/test_delete_tab_tool.py +33 -0
- package/backend/tests/unit/tools/test_manage_providers_tool.py +2 -0
- package/backend/tests/unit/tools/test_manage_settings_tool.py +3 -0
- package/backend/tests/unit/tools/test_set_permissions_tool.py +216 -12
- package/backend/tests/unit/tools/test_tool_registry.py +103 -0
- package/backend/uv.lock +1 -1
- package/dist/frontend/assets/AssistantPage-VBohhz4d.js +1 -0
- package/dist/frontend/assets/ChannelsPage-CIydPZA_.js +1 -0
- package/dist/frontend/assets/McpPage-CHPm2TPY.js +7 -0
- package/dist/frontend/assets/PageScaffold-DteOA8V7.js +1 -0
- package/dist/frontend/assets/PromptsPage-CSmJ3sZg.js +1 -0
- package/dist/frontend/assets/ProvidersPage-sl2jeG4e.js +3 -0
- package/dist/frontend/assets/RolesPage-DCe7W6Km.js +1 -0
- package/dist/frontend/assets/SettingsPage-Bix9e63E.js +3 -0
- package/dist/frontend/assets/ToolsPage-favNkj5C.js +1 -0
- package/dist/frontend/assets/WorkspaceCommandDialog-DRS6wiD6.js +1 -0
- package/dist/frontend/assets/WorkspacePage-KuaDjt_D.js +3 -0
- package/dist/frontend/assets/WorkspacePanels-BZxBw8M5.js +1 -0
- package/dist/frontend/assets/alert-dialog-DIBUCmqM.js +1 -0
- package/dist/frontend/assets/{dialog-BeGSweF6.js → dialog-BOvHIBrg.js} +1 -1
- package/dist/frontend/assets/index-Biio-CoI.js +10 -0
- package/dist/frontend/assets/index-CmQvO7sl.css +1 -0
- package/dist/frontend/assets/modelParams-DcEhGnu0.js +1 -0
- package/dist/frontend/assets/roles-BbIEIMeG.js +1 -0
- package/dist/frontend/assets/select-D9SwnlXF.js +1 -0
- package/dist/frontend/assets/surface-Bzr1FRG4.js +1 -0
- package/dist/frontend/assets/{ui-vendor-Dg9NNnWX.js → ui-vendor-UazN8rcv.js} +15 -15
- package/dist/frontend/index.html +3 -4
- package/package.json +3 -3
- package/backend/src/flowent/routes/__pycache__/stats.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/stats.py +0 -229
- package/backend/src/flowent/static/assets/AssistantPage-B3Xc08AS.js +0 -1
- package/backend/src/flowent/static/assets/ChannelsPage-ByLd28xk.js +0 -1
- package/backend/src/flowent/static/assets/HomePage-C0hAx9_l.js +0 -3
- package/backend/src/flowent/static/assets/McpPage-DkrYLvBv.js +0 -7
- package/backend/src/flowent/static/assets/PageScaffold-D4jO9ooX.js +0 -1
- package/backend/src/flowent/static/assets/PromptsPage-DWA7rRJd.js +0 -1
- package/backend/src/flowent/static/assets/ProvidersPage-PUWT8seJ.js +0 -3
- package/backend/src/flowent/static/assets/RolesPage-CqcclGRw.js +0 -1
- package/backend/src/flowent/static/assets/SettingsPage-8tS2cJgX.js +0 -3
- package/backend/src/flowent/static/assets/StatsPage-BX9khYzu.js +0 -1
- package/backend/src/flowent/static/assets/ToolsPage-9Tl9FdeD.js +0 -1
- package/backend/src/flowent/static/assets/WorkspaceCommandDialog-CCXxjDL8.js +0 -1
- package/backend/src/flowent/static/assets/WorkspacePanels-aMdJ7ZH7.js +0 -1
- package/backend/src/flowent/static/assets/alert-dialog-kFYVQ7oX.js +0 -1
- package/backend/src/flowent/static/assets/badge-74-3jsCg.js +0 -1
- package/backend/src/flowent/static/assets/constants-XUzFf6i1.js +0 -1
- package/backend/src/flowent/static/assets/index-BHC1Vhy8.css +0 -1
- package/backend/src/flowent/static/assets/index-CL1ALZ3r.js +0 -10
- package/backend/src/flowent/static/assets/modelParams-CaHd0903.js +0 -1
- package/backend/src/flowent/static/assets/roles-2OLDeTc5.js +0 -1
- package/backend/src/flowent/static/assets/select-DL_LPeDj.js +0 -1
- package/backend/src/flowent/static/assets/shared-CMxbpLeQ.js +0 -1
- package/backend/tests/unit/routes/__pycache__/test_stats_routes.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/routes/test_stats_routes.py +0 -149
- package/dist/frontend/assets/AssistantPage-B3Xc08AS.js +0 -1
- package/dist/frontend/assets/ChannelsPage-ByLd28xk.js +0 -1
- package/dist/frontend/assets/HomePage-C0hAx9_l.js +0 -3
- package/dist/frontend/assets/McpPage-DkrYLvBv.js +0 -7
- package/dist/frontend/assets/PageScaffold-D4jO9ooX.js +0 -1
- package/dist/frontend/assets/PromptsPage-DWA7rRJd.js +0 -1
- package/dist/frontend/assets/ProvidersPage-PUWT8seJ.js +0 -3
- package/dist/frontend/assets/RolesPage-CqcclGRw.js +0 -1
- package/dist/frontend/assets/SettingsPage-8tS2cJgX.js +0 -3
- package/dist/frontend/assets/StatsPage-BX9khYzu.js +0 -1
- package/dist/frontend/assets/ToolsPage-9Tl9FdeD.js +0 -1
- package/dist/frontend/assets/WorkspaceCommandDialog-CCXxjDL8.js +0 -1
- package/dist/frontend/assets/WorkspacePanels-aMdJ7ZH7.js +0 -1
- package/dist/frontend/assets/alert-dialog-kFYVQ7oX.js +0 -1
- package/dist/frontend/assets/badge-74-3jsCg.js +0 -1
- package/dist/frontend/assets/constants-XUzFf6i1.js +0 -1
- package/dist/frontend/assets/index-BHC1Vhy8.css +0 -1
- package/dist/frontend/assets/index-CL1ALZ3r.js +0 -10
- package/dist/frontend/assets/modelParams-CaHd0903.js +0 -1
- package/dist/frontend/assets/roles-2OLDeTc5.js +0 -1
- package/dist/frontend/assets/select-DL_LPeDj.js +0 -1
- package/dist/frontend/assets/shared-CMxbpLeQ.js +0 -1
- /package/backend/src/flowent/static/assets/{datetime-m6_O_Ci9.js → datetime-eJqd0V2S.js} +0 -0
- /package/backend/src/flowent/static/assets/{markdown-vendor-DVdy_w12.js → markdown-vendor-C9RtvaJh.js} +0 -0
- /package/backend/src/flowent/static/assets/{triState-DEr3NkXV.js → triState-DgLlKdRR.js} +0 -0
- /package/dist/frontend/assets/{datetime-m6_O_Ci9.js → datetime-eJqd0V2S.js} +0 -0
- /package/dist/frontend/assets/{markdown-vendor-DVdy_w12.js → markdown-vendor-C9RtvaJh.js} +0 -0
- /package/dist/frontend/assets/{triState-DEr3NkXV.js → triState-DgLlKdRR.js} +0 -0
|
@@ -21,14 +21,13 @@
|
|
|
21
21
|
}
|
|
22
22
|
})();
|
|
23
23
|
</script>
|
|
24
|
-
<script type="module" crossorigin src="/assets/index-
|
|
24
|
+
<script type="module" crossorigin src="/assets/index-Biio-CoI.js"></script>
|
|
25
25
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-BYbx6iT9.js">
|
|
26
|
-
<link rel="modulepreload" crossorigin href="/assets/shared-CMxbpLeQ.js">
|
|
27
26
|
<link rel="modulepreload" crossorigin href="/assets/graph-vendor-DRq_-6fV.js">
|
|
28
27
|
<link rel="modulepreload" crossorigin href="/assets/react-vendor-mEs_JJxa.js">
|
|
29
|
-
<link rel="modulepreload" crossorigin href="/assets/ui-vendor-
|
|
28
|
+
<link rel="modulepreload" crossorigin href="/assets/ui-vendor-UazN8rcv.js">
|
|
30
29
|
<link rel="stylesheet" crossorigin href="/assets/graph-vendor-CHpVij2M.css">
|
|
31
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
30
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CmQvO7sl.css">
|
|
32
31
|
</head>
|
|
33
32
|
<body class="min-h-dvh bg-background text-foreground">
|
|
34
33
|
<div id="root"></div>
|
|
@@ -24,6 +24,60 @@ MCP_BROWSING_TOOLS = (
|
|
|
24
24
|
"get_mcp_prompt",
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
+
ASSISTANT_ONLY_TOOLS = frozenset(
|
|
28
|
+
{
|
|
29
|
+
"create_workflow",
|
|
30
|
+
"delete_workflow",
|
|
31
|
+
"list_workflows",
|
|
32
|
+
"manage_providers",
|
|
33
|
+
"manage_roles",
|
|
34
|
+
"manage_settings",
|
|
35
|
+
"manage_prompts",
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
ASSISTANT_ONLY_MCP_TOOL_NAMES = frozenset(
|
|
40
|
+
{
|
|
41
|
+
"create_workflow",
|
|
42
|
+
"create_workflows",
|
|
43
|
+
"delete_workflow",
|
|
44
|
+
"delete_workflows",
|
|
45
|
+
"duplicate_workflow",
|
|
46
|
+
"duplicate_workflows",
|
|
47
|
+
"copy_workflow",
|
|
48
|
+
"copy_workflows",
|
|
49
|
+
"list_workflow",
|
|
50
|
+
"list_workflows",
|
|
51
|
+
"select_workflow",
|
|
52
|
+
"select_workflows",
|
|
53
|
+
"switch_workflow",
|
|
54
|
+
"switch_workflows",
|
|
55
|
+
"manage_provider",
|
|
56
|
+
"manage_providers",
|
|
57
|
+
"manage_role",
|
|
58
|
+
"manage_roles",
|
|
59
|
+
"manage_setting",
|
|
60
|
+
"manage_settings",
|
|
61
|
+
"manage_prompt",
|
|
62
|
+
"manage_prompts",
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def is_assistant_only_tool_name(tool_name: str) -> bool:
|
|
68
|
+
return tool_name in ASSISTANT_ONLY_TOOLS
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def is_assistant_only_mcp_tool_name(tool_name: str) -> bool:
|
|
72
|
+
normalized = tool_name.strip().lower().replace("-", "_")
|
|
73
|
+
candidate = (
|
|
74
|
+
normalized.rsplit("__", 1)[-1] if normalized.startswith("mcp__") else normalized
|
|
75
|
+
)
|
|
76
|
+
return (
|
|
77
|
+
normalized in ASSISTANT_ONLY_MCP_TOOL_NAMES
|
|
78
|
+
or candidate in ASSISTANT_ONLY_MCP_TOOL_NAMES
|
|
79
|
+
)
|
|
80
|
+
|
|
27
81
|
|
|
28
82
|
class Tool(ABC):
|
|
29
83
|
name: str
|
|
@@ -90,10 +144,13 @@ class ToolRegistry:
|
|
|
90
144
|
|
|
91
145
|
def get_tools_for_agent(self, agent: Agent) -> list[Tool]:
|
|
92
146
|
from flowent.mcp_service import mcp_service
|
|
147
|
+
from flowent.models import NodeType
|
|
93
148
|
from flowent.settings import find_role, get_settings
|
|
94
149
|
from flowent.tools.mcp import DynamicMCPTool
|
|
95
150
|
|
|
96
151
|
allowed = set(agent.config.tools) | set(MINIMUM_TOOLS)
|
|
152
|
+
if agent.node_type != NodeType.ASSISTANT:
|
|
153
|
+
allowed -= ASSISTANT_ONLY_TOOLS
|
|
97
154
|
visible_tools = [
|
|
98
155
|
t for t in self._tools.values() if t.name in allowed and t.llm_visible
|
|
99
156
|
]
|
|
@@ -104,6 +161,10 @@ class ToolRegistry:
|
|
|
104
161
|
for descriptor in mcp_service.list_agent_dynamic_tools(agent)
|
|
105
162
|
if descriptor.fully_qualified_id in allowed
|
|
106
163
|
and descriptor.fully_qualified_id not in excluded
|
|
164
|
+
and (
|
|
165
|
+
agent.node_type == NodeType.ASSISTANT
|
|
166
|
+
or not is_assistant_only_mcp_tool_name(descriptor.tool_name)
|
|
167
|
+
)
|
|
107
168
|
]
|
|
108
169
|
return [*visible_tools, *dynamic_tools]
|
|
109
170
|
|
|
@@ -174,7 +235,10 @@ def build_tool_registry() -> ToolRegistry:
|
|
|
174
235
|
return reg
|
|
175
236
|
|
|
176
237
|
|
|
177
|
-
def list_agent_visible_tool_descriptors(
|
|
238
|
+
def list_agent_visible_tool_descriptors(
|
|
239
|
+
*,
|
|
240
|
+
include_assistant_only: bool = True,
|
|
241
|
+
) -> list[dict[str, Any]]:
|
|
178
242
|
registry = build_tool_registry()
|
|
179
243
|
from flowent.mcp_service import mcp_service
|
|
180
244
|
|
|
@@ -182,6 +246,8 @@ def list_agent_visible_tool_descriptors() -> list[dict[str, Any]]:
|
|
|
182
246
|
for tool in registry.list_tools(agent_visible_only=True):
|
|
183
247
|
if tool.name.startswith("mcp__"):
|
|
184
248
|
continue
|
|
249
|
+
if not include_assistant_only and is_assistant_only_tool_name(tool.name):
|
|
250
|
+
continue
|
|
185
251
|
schema = tool.to_schema()
|
|
186
252
|
function = schema.get("function")
|
|
187
253
|
parameters = (
|
|
@@ -197,5 +263,13 @@ def list_agent_visible_tool_descriptors() -> list[dict[str, Any]]:
|
|
|
197
263
|
"source": "builtin",
|
|
198
264
|
}
|
|
199
265
|
)
|
|
200
|
-
|
|
266
|
+
for descriptor in mcp_service.list_discovered_tool_descriptors():
|
|
267
|
+
tool_name = descriptor.get("tool_name")
|
|
268
|
+
if (
|
|
269
|
+
not include_assistant_only
|
|
270
|
+
and isinstance(tool_name, str)
|
|
271
|
+
and is_assistant_only_mcp_tool_name(tool_name)
|
|
272
|
+
):
|
|
273
|
+
continue
|
|
274
|
+
descriptors.append(descriptor)
|
|
201
275
|
return descriptors
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -3,53 +3,13 @@ from __future__ import annotations
|
|
|
3
3
|
import json
|
|
4
4
|
from typing import TYPE_CHECKING, Any, ClassVar
|
|
5
5
|
|
|
6
|
-
from flowent.graph_runtime import resolve_node_ref
|
|
7
6
|
from flowent.models import NodeType
|
|
8
7
|
from flowent.tools import Tool
|
|
9
|
-
from flowent.workspace_store import workspace_store
|
|
10
8
|
|
|
11
9
|
if TYPE_CHECKING:
|
|
12
10
|
from flowent.agent import Agent
|
|
13
11
|
|
|
14
12
|
|
|
15
|
-
def _resolve_workflow_node_ref(
|
|
16
|
-
*,
|
|
17
|
-
tab_id: str,
|
|
18
|
-
node_ref: str,
|
|
19
|
-
) -> tuple[str, str] | None:
|
|
20
|
-
target = resolve_node_ref(node_ref)
|
|
21
|
-
if target is not None and target.config.tab_id == tab_id:
|
|
22
|
-
return target.uuid, target.config.tab_id
|
|
23
|
-
|
|
24
|
-
tab = workspace_store.get_tab(tab_id)
|
|
25
|
-
if tab is None:
|
|
26
|
-
return None
|
|
27
|
-
|
|
28
|
-
definition_nodes = list(tab.definition.nodes)
|
|
29
|
-
exact_match = next(
|
|
30
|
-
(node for node in definition_nodes if node.id == node_ref),
|
|
31
|
-
None,
|
|
32
|
-
)
|
|
33
|
-
if exact_match is not None:
|
|
34
|
-
return exact_match.id, tab_id
|
|
35
|
-
|
|
36
|
-
named_matches = [
|
|
37
|
-
node
|
|
38
|
-
for node in definition_nodes
|
|
39
|
-
if isinstance(node.config.get("name"), str) and node.config["name"] == node_ref
|
|
40
|
-
]
|
|
41
|
-
if len(named_matches) == 1:
|
|
42
|
-
return named_matches[0].id, tab_id
|
|
43
|
-
|
|
44
|
-
if 4 <= len(node_ref) < 36:
|
|
45
|
-
prefix_matches = [
|
|
46
|
-
node for node in definition_nodes if node.id.startswith(node_ref)
|
|
47
|
-
]
|
|
48
|
-
if len(prefix_matches) == 1:
|
|
49
|
-
return prefix_matches[0].id, tab_id
|
|
50
|
-
return None
|
|
51
|
-
|
|
52
|
-
|
|
53
13
|
class ConnectTool(Tool):
|
|
54
14
|
name = "connect"
|
|
55
15
|
description = (
|
|
@@ -76,24 +36,21 @@ class ConnectTool(Tool):
|
|
|
76
36
|
"description": "Target input port key",
|
|
77
37
|
"default": "in",
|
|
78
38
|
},
|
|
79
|
-
"kind": {
|
|
80
|
-
"type": "string",
|
|
81
|
-
"enum": ["control", "data", "event"],
|
|
82
|
-
"description": "Workflow edge kind",
|
|
83
|
-
"default": "control",
|
|
84
|
-
},
|
|
85
39
|
},
|
|
86
40
|
"required": ["from", "to"],
|
|
87
41
|
}
|
|
88
42
|
|
|
89
43
|
def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
|
|
90
|
-
from flowent.graph_service import
|
|
44
|
+
from flowent.graph_service import (
|
|
45
|
+
create_edge,
|
|
46
|
+
is_tab_leader,
|
|
47
|
+
resolve_workflow_node_ref,
|
|
48
|
+
)
|
|
91
49
|
|
|
92
50
|
from_ref = args.get("from")
|
|
93
51
|
to_ref = args.get("to")
|
|
94
52
|
from_port_key = args.get("from_port_key", "out")
|
|
95
53
|
to_port_key = args.get("to_port_key", "in")
|
|
96
|
-
kind = args.get("kind", "control")
|
|
97
54
|
|
|
98
55
|
if not isinstance(from_ref, str) or not from_ref:
|
|
99
56
|
return json.dumps({"error": "from must be a non-empty string"})
|
|
@@ -103,52 +60,39 @@ class ConnectTool(Tool):
|
|
|
103
60
|
return json.dumps({"error": "from_port_key must be a non-empty string"})
|
|
104
61
|
if not isinstance(to_port_key, str) or not to_port_key.strip():
|
|
105
62
|
return json.dumps({"error": "to_port_key must be a non-empty string"})
|
|
106
|
-
if kind not in {"control", "data", "event"}:
|
|
107
|
-
return json.dumps({"error": "kind must be control, data, or event"})
|
|
108
63
|
if not agent.config.tab_id:
|
|
109
64
|
return json.dumps(
|
|
110
65
|
{"error": "Only a workflow Leader may connect task nodes"}
|
|
111
66
|
)
|
|
112
67
|
|
|
113
|
-
|
|
68
|
+
source_id = resolve_workflow_node_ref(
|
|
114
69
|
tab_id=agent.config.tab_id,
|
|
115
70
|
node_ref=from_ref,
|
|
116
71
|
)
|
|
117
|
-
|
|
72
|
+
target_id = resolve_workflow_node_ref(
|
|
118
73
|
tab_id=agent.config.tab_id,
|
|
119
74
|
node_ref=to_ref,
|
|
120
75
|
)
|
|
121
|
-
if
|
|
76
|
+
if source_id is None:
|
|
122
77
|
return json.dumps({"error": f"Node '{from_ref}' not found"})
|
|
123
|
-
if
|
|
78
|
+
if target_id is None:
|
|
124
79
|
return json.dumps({"error": f"Node '{to_ref}' not found"})
|
|
125
80
|
|
|
126
|
-
source_id, source_tab_id = source
|
|
127
|
-
target_id, target_tab_id = target
|
|
128
|
-
if source_tab_id != target_tab_id:
|
|
129
|
-
return json.dumps({"error": "Both nodes must belong to the same workflow"})
|
|
130
81
|
if agent.node_type == NodeType.ASSISTANT:
|
|
131
82
|
return json.dumps(
|
|
132
83
|
{"error": "Assistant may not rewire a Workflow Graph directly"}
|
|
133
84
|
)
|
|
134
|
-
if agent.config.tab_id != source_tab_id:
|
|
135
|
-
return json.dumps(
|
|
136
|
-
{
|
|
137
|
-
"error": "A workflow Leader may only connect peers inside its own workflow"
|
|
138
|
-
}
|
|
139
|
-
)
|
|
140
85
|
if not is_tab_leader(node_id=agent.uuid, tab_id=agent.config.tab_id):
|
|
141
86
|
return json.dumps(
|
|
142
87
|
{"error": "Only a workflow Leader may connect task nodes"}
|
|
143
88
|
)
|
|
144
89
|
|
|
145
90
|
edge, error = create_edge(
|
|
146
|
-
tab_id=
|
|
91
|
+
tab_id=agent.config.tab_id,
|
|
147
92
|
from_node_id=source_id,
|
|
148
93
|
from_port_key=from_port_key,
|
|
149
94
|
to_node_id=target_id,
|
|
150
95
|
to_port_key=to_port_key,
|
|
151
|
-
kind=kind,
|
|
152
96
|
)
|
|
153
97
|
if error is not None or edge is None:
|
|
154
98
|
return json.dumps({"error": error or "Failed to connect nodes"})
|
|
@@ -11,7 +11,7 @@ if TYPE_CHECKING:
|
|
|
11
11
|
|
|
12
12
|
class ContactsTool(Tool):
|
|
13
13
|
name = "contacts"
|
|
14
|
-
description = "List
|
|
14
|
+
description = "List current contacts and output-port paths."
|
|
15
15
|
parameters: ClassVar[dict[str, Any]] = {
|
|
16
16
|
"type": "object",
|
|
17
17
|
"properties": {},
|
|
@@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, Any, ClassVar
|
|
|
5
5
|
|
|
6
6
|
from flowent.graph_service import create_agent_node
|
|
7
7
|
from flowent.models import NodeType
|
|
8
|
-
from flowent.settings import build_assistant_write_dirs, resolve_path
|
|
9
8
|
from flowent.tools import Tool
|
|
10
9
|
|
|
11
10
|
if TYPE_CHECKING:
|
|
@@ -31,16 +30,6 @@ class CreateAgentTool(Tool):
|
|
|
31
30
|
"items": {"type": "string"},
|
|
32
31
|
"description": "Optional additional tools",
|
|
33
32
|
},
|
|
34
|
-
"write_dirs": {
|
|
35
|
-
"type": "array",
|
|
36
|
-
"items": {"type": "string"},
|
|
37
|
-
"description": "Optional writable directories",
|
|
38
|
-
},
|
|
39
|
-
"allow_network": {
|
|
40
|
-
"type": "boolean",
|
|
41
|
-
"description": "Whether the node can access the network",
|
|
42
|
-
"default": False,
|
|
43
|
-
},
|
|
44
33
|
"placement": {
|
|
45
34
|
"type": "string",
|
|
46
35
|
"enum": ["standalone", "after", "between"],
|
|
@@ -68,6 +57,15 @@ class CreateAgentTool(Tool):
|
|
|
68
57
|
def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
|
|
69
58
|
if "workflow_id" in args or "tab_id" in args:
|
|
70
59
|
return json.dumps({"error": "create_agent does not accept workflow_id"})
|
|
60
|
+
if "write_dirs" in args or "allow_network" in args:
|
|
61
|
+
return json.dumps(
|
|
62
|
+
{
|
|
63
|
+
"error": (
|
|
64
|
+
"create_agent uses the current workflow permissions; "
|
|
65
|
+
"update the workflow permissions instead"
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
)
|
|
71
69
|
if "connect_to_creator" in args:
|
|
72
70
|
return json.dumps(
|
|
73
71
|
{
|
|
@@ -77,8 +75,6 @@ class CreateAgentTool(Tool):
|
|
|
77
75
|
role_name = args.get("role_name")
|
|
78
76
|
name = args.get("name")
|
|
79
77
|
tools = args.get("tools", [])
|
|
80
|
-
write_dirs = args.get("write_dirs", [])
|
|
81
|
-
allow_network = args.get("allow_network", False)
|
|
82
78
|
placement = args.get("placement", "standalone")
|
|
83
79
|
after_node_id = args.get("after_node_id")
|
|
84
80
|
between_from_node_id = args.get("between_from_node_id")
|
|
@@ -92,12 +88,6 @@ class CreateAgentTool(Tool):
|
|
|
92
88
|
isinstance(item, str) for item in tools
|
|
93
89
|
):
|
|
94
90
|
return json.dumps({"error": "tools must be an array of strings"})
|
|
95
|
-
if not isinstance(write_dirs, list) or not all(
|
|
96
|
-
isinstance(item, str) for item in write_dirs
|
|
97
|
-
):
|
|
98
|
-
return json.dumps({"error": "write_dirs must be an array of strings"})
|
|
99
|
-
if not isinstance(allow_network, bool):
|
|
100
|
-
return json.dumps({"error": "allow_network must be a boolean"})
|
|
101
91
|
if placement not in {"standalone", "after", "between"}:
|
|
102
92
|
return json.dumps(
|
|
103
93
|
{"error": "placement must be standalone, after, or between"}
|
|
@@ -110,13 +100,6 @@ class CreateAgentTool(Tool):
|
|
|
110
100
|
return json.dumps({"error": "between_from_node_id must be a string"})
|
|
111
101
|
if between_to_node_id is not None and not isinstance(between_to_node_id, str):
|
|
112
102
|
return json.dumps({"error": "between_to_node_id must be a string"})
|
|
113
|
-
try:
|
|
114
|
-
write_dirs = build_assistant_write_dirs(
|
|
115
|
-
write_dirs,
|
|
116
|
-
field_name="write_dirs",
|
|
117
|
-
)
|
|
118
|
-
except ValueError as exc:
|
|
119
|
-
return json.dumps({"error": str(exc)})
|
|
120
103
|
normalized_role_name = role_name.strip()
|
|
121
104
|
if agent.node_type == NodeType.ASSISTANT:
|
|
122
105
|
return json.dumps(
|
|
@@ -142,76 +125,14 @@ class CreateAgentTool(Tool):
|
|
|
142
125
|
|
|
143
126
|
leader = registry.get(leader_id)
|
|
144
127
|
leader_record = workspace_store.get_node_record(leader_id)
|
|
145
|
-
leader_config = (
|
|
146
|
-
agent.config
|
|
147
|
-
if agent.uuid == leader_id
|
|
148
|
-
else (
|
|
149
|
-
leader.config
|
|
150
|
-
if leader is not None
|
|
151
|
-
else (leader_record.config if leader_record is not None else None)
|
|
152
|
-
)
|
|
153
|
-
)
|
|
154
128
|
if leader is None and leader_record is None:
|
|
155
129
|
return json.dumps({"error": f"Leader '{leader_id}' was not found"})
|
|
156
|
-
leader_write_dirs_source = (
|
|
157
|
-
leader_config.write_dirs if leader_config is not None else []
|
|
158
|
-
)
|
|
159
|
-
leader_allow_network = (
|
|
160
|
-
leader_config.allow_network if leader_config is not None else False
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
parent_write_dirs = [resolve_path(path) for path in agent.config.write_dirs]
|
|
164
|
-
invalid_write_dirs = sorted(
|
|
165
|
-
path
|
|
166
|
-
for path in write_dirs
|
|
167
|
-
if not any(
|
|
168
|
-
resolve_path(path).is_relative_to(parent_path)
|
|
169
|
-
for parent_path in parent_write_dirs
|
|
170
|
-
)
|
|
171
|
-
)
|
|
172
|
-
if invalid_write_dirs:
|
|
173
|
-
return json.dumps(
|
|
174
|
-
{
|
|
175
|
-
"error": "write_dirs boundary exceeded: "
|
|
176
|
-
+ ", ".join(invalid_write_dirs)
|
|
177
|
-
}
|
|
178
|
-
)
|
|
179
|
-
if allow_network and not agent.config.allow_network:
|
|
180
|
-
return json.dumps(
|
|
181
|
-
{
|
|
182
|
-
"error": "allow_network boundary exceeded: parent disallows network access"
|
|
183
|
-
}
|
|
184
|
-
)
|
|
185
|
-
leader_write_dirs = [resolve_path(path) for path in leader_write_dirs_source]
|
|
186
|
-
leader_invalid_write_dirs = sorted(
|
|
187
|
-
path
|
|
188
|
-
for path in write_dirs
|
|
189
|
-
if not any(
|
|
190
|
-
resolve_path(path).is_relative_to(parent_path)
|
|
191
|
-
for parent_path in leader_write_dirs
|
|
192
|
-
)
|
|
193
|
-
)
|
|
194
|
-
if leader_invalid_write_dirs:
|
|
195
|
-
return json.dumps(
|
|
196
|
-
{
|
|
197
|
-
"error": "write_dirs boundary exceeded: "
|
|
198
|
-
+ ", ".join(leader_invalid_write_dirs)
|
|
199
|
-
}
|
|
200
|
-
)
|
|
201
|
-
if allow_network and not leader_allow_network:
|
|
202
|
-
return json.dumps(
|
|
203
|
-
{
|
|
204
|
-
"error": "allow_network boundary exceeded: workflow Leader disallows network access"
|
|
205
|
-
}
|
|
206
|
-
)
|
|
207
130
|
|
|
208
131
|
record, error = create_agent_node(
|
|
209
132
|
role_name=normalized_role_name,
|
|
210
133
|
tab_id=agent.config.tab_id,
|
|
211
134
|
name=name,
|
|
212
135
|
tools=tools,
|
|
213
|
-
write_dirs=write_dirs,
|
|
214
|
-
allow_network=allow_network,
|
|
215
136
|
)
|
|
216
137
|
if error is not None or record is None:
|
|
217
138
|
return json.dumps({"error": error or "Failed to create agent"})
|
|
@@ -4,6 +4,7 @@ import json
|
|
|
4
4
|
from typing import TYPE_CHECKING, Any, ClassVar
|
|
5
5
|
|
|
6
6
|
from flowent.graph_service import create_tab, serialize_tab_summary
|
|
7
|
+
from flowent.models import NodeType
|
|
7
8
|
from flowent.tools import Tool
|
|
8
9
|
|
|
9
10
|
if TYPE_CHECKING:
|
|
@@ -12,9 +13,7 @@ if TYPE_CHECKING:
|
|
|
12
13
|
|
|
13
14
|
class CreateTabTool(Tool):
|
|
14
15
|
name = "create_workflow"
|
|
15
|
-
description =
|
|
16
|
-
"Create a new workflow with its bound Leader and empty Workflow Graph."
|
|
17
|
-
)
|
|
16
|
+
description = "Create a new workflow with its own permission boundary."
|
|
18
17
|
parameters: ClassVar[dict[str, Any]] = {
|
|
19
18
|
"type": "object",
|
|
20
19
|
"properties": {
|
|
@@ -24,18 +23,21 @@ class CreateTabTool(Tool):
|
|
|
24
23
|
},
|
|
25
24
|
"allow_network": {
|
|
26
25
|
"type": "boolean",
|
|
27
|
-
"description": "Whether
|
|
26
|
+
"description": "Whether this workflow should have network access (default False)",
|
|
28
27
|
},
|
|
29
28
|
"write_dirs": {
|
|
30
29
|
"type": "array",
|
|
31
30
|
"items": {"type": "string"},
|
|
32
|
-
"description": "List of directory paths
|
|
31
|
+
"description": "List of directory paths this workflow is allowed to write to",
|
|
33
32
|
},
|
|
34
33
|
},
|
|
35
34
|
"required": ["title"],
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
|
|
38
|
+
if agent.node_type != NodeType.ASSISTANT:
|
|
39
|
+
return json.dumps({"error": "Only the Assistant may create workflows"})
|
|
40
|
+
|
|
39
41
|
title = args.get("title")
|
|
40
42
|
allow_network = args.get("allow_network", False)
|
|
41
43
|
write_dirs = args.get("write_dirs", [])
|
|
@@ -36,17 +36,18 @@ class ExecTool(Tool):
|
|
|
36
36
|
|
|
37
37
|
def execute(self, agent: Agent, args: dict[str, Any], **kwargs: Any) -> str:
|
|
38
38
|
on_output: Callable[[str], None] | None = kwargs.get("on_output")
|
|
39
|
+
from flowent.graph_service import resolve_effective_permissions_for_agent
|
|
39
40
|
from flowent.settings import get_runtime_working_dir_path
|
|
40
41
|
|
|
41
42
|
command = args["command"]
|
|
42
43
|
timeout = int(args.get("timeout", 30))
|
|
43
|
-
write_dirs = agent
|
|
44
|
+
allow_network, write_dirs = resolve_effective_permissions_for_agent(agent)
|
|
44
45
|
cwd = Path(get_runtime_working_dir_path())
|
|
45
46
|
|
|
46
47
|
bwrap_cmd = build_bwrap_cmd(
|
|
47
48
|
write_dirs,
|
|
48
49
|
command,
|
|
49
|
-
allow_network=
|
|
50
|
+
allow_network=allow_network,
|
|
50
51
|
cwd=cwd,
|
|
51
52
|
)
|
|
52
53
|
|
|
@@ -19,18 +19,43 @@ class ListRolesTool(Tool):
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
|
|
22
|
+
from flowent.models import NodeType
|
|
22
23
|
from flowent.settings import get_settings, normalize_tool_names
|
|
23
|
-
from flowent.tools import
|
|
24
|
+
from flowent.tools import (
|
|
25
|
+
MINIMUM_TOOLS,
|
|
26
|
+
build_tool_registry,
|
|
27
|
+
is_assistant_only_mcp_tool_name,
|
|
28
|
+
is_assistant_only_tool_name,
|
|
29
|
+
)
|
|
24
30
|
|
|
25
31
|
settings = get_settings()
|
|
26
32
|
tool_registry = build_tool_registry()
|
|
27
|
-
all_tool_names = [
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
all_tool_names: list[str] = []
|
|
34
|
+
for tool in tool_registry.list_tools(agent_visible_only=True):
|
|
35
|
+
descriptor = getattr(tool, "_descriptor", None)
|
|
36
|
+
descriptor_tool_name = getattr(descriptor, "tool_name", None)
|
|
37
|
+
if isinstance(
|
|
38
|
+
descriptor_tool_name, str
|
|
39
|
+
) and is_assistant_only_mcp_tool_name(descriptor_tool_name):
|
|
40
|
+
continue
|
|
41
|
+
all_tool_names.append(tool.name)
|
|
42
|
+
if agent.node_type != NodeType.ASSISTANT:
|
|
43
|
+
all_tool_names = [
|
|
44
|
+
tool_name
|
|
45
|
+
for tool_name in all_tool_names
|
|
46
|
+
if not is_assistant_only_tool_name(tool_name)
|
|
47
|
+
]
|
|
30
48
|
payload: list[dict[str, object]] = []
|
|
31
49
|
|
|
32
50
|
for role in settings.roles:
|
|
33
51
|
builtin_tools = normalize_tool_names([*MINIMUM_TOOLS, *role.included_tools])
|
|
52
|
+
if agent.node_type != NodeType.ASSISTANT:
|
|
53
|
+
builtin_tools = [
|
|
54
|
+
tool_name
|
|
55
|
+
for tool_name in builtin_tools
|
|
56
|
+
if not is_assistant_only_tool_name(tool_name)
|
|
57
|
+
and not is_assistant_only_mcp_tool_name(tool_name)
|
|
58
|
+
]
|
|
34
59
|
optional_tools = [
|
|
35
60
|
tool_name
|
|
36
61
|
for tool_name in all_tool_names
|
|
@@ -10,6 +10,7 @@ from flowent.graph_service import (
|
|
|
10
10
|
list_workflow_nodes,
|
|
11
11
|
serialize_tab_summary,
|
|
12
12
|
)
|
|
13
|
+
from flowent.models import NodeType
|
|
13
14
|
from flowent.tools import Tool
|
|
14
15
|
from flowent.workspace_store import workspace_store
|
|
15
16
|
|
|
@@ -32,6 +33,9 @@ class ListTabsTool(Tool):
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
|
|
36
|
+
if agent.node_type != NodeType.ASSISTANT:
|
|
37
|
+
return json.dumps({"error": "Only the Assistant may list workflows"})
|
|
38
|
+
|
|
35
39
|
workflow_id = args.get("workflow_id")
|
|
36
40
|
if workflow_id is not None and not isinstance(workflow_id, str):
|
|
37
41
|
return json.dumps({"error": "workflow_id must be a string"})
|
|
@@ -19,6 +19,10 @@ class ListToolsTool(Tool):
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
|
|
22
|
+
from flowent.models import NodeType
|
|
22
23
|
from flowent.tools import list_agent_visible_tool_descriptors
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
descriptors = list_agent_visible_tool_descriptors(
|
|
26
|
+
include_assistant_only=agent.node_type == NodeType.ASSISTANT
|
|
27
|
+
)
|
|
28
|
+
return json.dumps(descriptors)
|