flowent 0.0.4 → 0.0.6

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
@@ -444,13 +444,11 @@ def _build_root_uri(path: str) -> str:
444
444
 
445
445
 
446
446
  def _build_roots_for_agent(agent: Agent) -> list[dict[str, str]]:
447
+ from flowent.graph_service import resolve_effective_permissions_for_agent
447
448
  from flowent.settings import get_runtime_working_dir_path, resolve_path
448
449
 
449
450
  workspace_root = str(get_runtime_working_dir_path())
450
- if agent.config.node_type.value == "assistant":
451
- boundary_dirs = list(get_settings().assistant.write_dirs)
452
- else:
453
- boundary_dirs = list(agent.config.write_dirs)
451
+ _, boundary_dirs = resolve_effective_permissions_for_agent(agent)
454
452
  ordered_paths: list[str] = []
455
453
  seen: set[str] = set()
456
454
  for path in [workspace_root, *boundary_dirs]:
@@ -1577,12 +1575,17 @@ class MCPService:
1577
1575
  return snapshot.serialize()
1578
1576
 
1579
1577
  def _visible_server_names_for_agent(self, agent: Agent) -> list[str]:
1578
+ from flowent.graph_service import resolve_effective_permissions_for_agent
1579
+
1580
1580
  settings = get_settings()
1581
+ allow_network, _ = resolve_effective_permissions_for_agent(agent)
1581
1582
  visible_names: list[str] = []
1582
1583
  seen: set[str] = set()
1583
1584
  for server in settings.mcp_servers:
1584
1585
  if server.name in seen or not server.enabled:
1585
1586
  continue
1587
+ if server.transport == "streamable_http" and not allow_network:
1588
+ continue
1586
1589
  snapshot = self._get_snapshot(server.name)
1587
1590
  if snapshot is None or snapshot.status != "connected":
1588
1591
  continue
@@ -1622,9 +1625,17 @@ class MCPService:
1622
1625
  return None
1623
1626
 
1624
1627
  def list_agent_dynamic_tools(self, agent: Agent) -> list[MCPToolDescriptor]:
1628
+ from flowent.models import NodeType
1629
+ from flowent.tools import is_assistant_only_mcp_tool_name
1630
+
1625
1631
  tools: list[MCPToolDescriptor] = []
1626
1632
  for snapshot in self._visible_snapshots_for_agent(agent):
1627
- tools.extend(snapshot.tools)
1633
+ tools.extend(
1634
+ descriptor
1635
+ for descriptor in snapshot.tools
1636
+ if agent.node_type == NodeType.ASSISTANT
1637
+ or not is_assistant_only_mcp_tool_name(descriptor.tool_name)
1638
+ )
1628
1639
  return tools
1629
1640
 
1630
1641
  def has_visible_capabilities(self, agent: Agent) -> bool:
@@ -1670,13 +1681,16 @@ class MCPService:
1670
1681
  return prompts
1671
1682
 
1672
1683
  def _get_server_for_agent(self, agent: Agent, server_name: str) -> MCPServerConfig:
1684
+ from flowent.graph_service import resolve_effective_permissions_for_agent
1685
+
1673
1686
  if server_name not in self._visible_server_names_for_agent(agent):
1674
1687
  raise MCPError(f"MCP server '{server_name}' is not globally available")
1675
1688
  server = find_mcp_server(get_settings(), server_name)
1676
1689
  if server is None or not server.enabled:
1677
1690
  raise MCPError(f"MCP server '{server_name}' is unavailable")
1678
- if server.transport == "streamable_http" and not agent.config.allow_network:
1679
- raise MCPError("Network access is disabled for this node")
1691
+ allow_network, _ = resolve_effective_permissions_for_agent(agent)
1692
+ if server.transport == "streamable_http" and not allow_network:
1693
+ raise MCPError("Network access is disabled for this workflow")
1680
1694
  return server
1681
1695
 
