flowent 0.0.1 → 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 +20 -9
- package/backend/.python-version +1 -0
- package/backend/README.md +74 -0
- package/backend/pyproject.toml +58 -0
- package/backend/src/flowent/__init__.py +3 -0
- 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__/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/_version.py +7 -0
- package/backend/src/flowent/access.py +247 -0
- package/backend/src/flowent/agent.py +3120 -0
- package/backend/src/flowent/assistant_commands.py +115 -0
- package/backend/src/flowent/channels/__init__.py +3 -0
- 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 +615 -0
- package/backend/src/flowent/cli.py +85 -0
- package/backend/src/flowent/config.py +14 -0
- package/backend/src/flowent/dev.py +3 -0
- package/backend/src/flowent/events.py +157 -0
- package/backend/src/flowent/graph_runtime.py +60 -0
- package/backend/src/flowent/graph_service.py +2508 -0
- package/backend/src/flowent/image_assets.py +356 -0
- package/backend/src/flowent/logging.py +155 -0
- package/backend/src/flowent/main.py +124 -0
- package/backend/src/flowent/mcp_service.py +1918 -0
- package/backend/src/flowent/model_metadata.py +102 -0
- package/backend/src/flowent/models/__init__.py +125 -0
- 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 +34 -0
- package/backend/src/flowent/models/base.py +24 -0
- package/backend/src/flowent/models/blueprint.py +176 -0
- package/backend/src/flowent/models/content.py +164 -0
- package/backend/src/flowent/models/delta.py +44 -0
- package/backend/src/flowent/models/event.py +51 -0
- package/backend/src/flowent/models/graph.py +472 -0
- package/backend/src/flowent/models/history.py +272 -0
- package/backend/src/flowent/models/llm.py +62 -0
- package/backend/src/flowent/models/message.py +33 -0
- package/backend/src/flowent/models/tab.py +85 -0
- package/backend/src/flowent/models/todo.py +10 -0
- package/backend/src/flowent/network.py +146 -0
- package/backend/src/flowent/observability_service.py +218 -0
- package/backend/src/flowent/prompts/__init__.py +67 -0
- 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 +250 -0
- package/backend/src/flowent/prompts/steward.py +64 -0
- package/backend/src/flowent/providers/__init__.py +23 -0
- 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/anthropic.py +468 -0
- package/backend/src/flowent/providers/base_url.py +60 -0
- package/backend/src/flowent/providers/configuration.py +189 -0
- package/backend/src/flowent/providers/content.py +122 -0
- package/backend/src/flowent/providers/errors.py +223 -0
- package/backend/src/flowent/providers/gateway.py +169 -0
- package/backend/src/flowent/providers/gemini.py +447 -0
- package/backend/src/flowent/providers/headers.py +20 -0
- package/backend/src/flowent/providers/management.py +96 -0
- package/backend/src/flowent/providers/ollama.py +293 -0
- package/backend/src/flowent/providers/openai.py +422 -0
- package/backend/src/flowent/providers/openai_responses.py +655 -0
- package/backend/src/flowent/providers/registry.py +144 -0
- package/backend/src/flowent/providers/sse.py +31 -0
- package/backend/src/flowent/providers/thinking.py +79 -0
- package/backend/src/flowent/registry.py +73 -0
- package/backend/src/flowent/role_management.py +267 -0
- package/backend/src/flowent/routes/__init__.py +28 -0
- 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/access.py +48 -0
- package/backend/src/flowent/routes/assistant.py +155 -0
- package/backend/src/flowent/routes/image_assets.py +33 -0
- package/backend/src/flowent/routes/mcp.py +125 -0
- package/backend/src/flowent/routes/meta.py +28 -0
- package/backend/src/flowent/routes/nodes.py +413 -0
- package/backend/src/flowent/routes/prompts.py +46 -0
- package/backend/src/flowent/routes/providers_route.py +365 -0
- package/backend/src/flowent/routes/roles.py +207 -0
- package/backend/src/flowent/routes/settings.py +328 -0
- package/backend/src/flowent/routes/tabs.py +310 -0
- package/backend/src/flowent/routes/ws.py +33 -0
- package/backend/src/flowent/runtime.py +165 -0
- package/backend/src/flowent/sandbox.py +45 -0
- package/backend/src/flowent/security.py +57 -0
- package/backend/src/flowent/settings.py +2518 -0
- package/backend/src/flowent/settings_management.py +298 -0
- package/backend/src/flowent/state_db.py +120 -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/datetime-eJqd0V2S.js +1 -0
- package/backend/src/flowent/static/assets/dialog-BOvHIBrg.js +1 -0
- package/backend/src/flowent/static/assets/elk-worker.min-C9JGDOE-.js +6312 -0
- package/backend/src/flowent/static/assets/graph-vendor-CHpVij2M.css +1 -0
- package/backend/src/flowent/static/assets/graph-vendor-DRq_-6fV.js +7 -0
- 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/layout.worker-jMHqAFbP.js +24 -0
- package/backend/src/flowent/static/assets/markdown-vendor-C9RtvaJh.js +29 -0
- package/backend/src/flowent/static/assets/modelParams-DcEhGnu0.js +1 -0
- package/backend/src/flowent/static/assets/react-vendor-mEs_JJxa.js +9 -0
- package/backend/src/flowent/static/assets/roles-BbIEIMeG.js +1 -0
- package/backend/src/flowent/static/assets/rolldown-runtime-BYbx6iT9.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/triState-DgLlKdRR.js +1 -0
- package/backend/src/flowent/static/assets/ui-vendor-UazN8rcv.js +51 -0
- package/backend/src/flowent/static/index.html +35 -0
- package/backend/src/flowent/tools/__init__.py +275 -0
- 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 +100 -0
- package/backend/src/flowent/tools/contacts.py +22 -0
- package/backend/src/flowent/tools/create_agent.py +191 -0
- package/backend/src/flowent/tools/create_tab.py +61 -0
- package/backend/src/flowent/tools/delete_tab.py +39 -0
- package/backend/src/flowent/tools/edit.py +142 -0
- package/backend/src/flowent/tools/exec.py +118 -0
- package/backend/src/flowent/tools/fetch.py +85 -0
- package/backend/src/flowent/tools/idle.py +27 -0
- package/backend/src/flowent/tools/list_roles.py +75 -0
- package/backend/src/flowent/tools/list_tabs.py +100 -0
- package/backend/src/flowent/tools/list_tools.py +28 -0
- package/backend/src/flowent/tools/manage_prompts.py +102 -0
- package/backend/src/flowent/tools/manage_providers.py +220 -0
- package/backend/src/flowent/tools/manage_roles.py +275 -0
- package/backend/src/flowent/tools/manage_settings.py +364 -0
- package/backend/src/flowent/tools/mcp.py +199 -0
- package/backend/src/flowent/tools/read.py +152 -0
- package/backend/src/flowent/tools/send.py +68 -0
- package/backend/src/flowent/tools/set_permissions.py +99 -0
- package/backend/src/flowent/tools/sleep.py +41 -0
- package/backend/src/flowent/tools/todo.py +51 -0
- package/backend/src/flowent/workspace_store.py +479 -0
- package/backend/tests/__init__.py +0 -0
- 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/conftest.py +6 -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/conftest.py +29 -0
- package/backend/tests/integration/api/test_access_api.py +182 -0
- package/backend/tests/integration/api/test_assistant_api.py +354 -0
- package/backend/tests/integration/api/test_frontend_mounting.py +61 -0
- package/backend/tests/integration/api/test_mcp_api.py +116 -0
- package/backend/tests/integration/api/test_meta_api.py +33 -0
- package/backend/tests/integration/api/test_nodes_api.py +722 -0
- package/backend/tests/integration/api/test_prompts_api.py +47 -0
- package/backend/tests/integration/api/test_roles_api.py +228 -0
- package/backend/tests/integration/api/test_tabs_api.py +802 -0
- 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 +837 -0
- package/backend/tests/unit/agent/test_agent_runtime.py +2942 -0
- package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/channels/test_telegram_channel.py +552 -0
- package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/logging/test_logging.py +132 -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 +570 -0
- 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/providers/test_anthropic_provider.py +185 -0
- package/backend/tests/unit/providers/test_errors.py +68 -0
- package/backend/tests/unit/providers/test_extract_delta_parts.py +22 -0
- package/backend/tests/unit/providers/test_openai_provider.py +139 -0
- package/backend/tests/unit/providers/test_openai_responses.py +402 -0
- package/backend/tests/unit/providers/test_provider_gateway.py +359 -0
- package/backend/tests/unit/providers/test_think_tag_parser.py +36 -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_prompts_routes.py +104 -0
- package/backend/tests/unit/routes/test_providers_route.py +370 -0
- package/backend/tests/unit/routes/test_roles_routes.py +535 -0
- package/backend/tests/unit/routes/test_settings_routes.py +1142 -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 +1002 -0
- package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/sandbox/test_sandbox_tools.py +78 -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 +124 -0
- 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 +751 -0
- package/backend/tests/unit/test_access.py +45 -0
- package/backend/tests/unit/test_cli.py +124 -0
- package/backend/tests/unit/test_graph_runtime.py +72 -0
- package/backend/tests/unit/test_network.py +51 -0
- package/backend/tests/unit/test_state_sqlite_storage.py +159 -0
- package/backend/tests/unit/test_workspace_store.py +231 -0
- 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 +228 -0
- package/backend/tests/unit/tools/test_create_agent_tool.py +436 -0
- package/backend/tests/unit/tools/test_delete_tab_tool.py +116 -0
- package/backend/tests/unit/tools/test_edit_tool.py +115 -0
- package/backend/tests/unit/tools/test_exec_tool.py +81 -0
- package/backend/tests/unit/tools/test_fetch_tool.py +65 -0
- package/backend/tests/unit/tools/test_manage_prompts_tool.py +117 -0
- package/backend/tests/unit/tools/test_manage_providers_tool.py +460 -0
- package/backend/tests/unit/tools/test_manage_roles_tool.py +411 -0
- package/backend/tests/unit/tools/test_manage_settings_tool.py +611 -0
- package/backend/tests/unit/tools/test_read_tool.py +33 -0
- package/backend/tests/unit/tools/test_set_permissions_tool.py +595 -0
- package/backend/tests/unit/tools/test_todo_tool.py +37 -0
- package/backend/tests/unit/tools/test_tool_registry.py +194 -0
- package/backend/uv.lock +1144 -0
- package/bin/flowent.mjs +62 -36
- 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/datetime-eJqd0V2S.js +1 -0
- package/dist/frontend/assets/dialog-BOvHIBrg.js +1 -0
- package/dist/frontend/assets/elk-worker.min-C9JGDOE-.js +6312 -0
- package/dist/frontend/assets/graph-vendor-CHpVij2M.css +1 -0
- package/dist/frontend/assets/graph-vendor-DRq_-6fV.js +7 -0
- package/dist/frontend/assets/index-Biio-CoI.js +10 -0
- package/dist/frontend/assets/index-CmQvO7sl.css +1 -0
- package/dist/frontend/assets/layout.worker-jMHqAFbP.js +24 -0
- package/dist/frontend/assets/markdown-vendor-C9RtvaJh.js +29 -0
- package/dist/frontend/assets/modelParams-DcEhGnu0.js +1 -0
- package/dist/frontend/assets/react-vendor-mEs_JJxa.js +9 -0
- package/dist/frontend/assets/roles-BbIEIMeG.js +1 -0
- package/dist/frontend/assets/rolldown-runtime-BYbx6iT9.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/triState-DgLlKdRR.js +1 -0
- package/dist/frontend/assets/ui-vendor-UazN8rcv.js +51 -0
- package/dist/frontend/index.html +35 -0
- package/package.json +27 -41
- package/dist/.next/BUILD_ID +0 -1
- package/dist/.next/app-path-routes-manifest.json +0 -6
- package/dist/.next/build-manifest.json +0 -20
- package/dist/.next/package.json +0 -1
- package/dist/.next/prerender-manifest.json +0 -114
- package/dist/.next/required-server-files.json +0 -333
- package/dist/.next/routes-manifest.json +0 -69
- package/dist/.next/server/app/_global-error/page/app-paths-manifest.json +0 -3
- package/dist/.next/server/app/_global-error/page/build-manifest.json +0 -16
- package/dist/.next/server/app/_global-error/page/next-font-manifest.json +0 -6
- package/dist/.next/server/app/_global-error/page/react-loadable-manifest.json +0 -1
- package/dist/.next/server/app/_global-error/page/server-reference-manifest.json +0 -4
- package/dist/.next/server/app/_global-error/page.js +0 -9
- package/dist/.next/server/app/_global-error/page.js.map +0 -5
- package/dist/.next/server/app/_global-error/page.js.nft.json +0 -1
- package/dist/.next/server/app/_global-error/page_client-reference-manifest.js +0 -3
- package/dist/.next/server/app/_global-error.html +0 -1
- package/dist/.next/server/app/_global-error.meta +0 -15
- package/dist/.next/server/app/_global-error.rsc +0 -14
- package/dist/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +0 -5
- package/dist/.next/server/app/_global-error.segments/_full.segment.rsc +0 -14
- package/dist/.next/server/app/_global-error.segments/_head.segment.rsc +0 -5
- package/dist/.next/server/app/_global-error.segments/_index.segment.rsc +0 -5
- package/dist/.next/server/app/_global-error.segments/_tree.segment.rsc +0 -1
- package/dist/.next/server/app/_not-found/page/app-paths-manifest.json +0 -3
- package/dist/.next/server/app/_not-found/page/build-manifest.json +0 -16
- package/dist/.next/server/app/_not-found/page/next-font-manifest.json +0 -10
- package/dist/.next/server/app/_not-found/page/react-loadable-manifest.json +0 -1
- package/dist/.next/server/app/_not-found/page/server-reference-manifest.json +0 -4
- package/dist/.next/server/app/_not-found/page.js +0 -13
- package/dist/.next/server/app/_not-found/page.js.map +0 -5
- package/dist/.next/server/app/_not-found/page.js.nft.json +0 -1
- package/dist/.next/server/app/_not-found/page_client-reference-manifest.js +0 -3
- package/dist/.next/server/app/_not-found.html +0 -1
- package/dist/.next/server/app/_not-found.meta +0 -16
- package/dist/.next/server/app/_not-found.rsc +0 -16
- package/dist/.next/server/app/_not-found.segments/_full.segment.rsc +0 -16
- package/dist/.next/server/app/_not-found.segments/_head.segment.rsc +0 -6
- package/dist/.next/server/app/_not-found.segments/_index.segment.rsc +0 -5
- package/dist/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
- package/dist/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -5
- package/dist/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -2
- package/dist/.next/server/app/icon.svg/route/app-paths-manifest.json +0 -3
- package/dist/.next/server/app/icon.svg/route/build-manifest.json +0 -9
- package/dist/.next/server/app/icon.svg/route.js +0 -6
- package/dist/.next/server/app/icon.svg/route.js.map +0 -5
- package/dist/.next/server/app/icon.svg/route.js.nft.json +0 -1
- package/dist/.next/server/app/icon.svg.meta +0 -1
- package/dist/.next/server/app/index.html +0 -1
- package/dist/.next/server/app/index.meta +0 -14
- package/dist/.next/server/app/index.rsc +0 -15
- package/dist/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -5
- package/dist/.next/server/app/index.segments/_full.segment.rsc +0 -15
- package/dist/.next/server/app/index.segments/_head.segment.rsc +0 -6
- package/dist/.next/server/app/index.segments/_index.segment.rsc +0 -5
- package/dist/.next/server/app/index.segments/_tree.segment.rsc +0 -3
- package/dist/.next/server/app/page/app-paths-manifest.json +0 -3
- package/dist/.next/server/app/page/build-manifest.json +0 -16
- package/dist/.next/server/app/page/next-font-manifest.json +0 -10
- package/dist/.next/server/app/page/react-loadable-manifest.json +0 -1
- package/dist/.next/server/app/page/server-reference-manifest.json +0 -4
- package/dist/.next/server/app/page.js +0 -14
- package/dist/.next/server/app/page.js.map +0 -5
- package/dist/.next/server/app/page.js.nft.json +0 -1
- package/dist/.next/server/app/page_client-reference-manifest.js +0 -3
- package/dist/.next/server/app-paths-manifest.json +0 -6
- package/dist/.next/server/chunks/[externals]_next_dist_0arv.vj._.js +0 -3
- package/dist/.next/server/chunks/[root-of-the-server]__0vcj1q1._.js +0 -13
- package/dist/.next/server/chunks/[turbopack]_runtime.js +0 -903
- package/dist/.next/server/chunks/_next-internal_server_app_icon_svg_route_actions_0-0ehc~.js +0 -3
- package/dist/.next/server/chunks/ssr/05w9_next_dist_0ihu0u9._.js +0 -6
- package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_12u3mib._.js +0 -3
- package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_builtin_forbidden_04fbe_..js +0 -3
- package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_builtin_global-error_0brpl_..js +0 -3
- package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_builtin_unauthorized_0~2g66g.js +0 -3
- package/dist/.next/server/chunks/ssr/05w9_next_dist_esm_build_templates_app-page_0~cyr1_.js +0 -4
- package/dist/.next/server/chunks/ssr/05w9_next_dist_esm_build_templates_app-page_1105emf.js +0 -4
- package/dist/.next/server/chunks/ssr/05w9_next_dist_esm_build_templates_app-page_11uhyqv.js +0 -4
- package/dist/.next/server/chunks/ssr/[root-of-the-server]__0.t9_75._.js +0 -33
- package/dist/.next/server/chunks/ssr/[root-of-the-server]__0c0ud_z._.js +0 -3
- package/dist/.next/server/chunks/ssr/[root-of-the-server]__0f9_8d4._.js +0 -3
- package/dist/.next/server/chunks/ssr/[root-of-the-server]__0l5ko41._.js +0 -19
- package/dist/.next/server/chunks/ssr/[root-of-the-server]__0mn6z7i._.js +0 -3
- package/dist/.next/server/chunks/ssr/[root-of-the-server]__0npxxst._.js +0 -33
- package/dist/.next/server/chunks/ssr/[root-of-the-server]__0qjhaca._.js +0 -3
- package/dist/.next/server/chunks/ssr/[root-of-the-server]__0rwgw3s._.js +0 -3
- package/dist/.next/server/chunks/ssr/[turbopack]_runtime.js +0 -903
- package/dist/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_0k77kol.js +0 -3
- package/dist/.next/server/chunks/ssr/_next-internal_server_app__not-found_page_actions_0eq97pa.js +0 -3
- package/dist/.next/server/chunks/ssr/_next-internal_server_app_page_actions_09-gtaw.js +0 -3
- package/dist/.next/server/chunks/ssr/node_modules__pnpm_056~6.6._.js +0 -3
- package/dist/.next/server/chunks/ssr/node_modules__pnpm_0~j0k.e._.js +0 -33
- package/dist/.next/server/functions-config-manifest.json +0 -4
- package/dist/.next/server/middleware-build-manifest.js +0 -20
- package/dist/.next/server/middleware-manifest.json +0 -6
- package/dist/.next/server/next-font-manifest.js +0 -1
- package/dist/.next/server/next-font-manifest.json +0 -13
- package/dist/.next/server/pages/404.html +0 -1
- package/dist/.next/server/pages/500.html +0 -1
- package/dist/.next/server/pages-manifest.json +0 -4
- package/dist/.next/server/prefetch-hints.json +0 -1
- package/dist/.next/server/server-reference-manifest.js +0 -1
- package/dist/.next/server/server-reference-manifest.json +0 -5
- package/dist/.next/static/SMWpxFVvkpYFxY7uuFvGB/_buildManifest.js +0 -11
- package/dist/.next/static/SMWpxFVvkpYFxY7uuFvGB/_clientMiddlewareManifest.js +0 -1
- package/dist/.next/static/SMWpxFVvkpYFxY7uuFvGB/_ssgManifest.js +0 -1
- package/dist/.next/static/chunks/01qk2~bgf76vu.js +0 -1
- package/dist/.next/static/chunks/03~yq9q893hmn.js +0 -1
- package/dist/.next/static/chunks/080queev.r2uy.js +0 -31
- package/dist/.next/static/chunks/0v3lyuj75aq50.js +0 -1
- package/dist/.next/static/chunks/10b~xdx5c-i7s.js +0 -5
- package/dist/.next/static/chunks/14gla2ascffgv.css +0 -2
- package/dist/.next/static/chunks/turbopack-0m-970~qvs7sc.js +0 -1
- package/dist/.next/static/media/7178b3e590c64307-s.11.cyxs5p-0z~.woff2 +0 -0
- package/dist/.next/static/media/8a480f0b521d4e75-s.06d3mdzz5bre_.woff2 +0 -0
- package/dist/.next/static/media/caa3a2e1cccd8315-s.p.16t1db8_9y2o~.woff2 +0 -0
- package/dist/package.json +0 -88
- package/dist/server.js +0 -38
- /package/{dist/.next/server/app/icon.svg.body → backend/src/flowent/static/favicon.svg} +0 -0
- /package/dist/{.next/static/media/icon.0.r~afrtrocz9.svg → frontend/favicon.svg} +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
5
|
+
|
|
6
|
+
from flowent.graph_service import create_tab, serialize_tab_summary
|
|
7
|
+
from flowent.models import NodeType
|
|
8
|
+
from flowent.tools import Tool
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from flowent.agent import Agent
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CreateTabTool(Tool):
|
|
15
|
+
name = "create_workflow"
|
|
16
|
+
description = "Create a new workflow with its own permission boundary."
|
|
17
|
+
parameters: ClassVar[dict[str, Any]] = {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"title": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Human-readable workflow title",
|
|
23
|
+
},
|
|
24
|
+
"allow_network": {
|
|
25
|
+
"type": "boolean",
|
|
26
|
+
"description": "Whether this workflow should have network access (default False)",
|
|
27
|
+
},
|
|
28
|
+
"write_dirs": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": {"type": "string"},
|
|
31
|
+
"description": "List of directory paths this workflow is allowed to write to",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
"required": ["title"],
|
|
35
|
+
}
|
|
36
|
+
|
|
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
|
+
|
|
41
|
+
title = args.get("title")
|
|
42
|
+
allow_network = args.get("allow_network", False)
|
|
43
|
+
write_dirs = args.get("write_dirs", [])
|
|
44
|
+
if not isinstance(title, str) or not title.strip():
|
|
45
|
+
return json.dumps({"error": "title must be a non-empty string"})
|
|
46
|
+
if not isinstance(allow_network, bool):
|
|
47
|
+
return json.dumps({"error": "allow_network must be a boolean"})
|
|
48
|
+
if not isinstance(write_dirs, list) or not all(
|
|
49
|
+
isinstance(x, str) for x in write_dirs
|
|
50
|
+
):
|
|
51
|
+
return json.dumps({"error": "write_dirs must be a list of strings"})
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
tab = create_tab(
|
|
55
|
+
title=title,
|
|
56
|
+
allow_network=allow_network,
|
|
57
|
+
write_dirs=write_dirs,
|
|
58
|
+
)
|
|
59
|
+
except ValueError as exc:
|
|
60
|
+
return json.dumps({"error": str(exc)})
|
|
61
|
+
return json.dumps(serialize_tab_summary(tab))
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
5
|
+
|
|
6
|
+
from flowent.graph_service import delete_tab
|
|
7
|
+
from flowent.models import NodeType
|
|
8
|
+
from flowent.tools import Tool
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from flowent.agent import Agent
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DeleteTabTool(Tool):
|
|
15
|
+
name = "delete_workflow"
|
|
16
|
+
description = "Delete a workflow and clean up its graph."
|
|
17
|
+
parameters: ClassVar[dict[str, Any]] = {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"workflow_id": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "ID of the workflow to delete",
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required": ["workflow_id"],
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
|
|
29
|
+
if agent.node_type != NodeType.ASSISTANT:
|
|
30
|
+
return json.dumps({"error": "Only the Assistant may delete workflows"})
|
|
31
|
+
|
|
32
|
+
workflow_id = args.get("workflow_id")
|
|
33
|
+
if not isinstance(workflow_id, str) or not workflow_id.strip():
|
|
34
|
+
return json.dumps({"error": "workflow_id must be a non-empty string"})
|
|
35
|
+
|
|
36
|
+
deleted, error = delete_tab(tab_id=workflow_id.strip())
|
|
37
|
+
if error is not None or deleted is None:
|
|
38
|
+
return json.dumps({"error": error or "Failed to delete workflow"})
|
|
39
|
+
return json.dumps(deleted)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
8
|
+
|
|
9
|
+
from loguru import logger
|
|
10
|
+
|
|
11
|
+
from flowent.tools import Tool, re_raise_interrupt
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from flowent.agent import Agent
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EditTool(Tool):
|
|
18
|
+
name = "edit"
|
|
19
|
+
description = (
|
|
20
|
+
"Apply one or more line-based edits to a file in order. "
|
|
21
|
+
"Use the read tool first to get the exact line numbers. "
|
|
22
|
+
"Each edit uses 1-indexed inclusive start_line and end_line. "
|
|
23
|
+
"new_content replaces those lines exactly as given (include a trailing newline if needed). "
|
|
24
|
+
"If the file does not exist it will be created."
|
|
25
|
+
)
|
|
26
|
+
parameters: ClassVar[dict[str, Any]] = {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"properties": {
|
|
29
|
+
"path": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Absolute path to the file to edit or create",
|
|
32
|
+
},
|
|
33
|
+
"edits": {
|
|
34
|
+
"type": "array",
|
|
35
|
+
"items": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"properties": {
|
|
38
|
+
"start_line": {
|
|
39
|
+
"type": "integer",
|
|
40
|
+
"description": "First line to replace (1-indexed, inclusive). Use 1 for a new file.",
|
|
41
|
+
},
|
|
42
|
+
"end_line": {
|
|
43
|
+
"type": "integer",
|
|
44
|
+
"description": "Last line to replace (1-indexed, inclusive).",
|
|
45
|
+
},
|
|
46
|
+
"new_content": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "Replacement text for the specified line range. Use an empty string to delete lines.",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
"required": ["start_line", "end_line", "new_content"],
|
|
52
|
+
},
|
|
53
|
+
"description": "Edits to apply in order. Later line numbers are based on the file state after earlier edits.",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
"required": ["path", "edits"],
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def execute(self, agent: Agent, args: dict[str, Any], **kwargs: Any) -> str:
|
|
60
|
+
path_str = args["path"]
|
|
61
|
+
real_path = Path(path_str)
|
|
62
|
+
on_output: Callable[[str], None] | None = kwargs.get("on_output")
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
edits = args["edits"]
|
|
66
|
+
if not isinstance(edits, list):
|
|
67
|
+
return json.dumps({"error": "edits must be an array"})
|
|
68
|
+
|
|
69
|
+
if not real_path.exists():
|
|
70
|
+
os.makedirs(real_path.parent, exist_ok=True)
|
|
71
|
+
real_path.write_text("", encoding="utf-8")
|
|
72
|
+
if on_output is not None:
|
|
73
|
+
on_output(f"Created {path_str}\n")
|
|
74
|
+
|
|
75
|
+
with open(real_path, encoding="utf-8") as f:
|
|
76
|
+
lines = f.readlines()
|
|
77
|
+
|
|
78
|
+
applied_edits: list[dict[str, int | str]] = []
|
|
79
|
+
for index, raw_edit in enumerate(edits, start=1):
|
|
80
|
+
if not isinstance(raw_edit, dict):
|
|
81
|
+
return json.dumps({"error": "each edit must be an object"})
|
|
82
|
+
|
|
83
|
+
start_line = int(raw_edit["start_line"])
|
|
84
|
+
end_line = int(raw_edit["end_line"])
|
|
85
|
+
new_content: str = raw_edit["new_content"]
|
|
86
|
+
|
|
87
|
+
if start_line < 1:
|
|
88
|
+
return json.dumps({"error": "start_line must be >= 1"})
|
|
89
|
+
if end_line < start_line:
|
|
90
|
+
return json.dumps({"error": "end_line must be >= start_line"})
|
|
91
|
+
|
|
92
|
+
total_lines = len(lines)
|
|
93
|
+
if start_line > total_lines + 1:
|
|
94
|
+
return json.dumps(
|
|
95
|
+
{
|
|
96
|
+
"error": f"start_line {start_line} exceeds file length {total_lines}"
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
current_end_line = min(end_line, total_lines)
|
|
101
|
+
if on_output is not None:
|
|
102
|
+
on_output(
|
|
103
|
+
f"Applying edit {index}/{len(edits)} at lines {start_line}-{current_end_line}\n"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
replacement = []
|
|
107
|
+
if new_content:
|
|
108
|
+
replacement = new_content.splitlines(keepends=True)
|
|
109
|
+
if replacement and not replacement[-1].endswith("\n"):
|
|
110
|
+
replacement[-1] += "\n"
|
|
111
|
+
|
|
112
|
+
lines = lines[: start_line - 1] + replacement + lines[current_end_line:]
|
|
113
|
+
applied_edits.append(
|
|
114
|
+
{
|
|
115
|
+
"start_line": start_line,
|
|
116
|
+
"end_line": current_end_line,
|
|
117
|
+
"replacement_line_count": len(replacement),
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
with open(real_path, "w", encoding="utf-8") as f:
|
|
122
|
+
f.writelines(lines)
|
|
123
|
+
if on_output is not None:
|
|
124
|
+
on_output(f"Wrote {path_str}\n")
|
|
125
|
+
|
|
126
|
+
logger.debug(
|
|
127
|
+
"Edited file: {} ({} edit(s), new_line_count={})",
|
|
128
|
+
path_str,
|
|
129
|
+
len(applied_edits),
|
|
130
|
+
len(lines),
|
|
131
|
+
)
|
|
132
|
+
return json.dumps(
|
|
133
|
+
{
|
|
134
|
+
"status": "edited",
|
|
135
|
+
"path": path_str,
|
|
136
|
+
"applied_edits": applied_edits,
|
|
137
|
+
"new_line_count": len(lines),
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
except Exception as e:
|
|
141
|
+
re_raise_interrupt(agent, e)
|
|
142
|
+
return json.dumps({"error": str(e)})
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import subprocess
|
|
5
|
+
import threading
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
9
|
+
|
|
10
|
+
from loguru import logger
|
|
11
|
+
|
|
12
|
+
from flowent.sandbox import build_bwrap_cmd
|
|
13
|
+
from flowent.tools import Tool, re_raise_interrupt
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from flowent.agent import Agent
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ExecTool(Tool):
|
|
20
|
+
name = "exec"
|
|
21
|
+
description = "Execute a shell command in a sandboxed environment."
|
|
22
|
+
parameters: ClassVar[dict[str, Any]] = {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"properties": {
|
|
25
|
+
"command": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Shell command to execute",
|
|
28
|
+
},
|
|
29
|
+
"timeout": {
|
|
30
|
+
"type": "number",
|
|
31
|
+
"description": "Timeout in seconds (default 30)",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
"required": ["command"],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
def execute(self, agent: Agent, args: dict[str, Any], **kwargs: Any) -> str:
|
|
38
|
+
on_output: Callable[[str], None] | None = kwargs.get("on_output")
|
|
39
|
+
from flowent.graph_service import resolve_effective_permissions_for_agent
|
|
40
|
+
from flowent.settings import get_runtime_working_dir_path
|
|
41
|
+
|
|
42
|
+
command = args["command"]
|
|
43
|
+
timeout = int(args.get("timeout", 30))
|
|
44
|
+
allow_network, write_dirs = resolve_effective_permissions_for_agent(agent)
|
|
45
|
+
cwd = Path(get_runtime_working_dir_path())
|
|
46
|
+
|
|
47
|
+
bwrap_cmd = build_bwrap_cmd(
|
|
48
|
+
write_dirs,
|
|
49
|
+
command,
|
|
50
|
+
allow_network=allow_network,
|
|
51
|
+
cwd=cwd,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
logger.debug(
|
|
55
|
+
"Executing command: {} (timeout={}s, cwd={})",
|
|
56
|
+
command,
|
|
57
|
+
timeout,
|
|
58
|
+
cwd,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
proc: subprocess.Popen[str] | None = None
|
|
62
|
+
try:
|
|
63
|
+
proc = subprocess.Popen(
|
|
64
|
+
bwrap_cmd,
|
|
65
|
+
stdout=subprocess.PIPE,
|
|
66
|
+
stderr=subprocess.PIPE,
|
|
67
|
+
text=True,
|
|
68
|
+
cwd=str(cwd),
|
|
69
|
+
)
|
|
70
|
+
agent.set_interrupt_callback(
|
|
71
|
+
lambda: proc.kill() if proc.poll() is None else None
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
stdout_lines: list[str] = []
|
|
75
|
+
stderr_lines: list[str] = []
|
|
76
|
+
|
|
77
|
+
def _read_stderr() -> None:
|
|
78
|
+
assert proc.stderr is not None
|
|
79
|
+
for line in proc.stderr:
|
|
80
|
+
stderr_lines.append(line)
|
|
81
|
+
|
|
82
|
+
stderr_thread = threading.Thread(target=_read_stderr, daemon=True)
|
|
83
|
+
stderr_thread.start()
|
|
84
|
+
|
|
85
|
+
assert proc.stdout is not None
|
|
86
|
+
for line in proc.stdout:
|
|
87
|
+
stdout_lines.append(line)
|
|
88
|
+
if on_output:
|
|
89
|
+
on_output(line)
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
proc.wait(timeout=timeout)
|
|
93
|
+
except subprocess.TimeoutExpired:
|
|
94
|
+
proc.kill()
|
|
95
|
+
proc.wait()
|
|
96
|
+
logger.warning("Command timed out after {}s: {}", timeout, command)
|
|
97
|
+
return json.dumps({"error": f"Command timed out after {timeout}s"})
|
|
98
|
+
|
|
99
|
+
stderr_thread.join(timeout=5)
|
|
100
|
+
|
|
101
|
+
stdout = "".join(stdout_lines)
|
|
102
|
+
stderr = "".join(stderr_lines)
|
|
103
|
+
logger.debug("Command exited with code {}", proc.returncode)
|
|
104
|
+
return json.dumps(
|
|
105
|
+
{
|
|
106
|
+
"returncode": proc.returncode,
|
|
107
|
+
"stdout": stdout[:5000],
|
|
108
|
+
"stderr": stderr[:2000],
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
if proc is not None and proc.poll() is None:
|
|
113
|
+
proc.kill()
|
|
114
|
+
proc.wait()
|
|
115
|
+
re_raise_interrupt(agent, e)
|
|
116
|
+
return json.dumps({"error": str(e)})
|
|
117
|
+
finally:
|
|
118
|
+
agent.set_interrupt_callback(None)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
6
|
+
|
|
7
|
+
from loguru import logger
|
|
8
|
+
|
|
9
|
+
from flowent.network import create_http_session, iter_response_text
|
|
10
|
+
from flowent.tools import Tool, re_raise_interrupt
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from flowent.agent import Agent
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FetchTool(Tool):
|
|
17
|
+
name = "fetch"
|
|
18
|
+
description = "Make an HTTP request."
|
|
19
|
+
parameters: ClassVar[dict[str, Any]] = {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"method": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"enum": ["GET", "POST", "PUT", "DELETE", "PATCH"],
|
|
25
|
+
"description": "HTTP method",
|
|
26
|
+
},
|
|
27
|
+
"url": {"type": "string", "description": "Request URL"},
|
|
28
|
+
"headers": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"description": "Request headers (optional)",
|
|
31
|
+
},
|
|
32
|
+
"body": {"type": "string", "description": "Request body (optional)"},
|
|
33
|
+
},
|
|
34
|
+
"required": ["method", "url"],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
def execute(self, agent: Agent, args: dict[str, Any], **kwargs: Any) -> str:
|
|
38
|
+
method = args["method"]
|
|
39
|
+
url = args["url"]
|
|
40
|
+
on_output: Callable[[str], None] | None = kwargs.get("on_output")
|
|
41
|
+
logger.debug("HTTP {} {}", method, url)
|
|
42
|
+
try:
|
|
43
|
+
with (
|
|
44
|
+
create_http_session(timeout=30.0, impersonate_browser=True) as client,
|
|
45
|
+
client.stream(
|
|
46
|
+
method,
|
|
47
|
+
url,
|
|
48
|
+
headers=args.get("headers"),
|
|
49
|
+
data=args.get("body"),
|
|
50
|
+
) as response,
|
|
51
|
+
):
|
|
52
|
+
close_response = getattr(response, "close", None)
|
|
53
|
+
agent.set_interrupt_callback(
|
|
54
|
+
close_response if callable(close_response) else None
|
|
55
|
+
)
|
|
56
|
+
if on_output is not None:
|
|
57
|
+
on_output(f"{method} {url}\n")
|
|
58
|
+
on_output(f"HTTP {response.status_code}\n\n")
|
|
59
|
+
|
|
60
|
+
remaining = 5000
|
|
61
|
+
body_parts: list[str] = []
|
|
62
|
+
for chunk in iter_response_text(response):
|
|
63
|
+
if not chunk or remaining <= 0:
|
|
64
|
+
continue
|
|
65
|
+
if len(chunk) > remaining:
|
|
66
|
+
chunk = chunk[:remaining]
|
|
67
|
+
body_parts.append(chunk)
|
|
68
|
+
remaining -= len(chunk)
|
|
69
|
+
if on_output is not None:
|
|
70
|
+
on_output(chunk)
|
|
71
|
+
|
|
72
|
+
body = "".join(body_parts)
|
|
73
|
+
logger.debug("HTTP {} {} -> {}", method, url, response.status_code)
|
|
74
|
+
return json.dumps(
|
|
75
|
+
{
|
|
76
|
+
"status_code": response.status_code,
|
|
77
|
+
"body": body,
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
except Exception as e:
|
|
81
|
+
re_raise_interrupt(agent, e)
|
|
82
|
+
logger.warning("HTTP request failed: {} {} - {}", method, url, e)
|
|
83
|
+
return json.dumps({"error": str(e)})
|
|
84
|
+
finally:
|
|
85
|
+
agent.set_interrupt_callback(None)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
4
|
+
|
|
5
|
+
from flowent.tools import Tool
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from flowent.agent import Agent
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class IdleTool(Tool):
|
|
12
|
+
name = "idle"
|
|
13
|
+
description = (
|
|
14
|
+
"Enter idle state. The agent suspends execution until a wake signal re-activates it. "
|
|
15
|
+
"Use this when the current step or task is finished, paused, or blocked, and there is no "
|
|
16
|
+
"immediate next action to take right now. Incoming messages will automatically re-activate you as new input messages, and the tool returns the idle duration when execution resumes."
|
|
17
|
+
)
|
|
18
|
+
parameters: ClassVar[dict[str, Any]] = {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"properties": {},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
def execute(self, agent: Agent, args: dict[str, Any], **kwargs: Any) -> str:
|
|
24
|
+
tool_call_id = kwargs.get("tool_call_id")
|
|
25
|
+
if tool_call_id is not None and not isinstance(tool_call_id, str):
|
|
26
|
+
tool_call_id = None
|
|
27
|
+
return agent.request_idle(tool_call_id=tool_call_id)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
5
|
+
|
|
6
|
+
from flowent.tools import Tool
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from flowent.agent import Agent
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ListRolesTool(Tool):
|
|
13
|
+
name = "list_roles"
|
|
14
|
+
description = "List all registered roles with builtin and optional tool views."
|
|
15
|
+
parameters: ClassVar[dict[str, Any]] = {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {},
|
|
18
|
+
"required": [],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
|
|
22
|
+
from flowent.models import NodeType
|
|
23
|
+
from flowent.settings import get_settings, normalize_tool_names
|
|
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
|
+
)
|
|
30
|
+
|
|
31
|
+
settings = get_settings()
|
|
32
|
+
tool_registry = build_tool_registry()
|
|
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
|
+
]
|
|
48
|
+
payload: list[dict[str, object]] = []
|
|
49
|
+
|
|
50
|
+
for role in settings.roles:
|
|
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
|
+
]
|
|
59
|
+
optional_tools = [
|
|
60
|
+
tool_name
|
|
61
|
+
for tool_name in all_tool_names
|
|
62
|
+
if tool_name not in builtin_tools
|
|
63
|
+
and tool_name not in role.excluded_tools
|
|
64
|
+
]
|
|
65
|
+
payload.append(
|
|
66
|
+
{
|
|
67
|
+
"name": role.name,
|
|
68
|
+
"description": role.description,
|
|
69
|
+
"system_prompt": role.system_prompt,
|
|
70
|
+
"builtin_tools": builtin_tools,
|
|
71
|
+
"optional_tools": optional_tools,
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return json.dumps(payload)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
5
|
+
|
|
6
|
+
from flowent.graph_service import (
|
|
7
|
+
is_tab_leader,
|
|
8
|
+
list_tab_edges,
|
|
9
|
+
list_tab_nodes,
|
|
10
|
+
list_workflow_nodes,
|
|
11
|
+
serialize_tab_summary,
|
|
12
|
+
)
|
|
13
|
+
from flowent.models import NodeType
|
|
14
|
+
from flowent.tools import Tool
|
|
15
|
+
from flowent.workspace_store import workspace_store
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from flowent.agent import Agent
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ListTabsTool(Tool):
|
|
22
|
+
name = "list_workflows"
|
|
23
|
+
description = "List persistent workflows. Optionally include the full Workflow Graph for one workflow."
|
|
24
|
+
parameters: ClassVar[dict[str, Any]] = {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"properties": {
|
|
27
|
+
"workflow_id": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Optional workflow ID to inspect in detail",
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"required": [],
|
|
33
|
+
}
|
|
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
|
+
|
|
39
|
+
workflow_id = args.get("workflow_id")
|
|
40
|
+
if workflow_id is not None and not isinstance(workflow_id, str):
|
|
41
|
+
return json.dumps({"error": "workflow_id must be a string"})
|
|
42
|
+
|
|
43
|
+
if isinstance(workflow_id, str) and workflow_id.strip():
|
|
44
|
+
tab = workspace_store.get_tab(workflow_id.strip())
|
|
45
|
+
if tab is None:
|
|
46
|
+
return json.dumps(
|
|
47
|
+
{"error": f"Workflow '{workflow_id.strip()}' not found"}
|
|
48
|
+
)
|
|
49
|
+
nodes = [
|
|
50
|
+
{
|
|
51
|
+
"id": node.id,
|
|
52
|
+
"node_type": node.type.value,
|
|
53
|
+
"name": (
|
|
54
|
+
node.config["name"]
|
|
55
|
+
if isinstance(node.config.get("name"), str)
|
|
56
|
+
else None
|
|
57
|
+
),
|
|
58
|
+
"role_name": (
|
|
59
|
+
node.config["role_name"]
|
|
60
|
+
if isinstance(node.config.get("role_name"), str)
|
|
61
|
+
else None
|
|
62
|
+
),
|
|
63
|
+
"is_leader": False,
|
|
64
|
+
"position": (
|
|
65
|
+
tab.definition.view.positions[node.id].serialize()
|
|
66
|
+
if node.id in tab.definition.view.positions
|
|
67
|
+
else None
|
|
68
|
+
),
|
|
69
|
+
}
|
|
70
|
+
for node in list_workflow_nodes(tab.id)
|
|
71
|
+
]
|
|
72
|
+
return json.dumps(
|
|
73
|
+
{
|
|
74
|
+
"workflow": serialize_tab_summary(tab),
|
|
75
|
+
"nodes": nodes,
|
|
76
|
+
"runtime_nodes": [
|
|
77
|
+
{
|
|
78
|
+
"id": node.id,
|
|
79
|
+
"name": node.config.name,
|
|
80
|
+
"role_name": node.config.role_name,
|
|
81
|
+
"is_leader": is_tab_leader(
|
|
82
|
+
node_id=node.id,
|
|
83
|
+
tab_id=tab.id,
|
|
84
|
+
),
|
|
85
|
+
"state": node.state.value,
|
|
86
|
+
"position": (
|
|
87
|
+
node.position.serialize()
|
|
88
|
+
if node.position is not None
|
|
89
|
+
else None
|
|
90
|
+
),
|
|
91
|
+
}
|
|
92
|
+
for node in list_tab_nodes(tab.id)
|
|
93
|
+
],
|
|
94
|
+
"edges": [edge.serialize() for edge in list_tab_edges(tab.id)],
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return json.dumps(
|
|
99
|
+
[serialize_tab_summary(tab) for tab in workspace_store.list_tabs()]
|
|
100
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
5
|
+
|
|
6
|
+
from flowent.tools import Tool
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from flowent.agent import Agent
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ListToolsTool(Tool):
|
|
13
|
+
name = "list_tools"
|
|
14
|
+
description = "List all registered tools with their names and descriptions."
|
|
15
|
+
parameters: ClassVar[dict[str, Any]] = {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {},
|
|
18
|
+
"required": [],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
|
|
22
|
+
from flowent.models import NodeType
|
|
23
|
+
from flowent.tools import list_agent_visible_tool_descriptors
|
|
24
|
+
|
|
25
|
+
descriptors = list_agent_visible_tool_descriptors(
|
|
26
|
+
include_assistant_only=agent.node_type == NodeType.ASSISTANT
|
|
27
|
+
)
|
|
28
|
+
return json.dumps(descriptors)
|