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
@@ -12,6 +12,7 @@ from flowent.settings import (
12
12
  build_model_context_window_tokens,
13
13
  build_model_input_image,
14
14
  build_model_output_image,
15
+ build_model_structured_output,
15
16
  build_provider_headers,
16
17
  build_provider_retry_429_delay_seconds,
17
18
  serialize_provider_model_catalog_entry,
@@ -27,6 +28,7 @@ class ProviderModelCatalogPayload(Protocol):
27
28
  context_window_tokens: int | None
28
29
  input_image: bool | None
29
30
  output_image: bool | None
31
+ structured_output: bool | None
30
32
 
31
33
 
32
34
  def validate_provider_base_url_input(
@@ -72,6 +74,10 @@ def build_provider_model_catalog_entry(
72
74
  payload.output_image,
73
75
  field_name=f"{field_name_prefix}.output_image",
74
76
  ),
77
+ structured_output=build_model_structured_output(
78
+ payload.structured_output,
79
+ field_name=f"{field_name_prefix}.structured_output",
80
+ ),
75
81
  )
76
82
 
77
83
 
@@ -178,5 +184,6 @@ def serialize_discovered_model_catalog_entry(model: ModelInfo) -> dict[str, obje
178
184
  context_window_tokens=model.context_window_tokens,
179
185
  input_image=model.capabilities.input_image,
180
186
  output_image=model.capabilities.output_image,
187
+ structured_output=model.capabilities.structured_output,
181
188
  )
182
189
  )
@@ -67,6 +67,11 @@ def resolve_role_tool_config(
67
67
  included_tools: list[str] | None,
68
68
  excluded_tools: list[str] | None,
69
69
  ) -> tuple[list[str], list[str]]:
