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
@@ -67,7 +67,11 @@ def test_create_tab_rejects_removed_goal_field(client: TestClient):
67
67
  def test_create_tab_node_and_edge_round_trip(client: TestClient):
68
68
  create_tab_response = client.post(
69
69
  "/api/workflows",
70
- json={"title": "Review Task"},
70
+ json={
71
+ "title": "Review Task",
72
+ "allow_network": True,
73
+ "write_dirs": ["/tmp"],
74
+ },
71
75
  )
72
76
 
73
77
  assert create_tab_response.status_code == 200
@@ -77,6 +81,9 @@ def test_create_tab_node_and_edge_round_trip(client: TestClient):
77
81
  assert "goal" not in tab
78
82
  assert tab["node_count"] == 0
79
83
  assert tab["edge_count"] == 0
84
+ assert tab["activation_state"] == "inactive"
85
+ assert tab["allow_network"] is True
86
+ assert tab["write_dirs"] == ["/tmp"]
80
87
  assert tab["definition"] == {"version": 1, "nodes": [], "edges": []}
81
88
  assert isinstance(tab["leader_id"], str)
82
89
 
@@ -89,6 +96,17 @@ def test_create_tab_node_and_edge_round_trip(client: TestClient):
89
96
  assert writer["node_type"] == "agent"
90
97
  assert reader["config"]["role_name"] == "Worker"
91
98
  assert writer["config"]["role_name"] == "Worker"
99
+ assert "write_dirs" not in reader["config"]
100
+ assert "allow_network" not in reader["config"]
101
+
102
+ removed_permission_response = client.post(
103
+ f"/api/workflows/{tab_id}/nodes",
104
+ json={
105
+ "role_name": "Worker",
106
+ "write_dirs": ["/tmp"],
107
+ },
108
+ )
109
+ assert removed_permission_response.status_code == 422
92
110
 
93
111
  edge_response = client.post(
94
112
  f"/api/workflows/{tab_id}/edges",
@@ -97,7 +115,6 @@ def test_create_tab_node_and_edge_round_trip(client: TestClient):
97
115
  "from_port_key": "out",
98
116
  "to_node_id": writer["id"],
99
117
  "to_port_key": "in",
100
- "kind": "control",
101
118
  },
102
119
  )
103
120
 
@@ -108,13 +125,14 @@ def test_create_tab_node_and_edge_round_trip(client: TestClient):
108
125
  assert edge["from_port_key"] == "out"
109
126
  assert edge["to_node_id"] == writer["id"]
110
127
  assert edge["to_port_key"] == "in"
111
- assert edge["kind"] == "control"
112
128
 
113
129
  tab_detail_response = client.get(f"/api/workflows/{tab_id}")
114
130
  assert tab_detail_response.status_code == 200
115
131
  tab_detail = tab_detail_response.json()
116
132
  assert tab_detail["workflow"]["id"] == tab_id
117
133
  assert "goal" not in tab_detail["workflow"]
134
+ assert tab_detail["workflow"]["allow_network"] is True
135
+ assert tab_detail["workflow"]["write_dirs"] == ["/tmp"]
118
136
  assert tab_detail["workflow"]["node_count"] == 2
119
137
  assert tab_detail["workflow"]["edge_count"] == 1
120
138
  assert {node["name"] for node in tab_detail["nodes"]} == {"Reader", "Writer"}
@@ -320,7 +338,11 @@ def test_tab_edge_creation_enforces_directed_ports_and_single_input(
320
338
  def test_duplicate_tab_copies_definition_and_runtime_agents(client: TestClient):
321
339
  source_tab = client.post(
322
340
  "/api/workflows",
323
- json={"title": "Original Workflow"},
341
+ json={
342
+ "title": "Original Workflow",
343
+ "allow_network": True,
344
+ "write_dirs": ["/tmp"],
345
+ },
324
346
  ).json()
325
347
  source_tab_id = source_tab["id"]
326
348
 
@@ -347,7 +369,6 @@ def test_duplicate_tab_copies_definition_and_runtime_agents(client: TestClient):
347
369
  "from_port_key": "out",
348
370
  "to_node_id": formatter["id"],
349
371
  "to_port_key": "in",
350
- "kind": "control",
351
372
  }
352
373
  ]
