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.
Files changed (311) hide show
  1. package/README.md +1 -1
  2. package/backend/README.md +74 -0
  3. package/backend/pyproject.toml +2 -1
  4. package/backend/src/flowent/__pycache__/__init__.cpython-313.pyc +0 -0
  5. package/backend/src/flowent/__pycache__/_version.cpython-313.pyc +0 -0
  6. package/backend/src/flowent/__pycache__/access.cpython-313.pyc +0 -0
  7. package/backend/src/flowent/__pycache__/agent.cpython-313.pyc +0 -0
  8. package/backend/src/flowent/__pycache__/assistant_commands.cpython-313.pyc +0 -0
  9. package/backend/src/flowent/__pycache__/cli.cpython-313.pyc +0 -0
  10. package/backend/src/flowent/__pycache__/config.cpython-313.pyc +0 -0
  11. package/backend/src/flowent/__pycache__/events.cpython-313.pyc +0 -0
  12. package/backend/src/flowent/__pycache__/graph_runtime.cpython-313.pyc +0 -0
  13. package/backend/src/flowent/__pycache__/graph_service.cpython-313.pyc +0 -0
  14. package/backend/src/flowent/__pycache__/image_assets.cpython-313.pyc +0 -0
  15. package/backend/src/flowent/__pycache__/logging.cpython-313.pyc +0 -0
  16. package/backend/src/flowent/__pycache__/main.cpython-313.pyc +0 -0
  17. package/backend/src/flowent/__pycache__/mcp_service.cpython-313.pyc +0 -0
  18. package/backend/src/flowent/__pycache__/model_metadata.cpython-313.pyc +0 -0
  19. package/backend/src/flowent/__pycache__/network.cpython-313.pyc +0 -0
  20. package/backend/src/flowent/__pycache__/{stats_service.cpython-313.pyc → observability_service.cpython-313.pyc} +0 -0
  21. package/backend/src/flowent/__pycache__/registry.cpython-313.pyc +0 -0
  22. package/backend/src/flowent/__pycache__/role_management.cpython-313.pyc +0 -0
  23. package/backend/src/flowent/__pycache__/runtime.cpython-313.pyc +0 -0
  24. package/backend/src/flowent/__pycache__/sandbox.cpython-313.pyc +0 -0
  25. package/backend/src/flowent/__pycache__/security.cpython-313.pyc +0 -0
  26. package/backend/src/flowent/__pycache__/settings.cpython-313.pyc +0 -0
  27. package/backend/src/flowent/__pycache__/settings_management.cpython-313.pyc +0 -0
  28. package/backend/src/flowent/__pycache__/state_db.cpython-313.pyc +0 -0
  29. package/backend/src/flowent/__pycache__/workspace_store.cpython-313.pyc +0 -0
  30. package/backend/src/flowent/agent.py +364 -52
  31. package/backend/src/flowent/assistant_commands.py +31 -22
  32. package/backend/src/flowent/channels/__pycache__/__init__.cpython-313.pyc +0 -0
  33. package/backend/src/flowent/channels/__pycache__/telegram.cpython-313.pyc +0 -0
  34. package/backend/src/flowent/channels/telegram.py +4 -4
  35. package/backend/src/flowent/graph_service.py +1307 -145
  36. package/backend/src/flowent/mcp_service.py +21 -7
  37. package/backend/src/flowent/model_metadata.py +4 -0
  38. package/backend/src/flowent/models/__init__.py +6 -2
  39. package/backend/src/flowent/models/__pycache__/__init__.cpython-313.pyc +0 -0
  40. package/backend/src/flowent/models/__pycache__/agent.cpython-313.pyc +0 -0
  41. package/backend/src/flowent/models/__pycache__/base.cpython-313.pyc +0 -0
  42. package/backend/src/flowent/models/__pycache__/blueprint.cpython-313.pyc +0 -0
  43. package/backend/src/flowent/models/__pycache__/content.cpython-313.pyc +0 -0
  44. package/backend/src/flowent/models/__pycache__/delta.cpython-313.pyc +0 -0
  45. package/backend/src/flowent/models/__pycache__/event.cpython-313.pyc +0 -0
  46. package/backend/src/flowent/models/__pycache__/graph.cpython-313.pyc +0 -0
  47. package/backend/src/flowent/models/__pycache__/history.cpython-313.pyc +0 -0
  48. package/backend/src/flowent/models/__pycache__/llm.cpython-313.pyc +0 -0
  49. package/backend/src/flowent/models/__pycache__/message.cpython-313.pyc +0 -0
  50. package/backend/src/flowent/models/__pycache__/tab.cpython-313.pyc +0 -0
  51. package/backend/src/flowent/models/__pycache__/todo.cpython-313.pyc +0 -0
  52. package/backend/src/flowent/models/agent.py +1 -0
  53. package/backend/src/flowent/models/graph.py +44 -9
  54. package/backend/src/flowent/models/history.py +73 -15
  55. package/backend/src/flowent/models/llm.py +1 -0
  56. package/backend/src/flowent/models/message.py +6 -0
  57. package/backend/src/flowent/models/tab.py +38 -1
  58. package/backend/src/flowent/{stats_service.py → observability_service.py} +4 -4
  59. package/backend/src/flowent/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
  60. package/backend/src/flowent/prompts/__pycache__/common.cpython-313.pyc +0 -0
  61. package/backend/src/flowent/prompts/__pycache__/steward.cpython-313.pyc +0 -0
  62. package/backend/src/flowent/prompts/common.py +2 -2
  63. package/backend/src/flowent/prompts/steward.py +2 -2
  64. package/backend/src/flowent/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  65. package/backend/src/flowent/providers/__pycache__/anthropic.cpython-313.pyc +0 -0
  66. package/backend/src/flowent/providers/__pycache__/base_url.cpython-313.pyc +0 -0
  67. package/backend/src/flowent/providers/__pycache__/configuration.cpython-313.pyc +0 -0
  68. package/backend/src/flowent/providers/__pycache__/content.cpython-313.pyc +0 -0
  69. package/backend/src/flowent/providers/__pycache__/errors.cpython-313.pyc +0 -0
  70. package/backend/src/flowent/providers/__pycache__/gateway.cpython-313.pyc +0 -0
  71. package/backend/src/flowent/providers/__pycache__/headers.cpython-313.pyc +0 -0
  72. package/backend/src/flowent/providers/__pycache__/management.cpython-313.pyc +0 -0
  73. package/backend/src/flowent/providers/__pycache__/openai.cpython-313.pyc +0 -0
  74. package/backend/src/flowent/providers/__pycache__/openai_responses.cpython-313.pyc +0 -0
  75. package/backend/src/flowent/providers/__pycache__/registry.cpython-313.pyc +0 -0
  76. package/backend/src/flowent/providers/__pycache__/sse.cpython-313.pyc +0 -0
  77. package/backend/src/flowent/providers/__pycache__/thinking.cpython-313.pyc +0 -0
  78. package/backend/src/flowent/providers/configuration.py +7 -0
  79. package/backend/src/flowent/role_management.py +12 -0
  80. package/backend/src/flowent/routes/__init__.py +0 -2
  81. package/backend/src/flowent/routes/__pycache__/__init__.cpython-313.pyc +0 -0
  82. package/backend/src/flowent/routes/__pycache__/access.cpython-313.pyc +0 -0
  83. package/backend/src/flowent/routes/__pycache__/assistant.cpython-313.pyc +0 -0
  84. package/backend/src/flowent/routes/__pycache__/image_assets.cpython-313.pyc +0 -0
  85. package/backend/src/flowent/routes/__pycache__/mcp.cpython-313.pyc +0 -0
  86. package/backend/src/flowent/routes/__pycache__/meta.cpython-313.pyc +0 -0
  87. package/backend/src/flowent/routes/__pycache__/nodes.cpython-313.pyc +0 -0
  88. package/backend/src/flowent/routes/__pycache__/prompts.cpython-313.pyc +0 -0
  89. package/backend/src/flowent/routes/__pycache__/providers_route.cpython-313.pyc +0 -0
  90. package/backend/src/flowent/routes/__pycache__/roles.cpython-313.pyc +0 -0
  91. package/backend/src/flowent/routes/__pycache__/settings.cpython-313.pyc +0 -0
  92. package/backend/src/flowent/routes/__pycache__/tabs.cpython-313.pyc +0 -0
  93. package/backend/src/flowent/routes/__pycache__/ws.cpython-313.pyc +0 -0
  94. package/backend/src/flowent/routes/assistant.py +4 -4
  95. package/backend/src/flowent/routes/nodes.py +54 -6
  96. package/backend/src/flowent/routes/providers_route.py +1 -0
  97. package/backend/src/flowent/routes/roles.py +1 -1
  98. package/backend/src/flowent/routes/settings.py +4 -0
  99. package/backend/src/flowent/routes/tabs.py +29 -11
  100. package/backend/src/flowent/runtime.py +7 -30
  101. package/backend/src/flowent/security.py +23 -8
  102. package/backend/src/flowent/settings.py +56 -5
  103. package/backend/src/flowent/settings_management.py +12 -0
  104. package/backend/src/flowent/static/assets/AssistantPage-VBohhz4d.js +1 -0
  105. package/backend/src/flowent/static/assets/ChannelsPage-CIydPZA_.js +1 -0
  106. package/backend/src/flowent/static/assets/McpPage-CHPm2TPY.js +7 -0
  107. package/backend/src/flowent/static/assets/PageScaffold-DteOA8V7.js +1 -0
  108. package/backend/src/flowent/static/assets/PromptsPage-CSmJ3sZg.js +1 -0
  109. package/backend/src/flowent/static/assets/ProvidersPage-sl2jeG4e.js +3 -0
  110. package/backend/src/flowent/static/assets/RolesPage-DCe7W6Km.js +1 -0
  111. package/backend/src/flowent/static/assets/SettingsPage-Bix9e63E.js +3 -0
  112. package/backend/src/flowent/static/assets/ToolsPage-favNkj5C.js +1 -0
  113. package/backend/src/flowent/static/assets/WorkspaceCommandDialog-DRS6wiD6.js +1 -0
  114. package/backend/src/flowent/static/assets/WorkspacePage-KuaDjt_D.js +3 -0
  115. package/backend/src/flowent/static/assets/WorkspacePanels-BZxBw8M5.js +1 -0
  116. package/backend/src/flowent/static/assets/alert-dialog-DIBUCmqM.js +1 -0
  117. package/backend/src/flowent/static/assets/{dialog-BeGSweF6.js → dialog-BOvHIBrg.js} +1 -1
  118. package/backend/src/flowent/static/assets/index-Biio-CoI.js +10 -0
  119. package/backend/src/flowent/static/assets/index-CmQvO7sl.css +1 -0
  120. package/backend/src/flowent/static/assets/modelParams-DcEhGnu0.js +1 -0
  121. package/backend/src/flowent/static/assets/roles-BbIEIMeG.js +1 -0
  122. package/backend/src/flowent/static/assets/select-D9SwnlXF.js +1 -0
  123. package/backend/src/flowent/static/assets/surface-Bzr1FRG4.js +1 -0
  124. package/backend/src/flowent/static/assets/{ui-vendor-Dg9NNnWX.js → ui-vendor-UazN8rcv.js} +15 -15
  125. package/backend/src/flowent/static/index.html +3 -4
  126. package/backend/src/flowent/tools/__init__.py +76 -2
  127. package/backend/src/flowent/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  128. package/backend/src/flowent/tools/__pycache__/connect.cpython-313.pyc +0 -0
  129. package/backend/src/flowent/tools/__pycache__/contacts.cpython-313.pyc +0 -0
  130. package/backend/src/flowent/tools/__pycache__/create_agent.cpython-313.pyc +0 -0
  131. package/backend/src/flowent/tools/__pycache__/create_tab.cpython-313.pyc +0 -0
  132. package/backend/src/flowent/tools/__pycache__/delete_tab.cpython-313.pyc +0 -0
  133. package/backend/src/flowent/tools/__pycache__/edit.cpython-313.pyc +0 -0
  134. package/backend/src/flowent/tools/__pycache__/exec.cpython-313.pyc +0 -0
  135. package/backend/src/flowent/tools/__pycache__/fetch.cpython-313.pyc +0 -0
  136. package/backend/src/flowent/tools/__pycache__/idle.cpython-313.pyc +0 -0
  137. package/backend/src/flowent/tools/__pycache__/list_roles.cpython-313.pyc +0 -0
  138. package/backend/src/flowent/tools/__pycache__/list_tabs.cpython-313.pyc +0 -0
  139. package/backend/src/flowent/tools/__pycache__/list_tools.cpython-313.pyc +0 -0
  140. package/backend/src/flowent/tools/__pycache__/manage_prompts.cpython-313.pyc +0 -0
  141. package/backend/src/flowent/tools/__pycache__/manage_providers.cpython-313.pyc +0 -0
  142. package/backend/src/flowent/tools/__pycache__/manage_roles.cpython-313.pyc +0 -0
  143. package/backend/src/flowent/tools/__pycache__/manage_settings.cpython-313.pyc +0 -0
  144. package/backend/src/flowent/tools/__pycache__/mcp.cpython-313.pyc +0 -0
  145. package/backend/src/flowent/tools/__pycache__/read.cpython-313.pyc +0 -0
  146. package/backend/src/flowent/tools/__pycache__/send.cpython-313.pyc +0 -0
  147. package/backend/src/flowent/tools/__pycache__/set_permissions.cpython-313.pyc +0 -0
  148. package/backend/src/flowent/tools/__pycache__/sleep.cpython-313.pyc +0 -0
  149. package/backend/src/flowent/tools/__pycache__/todo.cpython-313.pyc +0 -0
  150. package/backend/src/flowent/tools/connect.py +10 -66
  151. package/backend/src/flowent/tools/contacts.py +1 -1
  152. package/backend/src/flowent/tools/create_agent.py +9 -88
  153. package/backend/src/flowent/tools/create_tab.py +7 -5
  154. package/backend/src/flowent/tools/exec.py +3 -2
  155. package/backend/src/flowent/tools/list_roles.py +29 -4
  156. package/backend/src/flowent/tools/list_tabs.py +4 -0
  157. package/backend/src/flowent/tools/list_tools.py +5 -1
  158. package/backend/src/flowent/tools/manage_settings.py +18 -0
  159. package/backend/src/flowent/tools/send.py +21 -3
  160. package/backend/src/flowent/tools/set_permissions.py +21 -6
  161. package/backend/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  162. package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  163. package/backend/tests/integration/api/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  164. package/backend/tests/integration/api/__pycache__/test_access_api.cpython-313-pytest-9.0.3.pyc +0 -0
  165. package/backend/tests/integration/api/__pycache__/test_assistant_api.cpython-313-pytest-9.0.3.pyc +0 -0
  166. package/backend/tests/integration/api/__pycache__/test_frontend_mounting.cpython-313-pytest-9.0.3.pyc +0 -0
  167. package/backend/tests/integration/api/__pycache__/test_mcp_api.cpython-313-pytest-9.0.3.pyc +0 -0
  168. package/backend/tests/integration/api/__pycache__/test_meta_api.cpython-313-pytest-9.0.3.pyc +0 -0
  169. package/backend/tests/integration/api/__pycache__/test_nodes_api.cpython-313-pytest-9.0.3.pyc +0 -0
  170. package/backend/tests/integration/api/__pycache__/test_prompts_api.cpython-313-pytest-9.0.3.pyc +0 -0
  171. package/backend/tests/integration/api/__pycache__/test_roles_api.cpython-313-pytest-9.0.3.pyc +0 -0
  172. package/backend/tests/integration/api/__pycache__/test_tabs_api.cpython-313-pytest-9.0.3.pyc +0 -0
  173. package/backend/tests/integration/api/test_assistant_api.py +1 -1
  174. package/backend/tests/integration/api/test_nodes_api.py +257 -21
  175. package/backend/tests/integration/api/test_roles_api.py +3 -2
  176. package/backend/tests/integration/api/test_tabs_api.py +312 -11
  177. package/backend/tests/unit/__pycache__/test_access.cpython-313-pytest-9.0.3.pyc +0 -0
  178. package/backend/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  179. package/backend/tests/unit/__pycache__/test_graph_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  180. package/backend/tests/unit/__pycache__/test_network.cpython-313-pytest-9.0.3.pyc +0 -0
  181. package/backend/tests/unit/__pycache__/test_state_sqlite_storage.cpython-313-pytest-9.0.3.pyc +0 -0
  182. package/backend/tests/unit/__pycache__/test_workspace_store.cpython-313-pytest-9.0.3.pyc +0 -0
  183. package/backend/tests/unit/agent/__pycache__/test_agent_public_api.cpython-313-pytest-9.0.3.pyc +0 -0
  184. package/backend/tests/unit/agent/__pycache__/test_agent_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  185. package/backend/tests/unit/agent/test_agent_public_api.py +162 -71
  186. package/backend/tests/unit/agent/test_agent_runtime.py +285 -69
  187. package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
  188. package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
  189. package/backend/tests/unit/prompts/__pycache__/test_prompts.cpython-313-pytest-9.0.3.pyc +0 -0
  190. package/backend/tests/unit/prompts/test_prompts.py +3 -2
  191. package/backend/tests/unit/providers/__pycache__/test_anthropic_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  192. package/backend/tests/unit/providers/__pycache__/test_errors.cpython-313-pytest-9.0.3.pyc +0 -0
  193. package/backend/tests/unit/providers/__pycache__/test_extract_delta_parts.cpython-313-pytest-9.0.3.pyc +0 -0
  194. package/backend/tests/unit/providers/__pycache__/test_openai_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  195. package/backend/tests/unit/providers/__pycache__/test_openai_responses.cpython-313-pytest-9.0.3.pyc +0 -0
  196. package/backend/tests/unit/providers/__pycache__/test_provider_gateway.cpython-313-pytest-9.0.3.pyc +0 -0
  197. package/backend/tests/unit/providers/__pycache__/test_think_tag_parser.cpython-313-pytest-9.0.3.pyc +0 -0
  198. package/backend/tests/unit/routes/__pycache__/test_prompts_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  199. package/backend/tests/unit/routes/__pycache__/test_providers_route.cpython-313-pytest-9.0.3.pyc +0 -0
  200. package/backend/tests/unit/routes/__pycache__/test_roles_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  201. package/backend/tests/unit/routes/__pycache__/test_settings_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  202. package/backend/tests/unit/routes/test_providers_route.py +2 -0
  203. package/backend/tests/unit/routes/test_roles_routes.py +109 -0
  204. package/backend/tests/unit/routes/test_settings_routes.py +4 -0
  205. package/backend/tests/unit/runtime/__pycache__/test_bootstrap_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  206. package/backend/tests/unit/runtime/test_bootstrap_runtime.py +8 -18
  207. package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
  208. package/backend/tests/unit/security/__pycache__/test_security.cpython-313-pytest-9.0.3.pyc +0 -0
  209. package/backend/tests/unit/security/test_security.py +16 -2
  210. package/backend/tests/unit/settings/__pycache__/test_settings_roles.cpython-313-pytest-9.0.3.pyc +0 -0
  211. package/backend/tests/unit/settings/test_settings_roles.py +40 -0
  212. package/backend/tests/unit/test_state_sqlite_storage.py +67 -1
  213. package/backend/tests/unit/tools/__pycache__/test_connect_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  214. package/backend/tests/unit/tools/__pycache__/test_create_agent_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  215. package/backend/tests/unit/tools/__pycache__/test_delete_tab_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  216. package/backend/tests/unit/tools/__pycache__/test_edit_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  217. package/backend/tests/unit/tools/__pycache__/test_exec_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  218. package/backend/tests/unit/tools/__pycache__/test_fetch_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  219. package/backend/tests/unit/tools/__pycache__/test_manage_prompts_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  220. package/backend/tests/unit/tools/__pycache__/test_manage_providers_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  221. package/backend/tests/unit/tools/__pycache__/test_manage_roles_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  222. package/backend/tests/unit/tools/__pycache__/test_manage_settings_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  223. package/backend/tests/unit/tools/__pycache__/test_read_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  224. package/backend/tests/unit/tools/__pycache__/test_set_permissions_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  225. package/backend/tests/unit/tools/__pycache__/test_todo_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  226. package/backend/tests/unit/tools/__pycache__/test_tool_registry.cpython-313-pytest-9.0.3.pyc +0 -0
  227. package/backend/tests/unit/tools/test_connect_tool.py +2 -3
  228. package/backend/tests/unit/tools/test_create_agent_tool.py +9 -97
  229. package/backend/tests/unit/tools/test_delete_tab_tool.py +33 -0
  230. package/backend/tests/unit/tools/test_manage_providers_tool.py +2 -0
  231. package/backend/tests/unit/tools/test_manage_settings_tool.py +3 -0
  232. package/backend/tests/unit/tools/test_set_permissions_tool.py +216 -12
  233. package/backend/tests/unit/tools/test_tool_registry.py +103 -0
  234. package/backend/uv.lock +1 -1
  235. package/dist/frontend/assets/AssistantPage-VBohhz4d.js +1 -0
  236. package/dist/frontend/assets/ChannelsPage-CIydPZA_.js +1 -0
  237. package/dist/frontend/assets/McpPage-CHPm2TPY.js +7 -0
  238. package/dist/frontend/assets/PageScaffold-DteOA8V7.js +1 -0
  239. package/dist/frontend/assets/PromptsPage-CSmJ3sZg.js +1 -0
  240. package/dist/frontend/assets/ProvidersPage-sl2jeG4e.js +3 -0
  241. package/dist/frontend/assets/RolesPage-DCe7W6Km.js +1 -0
  242. package/dist/frontend/assets/SettingsPage-Bix9e63E.js +3 -0
  243. package/dist/frontend/assets/ToolsPage-favNkj5C.js +1 -0
  244. package/dist/frontend/assets/WorkspaceCommandDialog-DRS6wiD6.js +1 -0
  245. package/dist/frontend/assets/WorkspacePage-KuaDjt_D.js +3 -0
  246. package/dist/frontend/assets/WorkspacePanels-BZxBw8M5.js +1 -0
  247. package/dist/frontend/assets/alert-dialog-DIBUCmqM.js +1 -0
  248. package/dist/frontend/assets/{dialog-BeGSweF6.js → dialog-BOvHIBrg.js} +1 -1
  249. package/dist/frontend/assets/index-Biio-CoI.js +10 -0
  250. package/dist/frontend/assets/index-CmQvO7sl.css +1 -0
  251. package/dist/frontend/assets/modelParams-DcEhGnu0.js +1 -0
  252. package/dist/frontend/assets/roles-BbIEIMeG.js +1 -0
  253. package/dist/frontend/assets/select-D9SwnlXF.js +1 -0
  254. package/dist/frontend/assets/surface-Bzr1FRG4.js +1 -0
  255. package/dist/frontend/assets/{ui-vendor-Dg9NNnWX.js → ui-vendor-UazN8rcv.js} +15 -15
  256. package/dist/frontend/index.html +3 -4
  257. package/package.json +3 -3
  258. package/backend/src/flowent/routes/__pycache__/stats.cpython-313.pyc +0 -0
  259. package/backend/src/flowent/routes/stats.py +0 -229
  260. package/backend/src/flowent/static/assets/AssistantPage-B3Xc08AS.js +0 -1
  261. package/backend/src/flowent/static/assets/ChannelsPage-ByLd28xk.js +0 -1
  262. package/backend/src/flowent/static/assets/HomePage-C0hAx9_l.js +0 -3
  263. package/backend/src/flowent/static/assets/McpPage-DkrYLvBv.js +0 -7
  264. package/backend/src/flowent/static/assets/PageScaffold-D4jO9ooX.js +0 -1
  265. package/backend/src/flowent/static/assets/PromptsPage-DWA7rRJd.js +0 -1
  266. package/backend/src/flowent/static/assets/ProvidersPage-PUWT8seJ.js +0 -3
  267. package/backend/src/flowent/static/assets/RolesPage-CqcclGRw.js +0 -1
  268. package/backend/src/flowent/static/assets/SettingsPage-8tS2cJgX.js +0 -3
  269. package/backend/src/flowent/static/assets/StatsPage-BX9khYzu.js +0 -1
  270. package/backend/src/flowent/static/assets/ToolsPage-9Tl9FdeD.js +0 -1
  271. package/backend/src/flowent/static/assets/WorkspaceCommandDialog-CCXxjDL8.js +0 -1
  272. package/backend/src/flowent/static/assets/WorkspacePanels-aMdJ7ZH7.js +0 -1
  273. package/backend/src/flowent/static/assets/alert-dialog-kFYVQ7oX.js +0 -1
  274. package/backend/src/flowent/static/assets/badge-74-3jsCg.js +0 -1
  275. package/backend/src/flowent/static/assets/constants-XUzFf6i1.js +0 -1
  276. package/backend/src/flowent/static/assets/index-BHC1Vhy8.css +0 -1
  277. package/backend/src/flowent/static/assets/index-CL1ALZ3r.js +0 -10
  278. package/backend/src/flowent/static/assets/modelParams-CaHd0903.js +0 -1
  279. package/backend/src/flowent/static/assets/roles-2OLDeTc5.js +0 -1
  280. package/backend/src/flowent/static/assets/select-DL_LPeDj.js +0 -1
  281. package/backend/src/flowent/static/assets/shared-CMxbpLeQ.js +0 -1
  282. package/backend/tests/unit/routes/__pycache__/test_stats_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  283. package/backend/tests/unit/routes/test_stats_routes.py +0 -149
  284. package/dist/frontend/assets/AssistantPage-B3Xc08AS.js +0 -1
  285. package/dist/frontend/assets/ChannelsPage-ByLd28xk.js +0 -1
  286. package/dist/frontend/assets/HomePage-C0hAx9_l.js +0 -3
  287. package/dist/frontend/assets/McpPage-DkrYLvBv.js +0 -7
  288. package/dist/frontend/assets/PageScaffold-D4jO9ooX.js +0 -1
  289. package/dist/frontend/assets/PromptsPage-DWA7rRJd.js +0 -1
  290. package/dist/frontend/assets/ProvidersPage-PUWT8seJ.js +0 -3
  291. package/dist/frontend/assets/RolesPage-CqcclGRw.js +0 -1
  292. package/dist/frontend/assets/SettingsPage-8tS2cJgX.js +0 -3
  293. package/dist/frontend/assets/StatsPage-BX9khYzu.js +0 -1
  294. package/dist/frontend/assets/ToolsPage-9Tl9FdeD.js +0 -1
  295. package/dist/frontend/assets/WorkspaceCommandDialog-CCXxjDL8.js +0 -1
  296. package/dist/frontend/assets/WorkspacePanels-aMdJ7ZH7.js +0 -1
  297. package/dist/frontend/assets/alert-dialog-kFYVQ7oX.js +0 -1
  298. package/dist/frontend/assets/badge-74-3jsCg.js +0 -1
  299. package/dist/frontend/assets/constants-XUzFf6i1.js +0 -1
  300. package/dist/frontend/assets/index-BHC1Vhy8.css +0 -1
  301. package/dist/frontend/assets/index-CL1ALZ3r.js +0 -10
  302. package/dist/frontend/assets/modelParams-CaHd0903.js +0 -1
  303. package/dist/frontend/assets/roles-2OLDeTc5.js +0 -1
  304. package/dist/frontend/assets/select-DL_LPeDj.js +0 -1
  305. package/dist/frontend/assets/shared-CMxbpLeQ.js +0 -1
  306. /package/backend/src/flowent/static/assets/{datetime-m6_O_Ci9.js → datetime-eJqd0V2S.js} +0 -0
  307. /package/backend/src/flowent/static/assets/{markdown-vendor-DVdy_w12.js → markdown-vendor-C9RtvaJh.js} +0 -0
  308. /package/backend/src/flowent/static/assets/{triState-DEr3NkXV.js → triState-DgLlKdRR.js} +0 -0
  309. /package/dist/frontend/assets/{datetime-m6_O_Ci9.js → datetime-eJqd0V2S.js} +0 -0
  310. /package/dist/frontend/assets/{markdown-vendor-DVdy_w12.js → markdown-vendor-C9RtvaJh.js} +0 -0
  311. /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-CL1ALZ3r.js"></script>
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-Dg9NNnWX.js">
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-BHC1Vhy8.css">
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() -> list[dict[str, Any]]:
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
- descriptors.extend(mcp_service.list_discovered_tool_descriptors())
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
@@ -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 create_edge, is_tab_leader
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
- source = _resolve_workflow_node_ref(
68
+ source_id = resolve_workflow_node_ref(
114
69
  tab_id=agent.config.tab_id,
115
70
  node_ref=from_ref,
116
71
  )
117
- target = _resolve_workflow_node_ref(
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 source is None:
76
+ if source_id is None:
122
77
  return json.dumps({"error": f"Node '{from_ref}' not found"})
123
- if target is None:
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=source_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 the agents this node can message directly right now."
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 the workflow's leader should have network access (default False)",
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 the workflow's leader is allowed to write to",
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.config.write_dirs
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=agent.config.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 MINIMUM_TOOLS, build_tool_registry
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
- tool.name for tool in tool_registry.list_tools(agent_visible_only=True)
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
- return json.dumps(list_agent_visible_tool_descriptors())
25
+ descriptors = list_agent_visible_tool_descriptors(
26
+ include_assistant_only=agent.node_type == NodeType.ASSISTANT
27
+ )
28
+ return json.dumps(descriptors)