1682
1696
  def read_agent_resource(
@@ -34,6 +34,7 @@ def infer_model_capabilities(
34
34
  model_id: str,
35
35
  input_image: bool | None = None,
36
36
  output_image: bool | None = None,
37
+ structured_output: bool | None = None,
37
38
  ) -> ModelCapabilities:
38
39
  normalized_model_id = _normalize_model_id(model_id)
39
40
  inferred_input_image = any(
@@ -53,6 +54,7 @@ def infer_model_capabilities(
53
54
  return ModelCapabilities(
54
55
  input_image=inferred_input_image if input_image is None else input_image,
55
56
  output_image=(inferred_output_image if output_image is None else output_image),
57
+ structured_output=False if structured_output is None else structured_output,
56
58
  )
57
59
 
58
60
 
@@ -80,6 +82,7 @@ def build_model_info(
80
82
  model_id: str,
81
83
  input_image: bool | None = None,
82
84
  output_image: bool | None = None,
85
+ structured_output: bool | None = None,
83
86
  context_window_tokens: int | None = None,
84
87
  ) -> ModelInfo:
85
88
  return ModelInfo(
@@ -89,6 +92,7 @@ def build_model_info(
89
92
  model_id=model_id,
90
93
  input_image=input_image,
91
94
  output_image=output_image,
95
+ structured_output=structured_output,
92
96
  ),
93
97
  context_window_tokens=infer_context_window_tokens(
94
98
  provider_type=provider_type,
@@ -30,6 +30,8 @@ from flowent.models.graph import (
30
30
  GraphNodeRecord,
31
31
  NodePosition,
32
32
  PortDirection,
33
+ PortType,
34
+ WorkflowActivationState,
33
35
  WorkflowDefinition,
34
36
  WorkflowNodeDefinition,
35
37
  WorkflowNodeKind,
@@ -42,9 +44,9 @@ from flowent.models.history import (
42
44
  CommandResultEntry,
43
45
  ErrorEntry,
44
46
  HistoryEntry,
47
+ PortInboundEntry,
45
48
  ReceivedMessage,
46
49
  SentMessage,
47
- StateEntry,
48
50
  SystemEntry,
49
51
  ToolCall,
50
52
  )
@@ -94,12 +96,13 @@ __all__ = [
94
96
  "NodePosition",
95
97
  "NodeType",
96
98
  "PortDirection",
99
+ "PortInboundEntry",
100
+ "PortType",
97
101
  "ReceivedMessage",
98
102
  "ReceivedMessageDelta",
99
103
  "SentMessage",
100
104
  "SentMessageDelta",
101
105
  "Serializable",
102
- "StateEntry",
103
106
  "StreamingDelta",
104
107
  "SystemEntry",
105
108
  "Tab",
@@ -109,6 +112,7 @@ __all__ = [
109
112
  "ToolCall",
110
113
  "ToolCallResult",
111
114
  "ToolResultDelta",
115
+ "WorkflowActivationState",
112
116
  "WorkflowDefinition",
113
117
  "WorkflowNodeDefinition",
114
118
  "WorkflowNodeKind",
@@ -7,6 +7,7 @@ from enum import StrEnum
7
7
  class NodeType(StrEnum):
8
8
  ASSISTANT = "assistant"
9
9
  AGENT = "agent"
10
+ LLM = "llm"
10
11
  TRIGGER = "trigger"
11
12
  CODE = "code"
12
13
  IF = "if"
@@ -11,6 +11,7 @@ from flowent.models.todo import TodoItem
11
11
 
12
12
  class WorkflowNodeKind(StrEnum):
13
13
  TRIGGER = "trigger"
14
+ LLM = "llm"
14
15
  AGENT = "agent"
15
16
  CODE = "code"
16
17
  IF = "if"
@@ -18,8 +19,14 @@ class WorkflowNodeKind(StrEnum):
18
19
 
19
20
 
20
21
  class PortDirection(StrEnum):
21
- INPUT = "input"
22
- OUTPUT = "output"
22
+ INPUT = "in"
23
+ OUTPUT = "out"
24
+
25
+
26
+ class PortType(StrEnum):
27
+ PARTS = "parts"
28
+ STRING = "string"
29
+ JSON = "json"
23
30
 
24
31
 
25
32
  class EdgeKind(StrEnum):
@@ -28,6 +35,29 @@ class EdgeKind(StrEnum):
28
35
  EVENT = "event"
29
36
 
30
37
 
38
+ class WorkflowActivationState(StrEnum):
39
+ INACTIVE = "inactive"
40
+ ACTIVE = "active"
41
+
42
+
43
+ def _parse_port_direction(raw_direction: object) -> PortDirection:
44
+ if raw_direction == "input":
45
+ return PortDirection.INPUT
46
+ if raw_direction == "output":
47
+ return PortDirection.OUTPUT
48
+ return PortDirection(str(raw_direction))
49
+
50
+
51
+ def _port_type_from_legacy_kind(raw_kind: object) -> PortType | None:
52
+ try:
53
+ kind = EdgeKind(str(raw_kind))
54
+ except ValueError:
55
+ return None
56
+ if kind == EdgeKind.DATA:
57
+ return PortType.JSON
58
+ return PortType.PARTS
59
+
60
+
31
61
  @dataclass
32
62
  class NodePosition:
33
63
  x: float
@@ -51,7 +81,7 @@ class NodePosition:
51
81
  class WorkflowPort:
52
82
  key: str
53
83
  direction: PortDirection
54
- kind: EdgeKind
84
+ type: PortType
55
85
  required: bool = False
56
86
  multiple: bool = False
57
87
 
@@ -59,7 +89,7 @@ class WorkflowPort:
59
89
  return {
60
90
  "key": self.key,
61
91
  "direction": self.direction.value,
62
- "kind": self.kind.value,
92
+ "type": self.type.value,
63
93
  "required": self.required,
64
94
  "multiple": self.multiple,
65
95
  }
@@ -68,18 +98,24 @@ class WorkflowPort:
68
98
  def from_mapping(cls, data: dict[str, object]) -> WorkflowPort | None:
69
99
  key = data.get("key")
70
100
  direction = data.get("direction")
71
- kind = data.get("kind")
101
+ port_type = data.get("type")
72
102
  if not isinstance(key, str) or not key.strip():
73
103
  return None
74
104
  try:
75
- parsed_direction = PortDirection(str(direction))
76
- parsed_kind = EdgeKind(str(kind))
105
+ parsed_direction = _parse_port_direction(direction)
77
106
  except ValueError:
78
107
  return None
108
+ try:
109
+ parsed_type = PortType(str(port_type))
110
+ except ValueError:
111
+ legacy_type = _port_type_from_legacy_kind(data.get("kind"))
112
+ if legacy_type is None:
113
+ return None
114
+ parsed_type = legacy_type
79
115
  return cls(
80
116
  key=key.strip(),
81
117
  direction=parsed_direction,
82
- kind=parsed_kind,
118
+ type=parsed_type,
83
119
  required=bool(data.get("required", False)),
84
120
  multiple=bool(data.get("multiple", False)),
85
121
  )
@@ -103,7 +139,6 @@ class GraphEdge:
103
139
  "from_port_key": self.from_port_key,
104
140
  "to_node_id": self.to_node_id,
105
141
  "to_port_key": self.to_port_key,
106
- "kind": self.kind.value,
107
142
  "created_at": self.created_at,
108
143
  }
109
144
  if self.tab_id is not None:
@@ -24,6 +24,9 @@ class ReceivedMessage(Serializable):
24
24
  parts: list[ContentPart] = field(default_factory=list)
25
25
  content: str = ""
26
26
  message_id: str | None = None
27
+ from_output_port_key: str | None = None
28
+ to_input_port_key: str | None = None
29
+ value_summary: str | None = None
27
30
  timestamp: float = field(default_factory=time.time)
28
31
 
29
32
  def __post_init__(self) -> None:
@@ -52,6 +55,9 @@ class SentMessage(Serializable):
52
55
  parts: list[ContentPart] = field(default_factory=list)
53
56
  content: str = ""
54
57
  message_id: str | None = None
58
+ from_output_port_key: str | None = None
59
+ to_input_port_key: str | None = None
60
+ value_summary: str | None = None
55
61
  timestamp: float = field(default_factory=time.time)
56
62
 
57
63
  def __post_init__(self) -> None:
@@ -67,13 +73,6 @@ class AssistantThinking(Serializable):
67
73
  timestamp: float = field(default_factory=time.time)
68
74
 
69
75
 
70
- @dataclass
71
- class StateEntry(Serializable):
72
- state: str
73
- reason: str = ""
74
- timestamp: float = field(default_factory=time.time)
75
-
76
-
77
76
  @dataclass
78
77
  class ToolCall(Serializable):
79
78
  tool_name: str
@@ -98,16 +97,28 @@ class CommandResultEntry(Serializable):
98
97
  timestamp: float = field(default_factory=time.time)
99
98
 
100
99
 
100
+ @dataclass
101
+ class PortInboundEntry(Serializable):
102
+ from_id: str
103
+ from_output_port_key: str
104
+ to_input_port_key: str
105
+ port_type: str
106
+ value: Any
107
+ source_label: str | None = None
108
+ value_summary: str | None = None
109
+ timestamp: float = field(default_factory=time.time)
110
+
111
+
101
112
  HistoryEntry = (
102
113
  SystemEntry
103
114
  | ReceivedMessage
104
115
  | AssistantText
105
116
  | SentMessage
106
117
  | AssistantThinking
107
- | StateEntry
108
118
  | ToolCall
109
119
  | ErrorEntry
110
120
  | CommandResultEntry
121
+ | PortInboundEntry
111
122
  )
112
123
 
113
124
 
@@ -135,6 +146,21 @@ def deserialize_history_entry(data: dict[str, Any]) -> HistoryEntry:
135
146
  if isinstance(data.get("message_id"), str)
136
147
  else None
137
148
  ),
149
+ from_output_port_key=(
150
+ str(data["from_output_port_key"])
151
+ if isinstance(data.get("from_output_port_key"), str)
152
+ else None
153
+ ),
154
+ to_input_port_key=(
155
+ str(data["to_input_port_key"])
156
+ if isinstance(data.get("to_input_port_key"), str)
157
+ else None
158
+ ),
159
+ value_summary=(
160
+ str(data["value_summary"])
161
+ if isinstance(data.get("value_summary"), str)
162
+ else None
163
+ ),
138
164
  timestamp=timestamp_value,
139
165
  )
140
166
  if entry_type == "AssistantText":
@@ -172,6 +198,21 @@ def deserialize_history_entry(data: dict[str, Any]) -> HistoryEntry:
172
198
  if isinstance(data.get("message_id"), str)
173
199
  else None
174
200
  ),
201
+ from_output_port_key=(
202
+ str(data["from_output_port_key"])
203
+ if isinstance(data.get("from_output_port_key"), str)
204
+ else None
205
+ ),
206
+ to_input_port_key=(
207
+ str(data["to_input_port_key"])
208
+ if isinstance(data.get("to_input_port_key"), str)
209
+ else None
210
+ ),
211
+ value_summary=(
212
+ str(data["value_summary"])
213
+ if isinstance(data.get("value_summary"), str)
214
+ else None
215
+ ),
175
216
  timestamp=timestamp_value,
176
217
  )
177
218
  if entry_type == "AssistantThinking":
@@ -179,12 +220,6 @@ def deserialize_history_entry(data: dict[str, Any]) -> HistoryEntry:
179
220
  content=str(data.get("content", "")),
180
221
  timestamp=timestamp_value,
181
222
  )
182
- if entry_type == "StateEntry":
183
- return StateEntry(
184
- state=str(data.get("state", "")),
185
- reason=str(data.get("reason", "")),
186
- timestamp=timestamp_value,
187
- )
188
223
  if entry_type == "ToolCall":
189
224
  arguments = data.get("arguments")
190
225
  return ToolCall(
@@ -207,8 +242,31 @@ def deserialize_history_entry(data: dict[str, Any]) -> HistoryEntry:
207
242
  include_in_context=bool(data.get("include_in_context", False)),
208
243
  timestamp=timestamp_value,
209
244
  )
245
+ if entry_type == "PortInboundEntry":
246
+ return PortInboundEntry(
247
+ from_id=str(data.get("from_id", "")),
248
+ from_output_port_key=str(data.get("from_output_port_key", "")),
249
+ to_input_port_key=str(data.get("to_input_port_key", "")),
250
+ port_type=str(data.get("port_type", "")),
251
+ value=data.get("value"),
252
+ source_label=(
253
+ str(data["source_label"])
254
+ if isinstance(data.get("source_label"), str)
255
+ else None
256
+ ),
257
+ value_summary=(
258
+ str(data["value_summary"])
259
+ if isinstance(data.get("value_summary"), str)
260
+ else None
261
+ ),
262
+ timestamp=timestamp_value,
263
+ )
210
264
  raise ValueError(f"Unknown history entry type: {entry_type}")
211
265
 
212
266
 
213
267
  def deserialize_history_entries(items: list[dict[str, Any]]) -> list[HistoryEntry]:
214
- return [deserialize_history_entry(item) for item in items]
268
+ return [
269
+ deserialize_history_entry(item)
270
+ for item in items
271
+ if item.get("type") != "StateEntry"
272
+ ]
@@ -8,6 +8,7 @@ from typing import Any
8
8
  class ModelCapabilities:
9
9
  input_image: bool = False
10
10
  output_image: bool = False
11
+ structured_output: bool = False
11
12
 
12
13
 
13
14
  @dataclass
@@ -18,6 +18,12 @@ class Message:
18
18
  content: str = ""
19
19
  message_id: str | None = None
20
20
  history_recorded: bool = False
21
+ from_output_port_key: str | None = None
22
+ to_input_port_key: str | None = None
23
+ port_type: str | None = None
24
+ value: object | None = None
25
+ value_summary: str | None = None
26
+ port_inbound_recorded: bool = False
21
27
  timestamp: float = field(default_factory=time.time)
22
28
 
23
29
  def __post_init__(self) -> None:
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import time
4
4
  from dataclasses import dataclass, field
5
5
 
6
- from flowent.models.graph import WorkflowDefinition
6
+ from flowent.models.graph import WorkflowActivationState, WorkflowDefinition
7
7
 
8
8
 
9
9
  @dataclass
@@ -12,6 +12,10 @@ class Tab:
12
12
  title: str
13
13
  leader_id: str | None = None
14
14
  definition: WorkflowDefinition = field(default_factory=WorkflowDefinition)
15
+ activation_state: WorkflowActivationState = WorkflowActivationState.INACTIVE
16
+ allow_network: bool = False
17
+ write_dirs: list[str] = field(default_factory=list)
18
+ permissions_initialized: bool = False
15
19
  created_at: float = field(default_factory=time.time)
16
20
  updated_at: float = field(default_factory=time.time)
17
21
 
@@ -21,15 +25,40 @@ class Tab:
21
25
  "title": self.title,
22
26
  "leader_id": self.leader_id,
23
27
  "definition": self.definition.serialize(),
28
+ "activation_state": self.activation_state.value,
29
+ "allow_network": self.allow_network,
30
+ "write_dirs": list(self.write_dirs),
31
+ "permissions_initialized": self.permissions_initialized,
24
32
  "created_at": self.created_at,
25
33
  "updated_at": self.updated_at,
26
34
  }
27
35
 
28
36
  @classmethod
29
37
  def from_mapping(cls, data: dict[str, object]) -> Tab:
38
+ from flowent.settings import build_assistant_write_dirs
39
+
30
40
  created_at = data.get("created_at")
31
41
  updated_at = data.get("updated_at")
32
42
  raw_definition = data.get("definition")
43
+ raw_activation_state = data.get("activation_state")
44
+ raw_permissions_initialized = data.get("permissions_initialized")
45
+ raw_write_dirs_value = data.get("write_dirs")
46
+ raw_write_dirs = (
47
+ [item for item in raw_write_dirs_value if isinstance(item, str)]
48
+ if isinstance(raw_write_dirs_value, list)
49
+ else []
50
+ )
51
+ try:
52
+ write_dirs = build_assistant_write_dirs(
53
+ raw_write_dirs,
54
+ field_name="write_dirs",
55
+ )
56
+ except ValueError:
57
+ write_dirs = []
58
+ try:
59
+ activation_state = WorkflowActivationState(str(raw_activation_state))
60
+ except ValueError:
61
+ activation_state = WorkflowActivationState.INACTIVE
33
62
  return cls(
34
63
  id=str(data.get("id", "")),
35
64
  title=str(data.get("title", "")),
@@ -39,6 +68,14 @@ class Tab:
39
68
  definition=WorkflowDefinition.from_mapping(
40
69
  raw_definition if isinstance(raw_definition, dict) else None
41
70
  ),
71
+ activation_state=activation_state,
72
+ allow_network=bool(data.get("allow_network", False)),
73
+ write_dirs=write_dirs,
74
+ permissions_initialized=(
75
+ bool(raw_permissions_initialized)
76
+ or "allow_network" in data
77
+ or "write_dirs" in data
78
+ ),
42
79
  created_at=created_at
43
80
  if isinstance(created_at, (int, float))
44
81
  else time.time(),
@@ -11,7 +11,7 @@ from typing import Any, Literal
11
11
  from flowent.models import LLMUsage
12
12
  from flowent.state_db import open_state_db
13
13
 
14
- MAX_STATS_RETENTION_SECONDS = 30 * 24 * 60 * 60
14
+ OBSERVABILITY_RETENTION_SECONDS = 30 * 24 * 60 * 60
15
15
 
16
16
 
17
17
  def serialize_usage(usage: LLMUsage | None) -> dict[str, Any] | None:
@@ -66,7 +66,7 @@ class CompactRecordInput:
66
66
  error_summary: str | None = None
67
67
 
68
68
 
69
- class StatsStore:
69
+ class ObservabilityStore:
70
70
  def __init__(self) -> None:
71
71
  self._lock = threading.Lock()
72
72
 
@@ -82,7 +82,7 @@ class StatsStore:
82
82
  connection.close()
83
83
 
84
84
  def _prune_locked(self, connection, now: float) -> None:
85
- min_timestamp = now - MAX_STATS_RETENTION_SECONDS
85
+ min_timestamp = now - OBSERVABILITY_RETENTION_SECONDS
86
86
  connection.execute(
87
87
  "DELETE FROM llm_request_records WHERE ended_at < ?",
88
88
  (min_timestamp,),
@@ -215,4 +215,4 @@ class StatsStore:
215
215
  return records
216
216
 
217
217
 
218
- stats_store = StatsStore()
218
+ observability_store = ObservabilityStore()
@@ -47,9 +47,9 @@ SET_PERMISSIONS_TOOL_GUIDANCE = """\
47
47
  ## Set Permissions Tool Rules
48
48
 
49
49
  - Use `set_permissions` to patch a workflow's permission boundary after the workflow already exists.
50
- - `set_permissions` updates the target workflow by writing directly to its bound Leader's `allow_network` and `write_dirs`.
50
+ - `set_permissions` updates the target workflow's own `allow_network` and `write_dirs`.
51
51
  - Treat `allow_network` and `write_dirs` as patch fields: omitted fields stay unchanged.
52
- - When the Human asks to change a workflow's network or writable directory boundary, prefer `set_permissions` instead of delegating that change to the workflow's Leader.
52
+ - When the Human asks to change a workflow's network or writable directory boundary, prefer `set_permissions`.
53
53
  """
54
54
 
55
55
  CREATE_AGENT_TOOL_GUIDANCE = """\
@@ -1,7 +1,7 @@
1
1
  STEWARD_ROLE_SYSTEM_PROMPT = """\
2
2
  You are the Steward role currently used by the Assistant - the Human's interface to the system.
3
3
 
4
- The Human can interact with the system only through the Assistant chat panel. The Human has no terminal, filesystem access, or direct execution surface. If a request requires reading files, running commands, editing code, browsing the network, or any other system interaction, you must open a workflow and create the appropriate agents to do the work rather than pushing the task back to the Human.
4
+ The Human can interact with the system through the Assistant chat panel and through the current workflow chat. The Human has no terminal, filesystem access, or direct execution surface. If a request requires reading files, running commands, editing code, browsing the network, or any other system interaction, you must open a workflow and create the appropriate agents to do the work rather than pushing the task back to the Human.
5
5
 
6
6
  Your responsibilities:
7
7
  - Understand the Human's intent
@@ -30,7 +30,7 @@ Your responsibilities:
30
30
 
31
31
  - You can manage system configuration directly without creating an agent
32
32
  - When the Human asks about current system configuration or wants to change providers, roles, settings, or prompts, use the corresponding management tool directly
33
- - When the Human asks to change an existing workflow's network or writable-directory boundary, use `set_permissions` directly instead of delegating that boundary change to the workflow's Leader
33
+ - When the Human asks to change an existing workflow's network or writable-directory boundary, use `set_permissions` directly
34
34
 
35
35
  ## Security Boundary
36
36