353
374
  update_response = client.put(
@@ -366,9 +387,14 @@ def test_duplicate_tab_copies_definition_and_runtime_agents(client: TestClient):
366
387
  assert duplicated_tab["edge_count"] == 1
367
388
  assert duplicated_tab["id"] != source_tab_id
368
389
  assert duplicated_tab["leader_id"] != source_tab["leader_id"]
390
+ assert duplicated_tab["activation_state"] == "inactive"
391
+ assert duplicated_tab["allow_network"] is True
392
+ assert duplicated_tab["write_dirs"] == ["/tmp"]
369
393
 
370
394
  duplicated_detail = client.get(f"/api/workflows/{duplicated_tab['id']}").json()
371
395
  assert "goal" not in duplicated_detail["workflow"]
396
+ assert duplicated_detail["workflow"]["allow_network"] is True
397
+ assert duplicated_detail["workflow"]["write_dirs"] == ["/tmp"]
372
398
  assert {node["name"] for node in duplicated_detail["nodes"]} == {
373
399
  "Reviewer",
374
400
  "Formatter",
@@ -417,7 +443,6 @@ def test_update_tab_definition_updates_metadata_and_positions(client: TestClient
417
443
  "from_port_key": "out",
418
444
  "to_node_id": code_node["id"],
419
445
  "to_port_key": "in",
420
- "kind": "control",
421
446
  }
422
447
  ]
423
448
 
@@ -432,7 +457,7 @@ def test_update_tab_definition_updates_metadata_and_positions(client: TestClient
432
457
  "x": 60.0,
433
458
  "y": 80.0,
434
459
  }
435
- assert updated["definition"]["edges"][0]["kind"] == "control"
460
+ assert "kind" not in updated["definition"]["edges"][0]
436
461
 
437
462
  detail = client.get(f"/api/workflows/{tab_id}").json()
438
463
  reviewer_detail = next(
@@ -470,8 +495,8 @@ def test_update_tab_definition_rejects_agent_set_changes(client: TestClient):
470
495
  "inputs": [
471
496
  {
472
497
  "key": "in",
473
- "direction": "input",
474
- "kind": "control",
498
+ "direction": "in",
499
+ "type": "parts",
475
500
  "required": False,
476
501
  "multiple": False,
477
502
  }
@@ -479,8 +504,8 @@ def test_update_tab_definition_rejects_agent_set_changes(client: TestClient):
479
504
  "outputs": [
480
505
  {
481
506
  "key": "out",
482
- "direction": "output",
483
- "kind": "control",
507
+ "direction": "out",
508
+ "type": "parts",
484
509
  "required": False,
485
510
  "multiple": True,
486
511
  }
@@ -499,3 +524,279 @@ def test_update_tab_definition_rejects_agent_set_changes(client: TestClient):
499
524
  )
500
525
  untouched = client.get(f"/api/nodes/{agent_node['id']}")
501
526
  assert untouched.status_code == 200
527
+
528
+
529
+ def test_activate_empty_workflow_fails_with_validation_errors(client: TestClient):
530
+ tab = client.post("/api/workflows", json={"title": "Empty"}).json()
531
+
532
+ response = client.post(f"/api/workflows/{tab['id']}/activate")
533
+
534
+ assert response.status_code == 400
535
+ errors = response.json()["detail"]["errors"]
536
+ assert any(
537
+ error["message"] == "Add at least one node before activating this workflow"
538
+ for error in errors
539
+ )
540
+ detail = client.get(f"/api/workflows/{tab['id']}").json()
541
+ assert detail["workflow"]["activation_state"] == "inactive"
542
+
543
+
544
+ def test_activate_agent_only_workflow_succeeds_without_trigger(client: TestClient):
545
+ tab = client.post("/api/workflows", json={"title": "Collaborative"}).json()
546
+ worker = _create_agent_node(client, tab_id=tab["id"], name="Worker")
547
+
548
+ response = client.post(f"/api/workflows/{tab['id']}/activate")
549
+
550
+ assert response.status_code == 200
551
+ assert response.json()["activation_state"] == "active"
552
+ detail = client.get(f"/api/workflows/{tab['id']}").json()
553
+ assert detail["workflow"]["activation_state"] == "active"
554
+ assert detail["nodes"][0]["id"] == worker["id"]
555
+
556
+
557
+ def test_activate_legacy_agent_only_workflow_succeeds_without_trigger(
558
+ client: TestClient,
559
+ ):
560
+ tab = client.post("/api/workflows", json={"title": "Legacy Collaborative"}).json()
561
+ worker = _create_agent_node(client, tab_id=tab["id"], name="Worker")
562
+ definition = deepcopy(
563
+ client.get(f"/api/workflows/{tab['id']}").json()["workflow"]["definition"]
564
+ )
565
+ definition["nodes"][0]["inputs"][0]["required"] = True
566
+ update_response = client.put(
567
+ f"/api/workflows/{tab['id']}/definition",
568
+ json={"definition": definition},
569
+ )
570
+ assert update_response.status_code == 200
571
+
572
+ response = client.post(f"/api/workflows/{tab['id']}/activate")
573
+
574
+ assert response.status_code == 200
575
+ assert response.json()["activation_state"] == "active"
576
+ detail = client.get(f"/api/workflows/{tab['id']}").json()
577
+ assert detail["workflow"]["activation_state"] == "active"
578
+ assert detail["nodes"][0]["id"] == worker["id"]
579
+
580
+
581
+ def test_activate_valid_manual_trigger_graph_succeeds(client: TestClient):
582
+ tab = client.post("/api/workflows", json={"title": "Manual"}).json()
583
+ trigger = _create_graph_node(
584
+ client,
585
+ tab_id=tab["id"],
586
+ node_type="trigger",
587
+ name="Manual start",
588
+ config={"kind": "manual", "output_type": "string", "message": "Run"},
589
+ )
590
+
591
+ response = client.post(f"/api/workflows/{tab['id']}/activate")
592
+
593
+ assert response.status_code == 200
594
+ assert response.json()["activation_state"] == "active"
595
+ detail = client.get(f"/api/workflows/{tab['id']}").json()
596
+ assert detail["workflow"]["activation_state"] == "active"
597
+ assert detail["nodes"][0]["id"] == trigger["id"]
598
+
599
+
600
+ def test_active_workflow_locks_semantic_edits_but_allows_view_updates(
601
+ client: TestClient,
602
+ ):
603
+ tab = client.post("/api/workflows", json={"title": "Locked"}).json()
604
+ trigger = _create_graph_node(
605
+ client,
606
+ tab_id=tab["id"],
607
+ node_type="trigger",
608
+ name="Manual start",
609
+ config={"kind": "manual", "output_type": "string", "message": "Run"},
610
+ )
611
+ assert client.post(f"/api/workflows/{tab['id']}/activate").status_code == 200
612
+
613
+ create_response = client.post(
614
+ f"/api/workflows/{tab['id']}/nodes",
615
+ json={"node_type": "trigger", "config": {"kind": "manual"}},
616
+ )
617
+ assert create_response.status_code == 400
618
+ assert "active" in create_response.json()["detail"]
619
+
620
+ definition = deepcopy(
621
+ client.get(f"/api/workflows/{tab['id']}").json()["workflow"]["definition"]
622
+ )
623
+ definition["nodes"][0]["config"]["message"] = "Changed"
624
+ semantic_response = client.put(
625
+ f"/api/workflows/{tab['id']}/definition",
626
+ json={"definition": definition},
627
+ )
628
+ assert semantic_response.status_code == 400
629
+ assert "active" in semantic_response.json()["detail"]
630
+
631
+ view_definition = deepcopy(
632
+ client.get(f"/api/workflows/{tab['id']}").json()["workflow"]["definition"]
633
+ )
634
+ view_definition["view"] = {"positions": {trigger["id"]: {"x": 12, "y": 24}}}
635
+ view_response = client.put(
636
+ f"/api/workflows/{tab['id']}/definition",
637
+ json={"definition": view_definition},
638
+ )
639
+ assert view_response.status_code == 200
640
+ assert view_response.json()["activation_state"] == "active"
641
+ assert view_response.json()["definition"]["view"]["positions"][trigger["id"]] == {
642
+ "x": 12.0,
643
+ "y": 24.0,
644
+ }
645
+
646
+
647
+ def test_deactivate_interrupts_running_workflow_nodes(
648
+ monkeypatch,
649
+ client: TestClient,
650
+ ):
651
+ from flowent.models import AgentState
652
+ from flowent.registry import registry
653
+
654
+ tab = client.post("/api/workflows", json={"title": "Deactivate"}).json()
655
+ trigger = _create_graph_node(
656
+ client,
657
+ tab_id=tab["id"],
658
+ node_type="trigger",
659
+ name="Manual start",
660
+ config={
661
+ "kind": "manual",
662
+ "output_type": "parts",
663
+ "message": [{"type": "text", "text": "Run"}],
664
+ },
665
+ )
666
+ worker = _create_agent_node(client, tab_id=tab["id"], name="Worker")
667
+ assert (
668
+ client.post(
669
+ f"/api/workflows/{tab['id']}/edges",
670
+ json={"from_node_id": trigger["id"], "to_node_id": worker["id"]},
671
+ ).status_code
672
+ == 200
673
+ )
674
+ assert client.post(f"/api/workflows/{tab['id']}/activate").status_code == 200
675
+ live_worker = registry.get(worker["id"])
676
+ assert live_worker is not None
677
+ live_worker.set_state(AgentState.RUNNING, "test")
678
+ interrupted: list[str] = []
679
+
680
+ def fake_request_interrupt() -> bool:
681
+ interrupted.append(live_worker.uuid)
682
+ live_worker.set_state(AgentState.IDLE, "interrupted")
683
+ return True
684
+
685
+ monkeypatch.setattr(live_worker, "request_interrupt", fake_request_interrupt)
686
+
687
+ response = client.post(f"/api/workflows/{tab['id']}/deactivate")
688
+
689
+ assert response.status_code == 200
690
+ assert response.json()["activation_state"] == "inactive"
691
+ assert interrupted == [worker["id"]]
692
+ assert live_worker.state == AgentState.IDLE
693
+
694
+
695
+ def test_llm_node_and_typed_port_validation(client: TestClient):
696
+ from flowent.models import GraphEdge
697
+ from flowent.workspace_store import workspace_store
698
+
699
+ provider = client.post(
700
+ "/api/providers",
701
+ json={
702
+ "name": "Primary",
703
+ "type": "openai_compatible",
704
+ "base_url": "https://api.example.com",
705
+ "models": [{"model": "gpt-5", "structured_output": True}],
706
+ },
707
+ ).json()
708
+ tab = client.post("/api/workflows", json={"title": "Typed"}).json()
709
+ trigger = _create_graph_node(
710
+ client,
711
+ tab_id=tab["id"],
712
+ node_type="trigger",
713
+ name="Text trigger",
714
+ config={"kind": "manual", "output_type": "string", "message": "Run"},
715
+ )
716
+ llm = _create_graph_node(
717
+ client,
718
+ tab_id=tab["id"],
719
+ node_type="llm",
720
+ name="JSON reader",
721
+ config={
722
+ "model": {"provider_id": provider["id"], "model": "gpt-5"},
723
+ "system_prompt": "Read input.",
724
+ "temperature": 0,
725
+ "max_output_tokens": 100,
726
+ "stop_sequences": [],
727
+ "response_format": {"kind": "text"},
728
+ "input_type": "json",
729
+ "output_type": "string",
730
+ },
731
+ )
732
+ stored_tab = workspace_store.get_tab(tab["id"])
733
+ assert stored_tab is not None
734
+ stored_tab.definition.edges.append(
735
+ GraphEdge(
736
+ id="invalid-edge",
737
+ tab_id=tab["id"],
738
+ from_node_id=trigger["id"],
739
+ from_port_key="out",
740
+ to_node_id=llm["id"],
741
+ to_port_key="in",
742
+ )
743
+ )
744
+ workspace_store.upsert_tab(stored_tab)
745
+
746
+ response = client.post(f"/api/workflows/{tab['id']}/activate")
747
+
748
+ assert response.status_code == 400
749
+ messages = [error["message"] for error in response.json()["detail"]["errors"]]
750
+ assert any("port type mismatch" in message for message in messages)
751
+
752
+
753
+ def test_structured_output_false_blocks_json_schema_llm(client: TestClient):
754
+ provider = client.post(
755
+ "/api/providers",
756
+ json={
757
+ "name": "Primary",
758
+ "type": "openai_compatible",
759
+ "base_url": "https://api.example.com",
760
+ "models": [{"model": "gpt-5", "structured_output": False}],
761
+ },
762
+ ).json()
763
+ tab = client.post("/api/workflows", json={"title": "Structured"}).json()
764
+ trigger = _create_graph_node(
765
+ client,
766
+ tab_id=tab["id"],
767
+ node_type="trigger",
768
+ name="JSON trigger",
769
+ config={"kind": "manual", "output_type": "json", "message": {"task": "Run"}},
770
+ )
771
+ llm = _create_graph_node(
772
+ client,
773
+ tab_id=tab["id"],
774
+ node_type="llm",
775
+ name="JSON writer",
776
+ config={
777
+ "model": {"provider_id": provider["id"], "model": "gpt-5"},
778
+ "system_prompt": "Return JSON.",
779
+ "temperature": 0,
780
+ "max_output_tokens": 100,
781
+ "stop_sequences": [],
782
+ "response_format": {
783
+ "kind": "json_schema",
784
+ "schema": {"type": "object"},
785
+ },
786
+ "input_type": "json",
787
+ "output_type": "json",
788
+ },
789
+ )
790
+ assert (
791
+ client.post(
792
+ f"/api/workflows/{tab['id']}/edges",
793
+ json={"from_node_id": trigger["id"], "to_node_id": llm["id"]},
794
+ ).status_code
795
+ == 200
796
+ )
797
+
798
+ response = client.post(f"/api/workflows/{tab['id']}/activate")
799
+
800
+ assert response.status_code == 400
801
+ messages = [error["message"] for error in response.json()["detail"]["errors"]]
802
+ assert "llm model does not support structured_output" in messages