70
+ from flowent.tools import (
71
+ is_assistant_only_mcp_tool_name,
72
+ is_assistant_only_tool_name,
73
+ )
74
+
70
75
  next_included = normalize_tool_names(
71
76
  included_tools
72
77
  if included_tools is not None
@@ -74,6 +79,13 @@ def resolve_role_tool_config(
74
79
  if current
75
80
  else []
76
81
  )
82
+ if included_tools is not None:
83
+ next_included = [
84
+ tool_name
85
+ for tool_name in next_included
86
+ if not is_assistant_only_tool_name(tool_name)
87
+ and not is_assistant_only_mcp_tool_name(tool_name)
88
+ ]
77
89
  next_excluded = normalize_tool_names(
78
90
  excluded_tools
79
91
  if excluded_tools is not None
@@ -10,7 +10,6 @@ from flowent.routes.prompts import router as prompts_router
10
10
  from flowent.routes.providers_route import router as providers_router
11
11
  from flowent.routes.roles import router as roles_router
12
12
  from flowent.routes.settings import router as settings_router
13
- from flowent.routes.stats import router as stats_router
14
13
  from flowent.routes.tabs import router as tabs_router
15
14
  from flowent.routes.ws import router as ws_router
16
15
 
@@ -24,7 +23,6 @@ router.include_router(roles_router)
24
23
  router.include_router(providers_router)
25
24
  router.include_router(prompts_router)
26
25
  router.include_router(settings_router)
27
- router.include_router(stats_router)
28
26
  router.include_router(tabs_router)
29
27
  router.include_router(meta_router)
30
28
  router.include_router(ws_router)
@@ -7,8 +7,8 @@ from fastapi import APIRouter, HTTPException
7
7
  from pydantic import BaseModel
8
8
 
9
9
  from flowent.assistant_commands import (
10
- AssistantCommandError,
11
- execute_assistant_command_input,
10
+ ConversationCommandError,
11
+ execute_conversation_command_input,
12
12
  )
13
13
  from flowent.image_assets import require_image_asset
14
14
  from flowent.models import (
@@ -110,11 +110,11 @@ async def send_assistant_message(req: AssistantMessageRequest) -> dict:
110
110
 
111
111
  try:
112
112
  executed_command = (
113
- execute_assistant_command_input(assistant, command_input)
113
+ execute_conversation_command_input(assistant, command_input)
114
114
  if isinstance(command_input, str)
115
115
  else None
116
116
  )
117
- except AssistantCommandError as exc:
117
+ except ConversationCommandError as exc:
118
118
  raise HTTPException(status_code=400, detail=str(exc)) from exc
119
119
  except (RuntimeError, TimeoutError, LLMProviderError) as exc:
120
120
  raise HTTPException(status_code=409, detail=str(exc)) from exc
@@ -5,7 +5,17 @@ from typing import Literal
5
5
  from fastapi import APIRouter, HTTPException
6
6
  from pydantic import BaseModel
7
7
 
8
- from flowent.graph_service import is_tab_leader, list_node_connection_ids
8
+ from flowent.assistant_commands import (
9
+ ConversationCommandError,
10
+ execute_conversation_command_input,
11
+ )
12
+ from flowent.graph_service import (
13
+ is_tab_leader,
14
+ list_node_connection_ids,
15
+ resolve_effective_permissions_for_agent,
16
+ resolve_effective_permissions_for_node_record,
17
+ )
18
+ from flowent.providers.errors import LLMProviderError
9
19
  from flowent.registry import registry
10
20
  from flowent.settings import (
11
21
  find_provider,
@@ -196,11 +206,15 @@ async def get_node(node_id: str) -> dict:
196
206
  record_id = node.uuid
197
207
  record_state = node.state
198
208
  target_config = node.config
209
+ allow_network, write_dirs = resolve_effective_permissions_for_agent(node)
199
210
  else:
200
211
  assert record is not None
201
212
  record_id = record.id
202
213
  record_state = record.state
203
214
  target_config = record.config
215
+ allow_network, write_dirs = resolve_effective_permissions_for_node_record(
216
+ record
217
+ )
204
218
  history = (
205
219
  node.get_history_snapshot()
206
220
  if node is not None
@@ -219,7 +233,7 @@ async def get_node(node_id: str) -> dict:
219
233
  "role_name": target_config.role_name,
220
234
  "is_leader": is_tab_leader(node_id=record_id, tab_id=target_config.tab_id),
221
235
  "state": record_state.value,
222
- "contacts": node.get_contact_ids_snapshot() if node is not None else [],
236
+ "contacts": node.get_contacts_info() if node is not None else [],
223
237
  "connections": (
224
238
  list_node_connection_ids(
225
239
  tab_id=target_config.tab_id,
@@ -232,8 +246,12 @@ async def get_node(node_id: str) -> dict:
232
246
  "todos": [t.serialize() for t in todos],
233
247
  "capabilities": _serialize_model_capabilities(target_config.role_name),
234
248
  "tools": sorted(set(target_config.tools) | set(MINIMUM_TOOLS)),
235
- "write_dirs": list(target_config.write_dirs),
236
- "allow_network": target_config.allow_network,
249
+ "write_dirs": list(write_dirs),
250
+ "allow_network": allow_network,
251
+ "workflow_permissions": {
252
+ "allow_network": allow_network,
253
+ "write_dirs": list(write_dirs),
254
+ },
237
255
  "position": record.position.serialize()
238
256
  if record is not None and record.position is not None
239
257
  else None,
@@ -304,8 +322,14 @@ async def clear_node_chat(node_id: str) -> dict:
304
322
  node = registry.get(node_id)
305
323
  if node is None:
306
324
  raise HTTPException(status_code=404, detail="Node not found")
307
- if node.config.node_type != NodeType.ASSISTANT:
308
- raise HTTPException(status_code=400, detail="Can only clear assistant chat")
325
+ if node.config.node_type != NodeType.ASSISTANT and not is_tab_leader(
326
+ node_id=node.uuid,
327
+ tab_id=node.config.tab_id,
328
+ ):
329
+ raise HTTPException(
330
+ status_code=400,
331
+ detail="Can only clear Assistant or workflow chats",
332
+ )
309
333
 
310
334
  try:
311
335
  node.clear_chat_history()
@@ -321,6 +345,7 @@ async def clear_node_chat(node_id: str) -> dict:
321
345
  async def dispatch_node_message(node_id: str, req: DispatchNodeMessageRequest) -> dict:
322
346
  from flowent.graph_service import dispatch_node_message
323
347
  from flowent.models import NodeType, content_parts_to_text, has_image_parts
348
+ from flowent.models import TextPart as ModelTextPart
324
349
 
325
350
  node = registry.get(node_id)
326
351
  if node is None:
@@ -354,6 +379,29 @@ async def dispatch_node_message(node_id: str, req: DispatchNodeMessageRequest) -
354
379
  detail="Current model does not support `input_image`.",
355
380
  )
356
381
 
382
+ command_input = (
383
+ parts[0].text
384
+ if len(parts) == 1 and isinstance(parts[0], ModelTextPart)
385
+ else None
386
+ )
387
+
388
+ try:
389
+ executed_command = (
390
+ execute_conversation_command_input(node, command_input)
391
+ if isinstance(command_input, str)
392
+ else None
393
+ )
394
+ except ConversationCommandError as exc:
395
+ raise HTTPException(status_code=400, detail=str(exc)) from exc
396
+ except (RuntimeError, TimeoutError, LLMProviderError) as exc:
397
+ raise HTTPException(status_code=409, detail=str(exc)) from exc
398
+
399
+ if executed_command is not None:
400
+ return {
401
+ "status": "command_executed",
402
+ "command_name": executed_command.command_name,
403
+ }
404
+
357
405
  error, message_id = dispatch_node_message(
358
406
  node_id=node_id,
359
407
  content=content_parts_to_text(parts),
@@ -39,6 +39,7 @@ class ProviderModelRequest(BaseModel):
39
39
  context_window_tokens: int | None = None
40
40
  input_image: bool | None = None
41
41
  output_image: bool | None = None
42
+ structured_output: bool | None = None
42
43
 
43
44
 
44
45
  class CreateProviderRequest(BaseModel):
@@ -70,7 +70,7 @@ async def get_roles_bootstrap() -> dict[str, object]:
70
70
  return {
71
71
  "roles": [serialize_role(role) for role in settings.roles],
72
72
  "providers": [serialize_provider(provider) for provider in settings.providers],
73
- "tools": list_agent_visible_tool_descriptors(),
73
+ "tools": list_agent_visible_tool_descriptors(include_assistant_only=False),
74
74
  }
75
75
 
76
76
 
@@ -138,6 +138,7 @@ async def update_settings(req: UpdateSettingsRequest) -> dict[str, object]:
138
138
  context_window_tokens: object = MISSING
139
139
  input_image: object = MISSING
140
140
  output_image: object = MISSING
141
+ structured_output: object = MISSING
141
142
  timeout_ms: object = MISSING
142
143
  retry_policy: object = MISSING
143
144
  max_retries: object = MISSING
@@ -159,6 +160,8 @@ async def update_settings(req: UpdateSettingsRequest) -> dict[str, object]:
159
160
  input_image = req.model.get("input_image")
160
161
  if "output_image" in req.model:
161
162
  output_image = req.model.get("output_image")
163
+ if "structured_output" in req.model:
164
+ structured_output = req.model.get("structured_output")
162
165
  if "timeout_ms" in req.model:
163
166
  timeout_ms = req.model.get("timeout_ms")
164
167
  if "retry_policy" in req.model:
@@ -189,6 +192,7 @@ async def update_settings(req: UpdateSettingsRequest) -> dict[str, object]:
189
192
  context_window_tokens=context_window_tokens,
190
193
  input_image=input_image,
191
194
  output_image=output_image,
195
+ structured_output=structured_output,
192
196
  max_retries=max_retries,
193
197
  retry_policy=retry_policy,
194
198
  timeout_ms=timeout_ms,
@@ -4,10 +4,12 @@ from fastapi import APIRouter, HTTPException
4
4
  from pydantic import BaseModel, ConfigDict
5
5
 
6
6
  from flowent.graph_service import (
7
+ activate_tab,
7
8
  create_agent_node,
8
9
  create_edge,
9
10
  create_graph_node,
10
11
  create_tab,
12
+ deactivate_tab,
11
13
  delete_agent_node,
12
14
  delete_edge,
13
15
  delete_tab,
@@ -18,7 +20,7 @@ from flowent.graph_service import (
18
20
  serialize_tab_summary,
19
21
  update_tab_definition,
20
22
  )
21
- from flowent.models import AgentState, EdgeKind, WorkflowNodeKind
23
+ from flowent.models import AgentState, WorkflowNodeKind
22
24
  from flowent.registry import registry
23
25
  from flowent.workspace_store import workspace_store
24
26
 
@@ -39,8 +41,6 @@ class CreateTabNodeRequest(BaseModel):
39
41
  name: str | None = None
40
42
  config: dict[str, object] = {}
41
43
  tools: list[str] = []
42
- write_dirs: list[str] = []
43
- allow_network: bool = False
44
44
 
45
45
 
46
46
  class CreateTabEdgeRequest(BaseModel):
@@ -49,7 +49,7 @@ class CreateTabEdgeRequest(BaseModel):
49
49
  from_port_key: str = "out"
50
50
  to_node_id: str
51
51
  to_port_key: str = "in"
52
- kind: str = EdgeKind.CONTROL.value
52
+ kind: str | None = None
53
53
 
54
54
 
55
55
  class UpdateTabDefinitionRequest(BaseModel):
@@ -136,6 +136,30 @@ async def duplicate_workflow_route(tab_id: str) -> dict[str, object]:
136
136
  return serialize_tab_summary(duplicated)
137
137
 
138
138
 
139
+ @router.post("/api/workflows/{tab_id}/activate")
140
+ async def activate_workflow_route(tab_id: str) -> dict[str, object]:
141
+ updated, errors, error = activate_tab(tab_id=tab_id, actor_id=tab_id)
142
+ if errors:
143
+ raise HTTPException(status_code=400, detail={"errors": errors})
144
+ if error is not None or updated is None:
145
+ raise HTTPException(
146
+ status_code=404 if error and error.endswith("not found") else 400,
147
+ detail=error or "Failed to activate workflow",
148
+ )
149
+ return serialize_tab_summary(updated)
150
+
151
+
152
+ @router.post("/api/workflows/{tab_id}/deactivate")
153
+ async def deactivate_workflow_route(tab_id: str) -> dict[str, object]:
154
+ updated, error = deactivate_tab(tab_id=tab_id, actor_id=tab_id)
155
+ if error is not None or updated is None:
156
+ raise HTTPException(
157
+ status_code=404 if error and error.endswith("not found") else 400,
158
+ detail=error or "Failed to deactivate workflow",
159
+ )
160
+ return serialize_tab_summary(updated)
161
+
162
+
139
163
  @router.get("/api/workflows/{tab_id}")
140
164
  async def get_workflow(tab_id: str) -> dict[str, object]:
141
165
  tab = workspace_store.get_tab(tab_id)
@@ -201,8 +225,6 @@ async def create_workflow_node(
201
225
  tab_id=tab_id,
202
226
  name=req.name,
203
227
  tools=req.tools,
204
- write_dirs=req.write_dirs,
205
- allow_network=req.allow_network,
206
228
  )
207
229
  if error is not None or record is None:
208
230
  raise HTTPException(
@@ -236,17 +258,13 @@ async def create_workflow_edge(
236
258
  tab = workspace_store.get_tab(tab_id)
237
259
  if tab is None:
238
260
  raise HTTPException(status_code=404, detail="Workflow not found")
239
- try:
240
- edge_kind = EdgeKind(req.kind.strip())
241
- except ValueError as exc:
242
- raise HTTPException(status_code=400, detail="Invalid edge kind") from exc
243
261
  edge, error = create_edge(
244
262
  tab_id=tab_id,
245
263
  from_node_id=req.from_node_id,
246
264
  from_port_key=req.from_port_key,
247
265
  to_node_id=req.to_node_id,
248
266
  to_port_key=req.to_port_key,
249
- kind=edge_kind,
267
+ kind=req.kind or "",
250
268
  )
251
269
  if error is not None or edge is None:
252
270
  raise HTTPException(status_code=400, detail=error or "Failed to create edge")
@@ -48,9 +48,10 @@ def bootstrap_runtime() -> None:
48
48
  from flowent.graph_service import (
49
49
  build_assistant_tools,
50
50
  ensure_tab_leaders,
51
+ resolve_effective_permissions_for_node_record,
51
52
  )
52
53
  from flowent.mcp_service import mcp_service
53
- from flowent.models import AgentState, NodeConfig, NodeType, StateEntry
54
+ from flowent.models import AgentState, NodeConfig, NodeType
54
55
  from flowent.settings import (
55
56
  ensure_builtin_roles,
56
57
  get_settings,
@@ -94,20 +95,6 @@ def bootstrap_runtime() -> None:
94
95
  summary=assistant_record.execution_context_summary,
95
96
  history_cutoff=assistant_record.execution_context_history_cutoff,
96
97
  )
97
- if not any(isinstance(entry, StateEntry) for entry in assistant.history):
98
- assistant.history.insert(
99
- 0,
100
- StateEntry(state=assistant_record.state.value, reason="restored"),
101
- )
102
- if assistant_record.state in {
103
- AgentState.INITIALIZING,
104
- AgentState.IDLE,
105
- AgentState.RUNNING,
106
- AgentState.SLEEPING,
107
- }:
108
- assistant.history.append(
109
- StateEntry(state=AgentState.IDLE.value, reason="restored")
110
- )
111
98
  assistant.todos = list(assistant_record.todos)
112
99
  assistant.prime_runtime_state(
113
100
  AgentState.ERROR
@@ -126,6 +113,9 @@ def bootstrap_runtime() -> None:
126
113
  continue
127
114
  if record.state == AgentState.TERMINATED:
128
115
  continue
116
+ allow_network, write_dirs = resolve_effective_permissions_for_node_record(
117
+ record
118
+ )
129
119
  node = Agent(
130
120
  NodeConfig(
131
121
  node_type=record.config.node_type,
@@ -133,8 +123,8 @@ def bootstrap_runtime() -> None:
133
123
  tab_id=record.config.tab_id,
134
124
  name=record.config.name,
135
125
  tools=list(record.config.tools),
136
- write_dirs=list(record.config.write_dirs),
137
- allow_network=record.config.allow_network,
126
+ write_dirs=write_dirs,
127
+ allow_network=allow_network,
138
128
  ),
139
129
  uuid=record.id,
140
130
  )
@@ -143,19 +133,6 @@ def bootstrap_runtime() -> None:
143
133
  summary=record.execution_context_summary,
144
134
  history_cutoff=record.execution_context_history_cutoff,
145
135
  )
146
- if not any(isinstance(entry, StateEntry) for entry in node.history):
147
- node.history.insert(
148
- 0,
149
- StateEntry(state=record.state.value, reason="restored"),
150
- )
151
- if record.state in {
152
- AgentState.INITIALIZING,
153
- AgentState.RUNNING,
154
- AgentState.SLEEPING,
155
- }:
156
- node.history.append(
157
- StateEntry(state=AgentState.IDLE.value, reason="restored")
158
- )
159
136
  node.todos = list(record.todos)
160
137
  node.prime_runtime_state(
161
138
  AgentState.ERROR if record.state == AgentState.ERROR else AgentState.IDLE
@@ -9,6 +9,18 @@ if TYPE_CHECKING:
9
9
 
10
10
 
11
11
  def authorize(tool_name: str, agent: Agent, args: dict[str, Any]) -> str | None:
12
+ from flowent.graph_service import resolve_effective_permissions_for_agent
13
+ from flowent.models import NodeType
14
+ from flowent.tools import (
15
+ is_assistant_only_mcp_tool_name,
16
+ is_assistant_only_tool_name,
17
+ )
18
+
19
+ if agent.node_type != NodeType.ASSISTANT and is_assistant_only_tool_name(tool_name):
20
+ return "Ask the Assistant to manage workflows or settings"
21
+
22
+ allow_network, write_dirs = resolve_effective_permissions_for_agent(agent)
23
+
12
24
  if tool_name.startswith("mcp__"):
13
25
  from flowent.mcp_service import mcp_service
14
26
  from flowent.settings import find_mcp_server, get_settings
@@ -16,27 +28,30 @@ def authorize(tool_name: str, agent: Agent, args: dict[str, Any]) -> str | None:
16
28
  descriptor = mcp_service.get_dynamic_tool_descriptor(tool_name)
17
29
  if descriptor is None:
18
30
  return f"MCP tool not found: {tool_name}"
31
+ if agent.node_type != NodeType.ASSISTANT and is_assistant_only_mcp_tool_name(
32
+ descriptor.tool_name
33
+ ):
34
+ return "Ask the Assistant to manage workflows or settings"
19
35
  server = find_mcp_server(get_settings(), descriptor.server_name)
20
36
  if (
21
37
  server is not None
22
38
  and server.transport == "streamable_http"
23
- and not agent.config.allow_network
39
+ and not allow_network
24
40
  ):
25
- return "Network access is disabled for this agent"
26
- if descriptor.open_world_hint and not agent.config.allow_network:
27
- return "Network access is disabled for this agent"
41
+ return "Network access is disabled for this workflow"
42
+ if descriptor.open_world_hint and not allow_network:
43
+ return "Network access is disabled for this workflow"
28
44
  return None
29
45
 
30
46
  if tool_name == "edit":
31
- write_dirs = agent.config.write_dirs
32
47
  if not write_dirs:
33
- return "Write access is disabled for this agent"
48
+ return "Write access is disabled for this workflow"
34
49
  path = args.get("path")
35
50
  if isinstance(path, str) and not is_path_writable(path, write_dirs):
36
51
  return f"Path not in write_dirs: {path}"
37
52
  return None
38
53
 
39
- if tool_name == "fetch" and not agent.config.allow_network:
40
- return "Network access is disabled for this agent"
54
+ if tool_name == "fetch" and not allow_network:
55
+ return "Network access is disabled for this workflow"
41
56
 
42
57
  return None