flowent 0.0.0 → 0.0.4

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 (494) hide show
  1. package/README.md +70 -10
  2. package/assets/flowent-banner.png +0 -0
  3. package/backend/.python-version +1 -0
  4. package/backend/pyproject.toml +57 -0
  5. package/backend/src/flowent/__init__.py +3 -0
  6. package/backend/src/flowent/__pycache__/__init__.cpython-313.pyc +0 -0
  7. package/backend/src/flowent/__pycache__/_version.cpython-313.pyc +0 -0
  8. package/backend/src/flowent/__pycache__/access.cpython-313.pyc +0 -0
  9. package/backend/src/flowent/__pycache__/agent.cpython-313.pyc +0 -0
  10. package/backend/src/flowent/__pycache__/assistant_commands.cpython-313.pyc +0 -0
  11. package/backend/src/flowent/__pycache__/cli.cpython-313.pyc +0 -0
  12. package/backend/src/flowent/__pycache__/config.cpython-313.pyc +0 -0
  13. package/backend/src/flowent/__pycache__/events.cpython-313.pyc +0 -0
  14. package/backend/src/flowent/__pycache__/graph_runtime.cpython-313.pyc +0 -0
  15. package/backend/src/flowent/__pycache__/graph_service.cpython-313.pyc +0 -0
  16. package/backend/src/flowent/__pycache__/image_assets.cpython-313.pyc +0 -0
  17. package/backend/src/flowent/__pycache__/logging.cpython-313.pyc +0 -0
  18. package/backend/src/flowent/__pycache__/main.cpython-313.pyc +0 -0
  19. package/backend/src/flowent/__pycache__/mcp_service.cpython-313.pyc +0 -0
  20. package/backend/src/flowent/__pycache__/model_metadata.cpython-313.pyc +0 -0
  21. package/backend/src/flowent/__pycache__/network.cpython-313.pyc +0 -0
  22. package/backend/src/flowent/__pycache__/registry.cpython-313.pyc +0 -0
  23. package/backend/src/flowent/__pycache__/role_management.cpython-313.pyc +0 -0
  24. package/backend/src/flowent/__pycache__/runtime.cpython-313.pyc +0 -0
  25. package/backend/src/flowent/__pycache__/sandbox.cpython-313.pyc +0 -0
  26. package/backend/src/flowent/__pycache__/security.cpython-313.pyc +0 -0
  27. package/backend/src/flowent/__pycache__/settings.cpython-313.pyc +0 -0
  28. package/backend/src/flowent/__pycache__/settings_management.cpython-313.pyc +0 -0
  29. package/backend/src/flowent/__pycache__/state_db.cpython-313.pyc +0 -0
  30. package/backend/src/flowent/__pycache__/stats_service.cpython-313.pyc +0 -0
  31. package/backend/src/flowent/__pycache__/workspace_store.cpython-313.pyc +0 -0
  32. package/backend/src/flowent/_version.py +7 -0
  33. package/backend/src/flowent/access.py +247 -0
  34. package/backend/src/flowent/agent.py +2808 -0
  35. package/backend/src/flowent/assistant_commands.py +106 -0
  36. package/backend/src/flowent/channels/__init__.py +3 -0
  37. package/backend/src/flowent/channels/__pycache__/__init__.cpython-313.pyc +0 -0
  38. package/backend/src/flowent/channels/__pycache__/telegram.cpython-313.pyc +0 -0
  39. package/backend/src/flowent/channels/telegram.py +615 -0
  40. package/backend/src/flowent/cli.py +85 -0
  41. package/backend/src/flowent/config.py +14 -0
  42. package/backend/src/flowent/dev.py +3 -0
  43. package/backend/src/flowent/events.py +157 -0
  44. package/backend/src/flowent/graph_runtime.py +60 -0
  45. package/backend/src/flowent/graph_service.py +1346 -0
  46. package/backend/src/flowent/image_assets.py +356 -0
  47. package/backend/src/flowent/logging.py +155 -0
  48. package/backend/src/flowent/main.py +124 -0
  49. package/backend/src/flowent/mcp_service.py +1904 -0
  50. package/backend/src/flowent/model_metadata.py +98 -0
  51. package/backend/src/flowent/models/__init__.py +121 -0
  52. package/backend/src/flowent/models/__pycache__/__init__.cpython-313.pyc +0 -0
  53. package/backend/src/flowent/models/__pycache__/agent.cpython-313.pyc +0 -0
  54. package/backend/src/flowent/models/__pycache__/base.cpython-313.pyc +0 -0
  55. package/backend/src/flowent/models/__pycache__/blueprint.cpython-313.pyc +0 -0
  56. package/backend/src/flowent/models/__pycache__/content.cpython-313.pyc +0 -0
  57. package/backend/src/flowent/models/__pycache__/delta.cpython-313.pyc +0 -0
  58. package/backend/src/flowent/models/__pycache__/event.cpython-313.pyc +0 -0
  59. package/backend/src/flowent/models/__pycache__/graph.cpython-313.pyc +0 -0
  60. package/backend/src/flowent/models/__pycache__/history.cpython-313.pyc +0 -0
  61. package/backend/src/flowent/models/__pycache__/llm.cpython-313.pyc +0 -0
  62. package/backend/src/flowent/models/__pycache__/message.cpython-313.pyc +0 -0
  63. package/backend/src/flowent/models/__pycache__/tab.cpython-313.pyc +0 -0
  64. package/backend/src/flowent/models/__pycache__/todo.cpython-313.pyc +0 -0
  65. package/backend/src/flowent/models/agent.py +33 -0
  66. package/backend/src/flowent/models/base.py +24 -0
  67. package/backend/src/flowent/models/blueprint.py +176 -0
  68. package/backend/src/flowent/models/content.py +164 -0
  69. package/backend/src/flowent/models/delta.py +44 -0
  70. package/backend/src/flowent/models/event.py +51 -0
  71. package/backend/src/flowent/models/graph.py +437 -0
  72. package/backend/src/flowent/models/history.py +214 -0
  73. package/backend/src/flowent/models/llm.py +61 -0
  74. package/backend/src/flowent/models/message.py +27 -0
  75. package/backend/src/flowent/models/tab.py +48 -0
  76. package/backend/src/flowent/models/todo.py +10 -0
  77. package/backend/src/flowent/network.py +146 -0
  78. package/backend/src/flowent/prompts/__init__.py +67 -0
  79. package/backend/src/flowent/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
  80. package/backend/src/flowent/prompts/__pycache__/common.cpython-313.pyc +0 -0
  81. package/backend/src/flowent/prompts/__pycache__/steward.cpython-313.pyc +0 -0
  82. package/backend/src/flowent/prompts/common.py +250 -0
  83. package/backend/src/flowent/prompts/steward.py +64 -0
  84. package/backend/src/flowent/providers/__init__.py +23 -0
  85. package/backend/src/flowent/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  86. package/backend/src/flowent/providers/__pycache__/anthropic.cpython-313.pyc +0 -0
  87. package/backend/src/flowent/providers/__pycache__/base_url.cpython-313.pyc +0 -0
  88. package/backend/src/flowent/providers/__pycache__/configuration.cpython-313.pyc +0 -0
  89. package/backend/src/flowent/providers/__pycache__/content.cpython-313.pyc +0 -0
  90. package/backend/src/flowent/providers/__pycache__/errors.cpython-313.pyc +0 -0
  91. package/backend/src/flowent/providers/__pycache__/gateway.cpython-313.pyc +0 -0
  92. package/backend/src/flowent/providers/__pycache__/headers.cpython-313.pyc +0 -0
  93. package/backend/src/flowent/providers/__pycache__/management.cpython-313.pyc +0 -0
  94. package/backend/src/flowent/providers/__pycache__/openai.cpython-313.pyc +0 -0
  95. package/backend/src/flowent/providers/__pycache__/openai_responses.cpython-313.pyc +0 -0
  96. package/backend/src/flowent/providers/__pycache__/registry.cpython-313.pyc +0 -0
  97. package/backend/src/flowent/providers/__pycache__/sse.cpython-313.pyc +0 -0
  98. package/backend/src/flowent/providers/__pycache__/thinking.cpython-313.pyc +0 -0
  99. package/backend/src/flowent/providers/anthropic.py +468 -0
  100. package/backend/src/flowent/providers/base_url.py +60 -0
  101. package/backend/src/flowent/providers/configuration.py +182 -0
  102. package/backend/src/flowent/providers/content.py +122 -0
  103. package/backend/src/flowent/providers/errors.py +223 -0
  104. package/backend/src/flowent/providers/gateway.py +169 -0
  105. package/backend/src/flowent/providers/gemini.py +447 -0
  106. package/backend/src/flowent/providers/headers.py +20 -0
  107. package/backend/src/flowent/providers/management.py +96 -0
  108. package/backend/src/flowent/providers/ollama.py +293 -0
  109. package/backend/src/flowent/providers/openai.py +422 -0
  110. package/backend/src/flowent/providers/openai_responses.py +655 -0
  111. package/backend/src/flowent/providers/registry.py +144 -0
  112. package/backend/src/flowent/providers/sse.py +31 -0
  113. package/backend/src/flowent/providers/thinking.py +79 -0
  114. package/backend/src/flowent/registry.py +73 -0
  115. package/backend/src/flowent/role_management.py +255 -0
  116. package/backend/src/flowent/routes/__init__.py +30 -0
  117. package/backend/src/flowent/routes/__pycache__/__init__.cpython-313.pyc +0 -0
  118. package/backend/src/flowent/routes/__pycache__/access.cpython-313.pyc +0 -0
  119. package/backend/src/flowent/routes/__pycache__/assistant.cpython-313.pyc +0 -0
  120. package/backend/src/flowent/routes/__pycache__/image_assets.cpython-313.pyc +0 -0
  121. package/backend/src/flowent/routes/__pycache__/mcp.cpython-313.pyc +0 -0
  122. package/backend/src/flowent/routes/__pycache__/meta.cpython-313.pyc +0 -0
  123. package/backend/src/flowent/routes/__pycache__/nodes.cpython-313.pyc +0 -0
  124. package/backend/src/flowent/routes/__pycache__/prompts.cpython-313.pyc +0 -0
  125. package/backend/src/flowent/routes/__pycache__/providers_route.cpython-313.pyc +0 -0
  126. package/backend/src/flowent/routes/__pycache__/roles.cpython-313.pyc +0 -0
  127. package/backend/src/flowent/routes/__pycache__/settings.cpython-313.pyc +0 -0
  128. package/backend/src/flowent/routes/__pycache__/stats.cpython-313.pyc +0 -0
  129. package/backend/src/flowent/routes/__pycache__/tabs.cpython-313.pyc +0 -0
  130. package/backend/src/flowent/routes/__pycache__/ws.cpython-313.pyc +0 -0
  131. package/backend/src/flowent/routes/access.py +48 -0
  132. package/backend/src/flowent/routes/assistant.py +155 -0
  133. package/backend/src/flowent/routes/image_assets.py +33 -0
  134. package/backend/src/flowent/routes/mcp.py +125 -0
  135. package/backend/src/flowent/routes/meta.py +28 -0
  136. package/backend/src/flowent/routes/nodes.py +365 -0
  137. package/backend/src/flowent/routes/prompts.py +46 -0
  138. package/backend/src/flowent/routes/providers_route.py +364 -0
  139. package/backend/src/flowent/routes/roles.py +207 -0
  140. package/backend/src/flowent/routes/settings.py +324 -0
  141. package/backend/src/flowent/routes/stats.py +229 -0
  142. package/backend/src/flowent/routes/tabs.py +292 -0
  143. package/backend/src/flowent/routes/ws.py +33 -0
  144. package/backend/src/flowent/runtime.py +188 -0
  145. package/backend/src/flowent/sandbox.py +45 -0
  146. package/backend/src/flowent/security.py +42 -0
  147. package/backend/src/flowent/settings.py +2467 -0
  148. package/backend/src/flowent/settings_management.py +286 -0
  149. package/backend/src/flowent/state_db.py +120 -0
  150. package/backend/src/flowent/static/assets/AssistantPage-B3Xc08AS.js +1 -0
  151. package/backend/src/flowent/static/assets/ChannelsPage-ByLd28xk.js +1 -0
  152. package/backend/src/flowent/static/assets/HomePage-C0hAx9_l.js +3 -0
  153. package/backend/src/flowent/static/assets/McpPage-DkrYLvBv.js +7 -0
  154. package/backend/src/flowent/static/assets/PageScaffold-D4jO9ooX.js +1 -0
  155. package/backend/src/flowent/static/assets/PromptsPage-DWA7rRJd.js +1 -0
  156. package/backend/src/flowent/static/assets/ProvidersPage-PUWT8seJ.js +3 -0
  157. package/backend/src/flowent/static/assets/RolesPage-CqcclGRw.js +1 -0
  158. package/backend/src/flowent/static/assets/SettingsPage-8tS2cJgX.js +3 -0
  159. package/backend/src/flowent/static/assets/StatsPage-BX9khYzu.js +1 -0
  160. package/backend/src/flowent/static/assets/ToolsPage-9Tl9FdeD.js +1 -0
  161. package/backend/src/flowent/static/assets/WorkspaceCommandDialog-CCXxjDL8.js +1 -0
  162. package/backend/src/flowent/static/assets/WorkspacePanels-aMdJ7ZH7.js +1 -0
  163. package/backend/src/flowent/static/assets/alert-dialog-kFYVQ7oX.js +1 -0
  164. package/backend/src/flowent/static/assets/badge-74-3jsCg.js +1 -0
  165. package/backend/src/flowent/static/assets/constants-XUzFf6i1.js +1 -0
  166. package/backend/src/flowent/static/assets/datetime-m6_O_Ci9.js +1 -0
  167. package/backend/src/flowent/static/assets/dialog-BeGSweF6.js +1 -0
  168. package/backend/src/flowent/static/assets/elk-worker.min-C9JGDOE-.js +6312 -0
  169. package/backend/src/flowent/static/assets/graph-vendor-CHpVij2M.css +1 -0
  170. package/backend/src/flowent/static/assets/graph-vendor-DRq_-6fV.js +7 -0
  171. package/backend/src/flowent/static/assets/index-BHC1Vhy8.css +1 -0
  172. package/backend/src/flowent/static/assets/index-CL1ALZ3r.js +10 -0
  173. package/backend/src/flowent/static/assets/layout.worker-jMHqAFbP.js +24 -0
  174. package/backend/src/flowent/static/assets/markdown-vendor-DVdy_w12.js +29 -0
  175. package/backend/src/flowent/static/assets/modelParams-CaHd0903.js +1 -0
  176. package/backend/src/flowent/static/assets/react-vendor-mEs_JJxa.js +9 -0
  177. package/backend/src/flowent/static/assets/roles-2OLDeTc5.js +1 -0
  178. package/backend/src/flowent/static/assets/rolldown-runtime-BYbx6iT9.js +1 -0
  179. package/backend/src/flowent/static/assets/select-DL_LPeDj.js +1 -0
  180. package/backend/src/flowent/static/assets/shared-CMxbpLeQ.js +1 -0
  181. package/backend/src/flowent/static/assets/triState-DEr3NkXV.js +1 -0
  182. package/backend/src/flowent/static/assets/ui-vendor-Dg9NNnWX.js +51 -0
  183. package/backend/src/flowent/static/index.html +36 -0
  184. package/backend/src/flowent/stats_service.py +218 -0
  185. package/backend/src/flowent/tools/__init__.py +201 -0
  186. package/backend/src/flowent/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  187. package/backend/src/flowent/tools/__pycache__/connect.cpython-313.pyc +0 -0
  188. package/backend/src/flowent/tools/__pycache__/contacts.cpython-313.pyc +0 -0
  189. package/backend/src/flowent/tools/__pycache__/create_agent.cpython-313.pyc +0 -0
  190. package/backend/src/flowent/tools/__pycache__/create_tab.cpython-313.pyc +0 -0
  191. package/backend/src/flowent/tools/__pycache__/delete_tab.cpython-313.pyc +0 -0
  192. package/backend/src/flowent/tools/__pycache__/edit.cpython-313.pyc +0 -0
  193. package/backend/src/flowent/tools/__pycache__/exec.cpython-313.pyc +0 -0
  194. package/backend/src/flowent/tools/__pycache__/fetch.cpython-313.pyc +0 -0
  195. package/backend/src/flowent/tools/__pycache__/idle.cpython-313.pyc +0 -0
  196. package/backend/src/flowent/tools/__pycache__/list_roles.cpython-313.pyc +0 -0
  197. package/backend/src/flowent/tools/__pycache__/list_tabs.cpython-313.pyc +0 -0
  198. package/backend/src/flowent/tools/__pycache__/list_tools.cpython-313.pyc +0 -0
  199. package/backend/src/flowent/tools/__pycache__/manage_prompts.cpython-313.pyc +0 -0
  200. package/backend/src/flowent/tools/__pycache__/manage_providers.cpython-313.pyc +0 -0
  201. package/backend/src/flowent/tools/__pycache__/manage_roles.cpython-313.pyc +0 -0
  202. package/backend/src/flowent/tools/__pycache__/manage_settings.cpython-313.pyc +0 -0
  203. package/backend/src/flowent/tools/__pycache__/mcp.cpython-313.pyc +0 -0
  204. package/backend/src/flowent/tools/__pycache__/read.cpython-313.pyc +0 -0
  205. package/backend/src/flowent/tools/__pycache__/send.cpython-313.pyc +0 -0
  206. package/backend/src/flowent/tools/__pycache__/set_permissions.cpython-313.pyc +0 -0
  207. package/backend/src/flowent/tools/__pycache__/sleep.cpython-313.pyc +0 -0
  208. package/backend/src/flowent/tools/__pycache__/todo.cpython-313.pyc +0 -0
  209. package/backend/src/flowent/tools/connect.py +156 -0
  210. package/backend/src/flowent/tools/contacts.py +22 -0
  211. package/backend/src/flowent/tools/create_agent.py +270 -0
  212. package/backend/src/flowent/tools/create_tab.py +59 -0
  213. package/backend/src/flowent/tools/delete_tab.py +39 -0
  214. package/backend/src/flowent/tools/edit.py +142 -0
  215. package/backend/src/flowent/tools/exec.py +117 -0
  216. package/backend/src/flowent/tools/fetch.py +85 -0
  217. package/backend/src/flowent/tools/idle.py +27 -0
  218. package/backend/src/flowent/tools/list_roles.py +50 -0
  219. package/backend/src/flowent/tools/list_tabs.py +96 -0
  220. package/backend/src/flowent/tools/list_tools.py +24 -0
  221. package/backend/src/flowent/tools/manage_prompts.py +102 -0
  222. package/backend/src/flowent/tools/manage_providers.py +220 -0
  223. package/backend/src/flowent/tools/manage_roles.py +275 -0
  224. package/backend/src/flowent/tools/manage_settings.py +346 -0
  225. package/backend/src/flowent/tools/mcp.py +199 -0
  226. package/backend/src/flowent/tools/read.py +152 -0
  227. package/backend/src/flowent/tools/send.py +50 -0
  228. package/backend/src/flowent/tools/set_permissions.py +84 -0
  229. package/backend/src/flowent/tools/sleep.py +41 -0
  230. package/backend/src/flowent/tools/todo.py +51 -0
  231. package/backend/src/flowent/workspace_store.py +479 -0
  232. package/backend/tests/__init__.py +0 -0
  233. package/backend/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  234. package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  235. package/backend/tests/conftest.py +6 -0
  236. package/backend/tests/integration/api/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  237. package/backend/tests/integration/api/__pycache__/test_access_api.cpython-313-pytest-9.0.3.pyc +0 -0
  238. package/backend/tests/integration/api/__pycache__/test_assistant_api.cpython-313-pytest-9.0.3.pyc +0 -0
  239. package/backend/tests/integration/api/__pycache__/test_frontend_mounting.cpython-313-pytest-9.0.3.pyc +0 -0
  240. package/backend/tests/integration/api/__pycache__/test_mcp_api.cpython-313-pytest-9.0.3.pyc +0 -0
  241. package/backend/tests/integration/api/__pycache__/test_meta_api.cpython-313-pytest-9.0.3.pyc +0 -0
  242. package/backend/tests/integration/api/__pycache__/test_nodes_api.cpython-313-pytest-9.0.3.pyc +0 -0
  243. package/backend/tests/integration/api/__pycache__/test_prompts_api.cpython-313-pytest-9.0.3.pyc +0 -0
  244. package/backend/tests/integration/api/__pycache__/test_roles_api.cpython-313-pytest-9.0.3.pyc +0 -0
  245. package/backend/tests/integration/api/__pycache__/test_tabs_api.cpython-313-pytest-9.0.3.pyc +0 -0
  246. package/backend/tests/integration/api/conftest.py +29 -0
  247. package/backend/tests/integration/api/test_access_api.py +182 -0
  248. package/backend/tests/integration/api/test_assistant_api.py +354 -0
  249. package/backend/tests/integration/api/test_frontend_mounting.py +61 -0
  250. package/backend/tests/integration/api/test_mcp_api.py +116 -0
  251. package/backend/tests/integration/api/test_meta_api.py +33 -0
  252. package/backend/tests/integration/api/test_nodes_api.py +486 -0
  253. package/backend/tests/integration/api/test_prompts_api.py +47 -0
  254. package/backend/tests/integration/api/test_roles_api.py +227 -0
  255. package/backend/tests/integration/api/test_tabs_api.py +501 -0
  256. package/backend/tests/unit/__pycache__/test_access.cpython-313-pytest-9.0.3.pyc +0 -0
  257. package/backend/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  258. package/backend/tests/unit/__pycache__/test_graph_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  259. package/backend/tests/unit/__pycache__/test_network.cpython-313-pytest-9.0.3.pyc +0 -0
  260. package/backend/tests/unit/__pycache__/test_state_sqlite_storage.cpython-313-pytest-9.0.3.pyc +0 -0
  261. package/backend/tests/unit/__pycache__/test_workspace_store.cpython-313-pytest-9.0.3.pyc +0 -0
  262. package/backend/tests/unit/agent/__pycache__/test_agent_public_api.cpython-313-pytest-9.0.3.pyc +0 -0
  263. package/backend/tests/unit/agent/__pycache__/test_agent_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  264. package/backend/tests/unit/agent/test_agent_public_api.py +746 -0
  265. package/backend/tests/unit/agent/test_agent_runtime.py +2726 -0
  266. package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
  267. package/backend/tests/unit/channels/test_telegram_channel.py +552 -0
  268. package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
  269. package/backend/tests/unit/logging/test_logging.py +132 -0
  270. package/backend/tests/unit/prompts/__pycache__/test_prompts.cpython-313-pytest-9.0.3.pyc +0 -0
  271. package/backend/tests/unit/prompts/test_prompts.py +569 -0
  272. package/backend/tests/unit/providers/__pycache__/test_anthropic_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  273. package/backend/tests/unit/providers/__pycache__/test_errors.cpython-313-pytest-9.0.3.pyc +0 -0
  274. package/backend/tests/unit/providers/__pycache__/test_extract_delta_parts.cpython-313-pytest-9.0.3.pyc +0 -0
  275. package/backend/tests/unit/providers/__pycache__/test_openai_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  276. package/backend/tests/unit/providers/__pycache__/test_openai_responses.cpython-313-pytest-9.0.3.pyc +0 -0
  277. package/backend/tests/unit/providers/__pycache__/test_provider_gateway.cpython-313-pytest-9.0.3.pyc +0 -0
  278. package/backend/tests/unit/providers/__pycache__/test_think_tag_parser.cpython-313-pytest-9.0.3.pyc +0 -0
  279. package/backend/tests/unit/providers/test_anthropic_provider.py +185 -0
  280. package/backend/tests/unit/providers/test_errors.py +68 -0
  281. package/backend/tests/unit/providers/test_extract_delta_parts.py +22 -0
  282. package/backend/tests/unit/providers/test_openai_provider.py +139 -0
  283. package/backend/tests/unit/providers/test_openai_responses.py +402 -0
  284. package/backend/tests/unit/providers/test_provider_gateway.py +359 -0
  285. package/backend/tests/unit/providers/test_think_tag_parser.py +36 -0
  286. package/backend/tests/unit/routes/__pycache__/test_prompts_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  287. package/backend/tests/unit/routes/__pycache__/test_providers_route.cpython-313-pytest-9.0.3.pyc +0 -0
  288. package/backend/tests/unit/routes/__pycache__/test_roles_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  289. package/backend/tests/unit/routes/__pycache__/test_settings_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  290. package/backend/tests/unit/routes/__pycache__/test_stats_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  291. package/backend/tests/unit/routes/test_prompts_routes.py +104 -0
  292. package/backend/tests/unit/routes/test_providers_route.py +368 -0
  293. package/backend/tests/unit/routes/test_roles_routes.py +426 -0
  294. package/backend/tests/unit/routes/test_settings_routes.py +1138 -0
  295. package/backend/tests/unit/routes/test_stats_routes.py +149 -0
  296. package/backend/tests/unit/runtime/__pycache__/test_bootstrap_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  297. package/backend/tests/unit/runtime/test_bootstrap_runtime.py +1012 -0
  298. package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
  299. package/backend/tests/unit/sandbox/test_sandbox_tools.py +78 -0
  300. package/backend/tests/unit/security/__pycache__/test_security.cpython-313-pytest-9.0.3.pyc +0 -0
  301. package/backend/tests/unit/security/test_security.py +110 -0
  302. package/backend/tests/unit/settings/__pycache__/test_settings_roles.cpython-313-pytest-9.0.3.pyc +0 -0
  303. package/backend/tests/unit/settings/test_settings_roles.py +711 -0
  304. package/backend/tests/unit/test_access.py +45 -0
  305. package/backend/tests/unit/test_cli.py +124 -0
  306. package/backend/tests/unit/test_graph_runtime.py +72 -0
  307. package/backend/tests/unit/test_network.py +51 -0
  308. package/backend/tests/unit/test_state_sqlite_storage.py +93 -0
  309. package/backend/tests/unit/test_workspace_store.py +231 -0
  310. package/backend/tests/unit/tools/__pycache__/test_connect_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  311. package/backend/tests/unit/tools/__pycache__/test_create_agent_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  312. package/backend/tests/unit/tools/__pycache__/test_delete_tab_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  313. package/backend/tests/unit/tools/__pycache__/test_edit_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  314. package/backend/tests/unit/tools/__pycache__/test_exec_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  315. package/backend/tests/unit/tools/__pycache__/test_fetch_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  316. package/backend/tests/unit/tools/__pycache__/test_manage_prompts_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  317. package/backend/tests/unit/tools/__pycache__/test_manage_providers_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  318. package/backend/tests/unit/tools/__pycache__/test_manage_roles_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  319. package/backend/tests/unit/tools/__pycache__/test_manage_settings_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  320. package/backend/tests/unit/tools/__pycache__/test_read_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  321. package/backend/tests/unit/tools/__pycache__/test_set_permissions_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  322. package/backend/tests/unit/tools/__pycache__/test_todo_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  323. package/backend/tests/unit/tools/__pycache__/test_tool_registry.cpython-313-pytest-9.0.3.pyc +0 -0
  324. package/backend/tests/unit/tools/test_connect_tool.py +229 -0
  325. package/backend/tests/unit/tools/test_create_agent_tool.py +524 -0
  326. package/backend/tests/unit/tools/test_delete_tab_tool.py +83 -0
  327. package/backend/tests/unit/tools/test_edit_tool.py +115 -0
  328. package/backend/tests/unit/tools/test_exec_tool.py +81 -0
  329. package/backend/tests/unit/tools/test_fetch_tool.py +65 -0
  330. package/backend/tests/unit/tools/test_manage_prompts_tool.py +117 -0
  331. package/backend/tests/unit/tools/test_manage_providers_tool.py +458 -0
  332. package/backend/tests/unit/tools/test_manage_roles_tool.py +411 -0
  333. package/backend/tests/unit/tools/test_manage_settings_tool.py +608 -0
  334. package/backend/tests/unit/tools/test_read_tool.py +33 -0
  335. package/backend/tests/unit/tools/test_set_permissions_tool.py +391 -0
  336. package/backend/tests/unit/tools/test_todo_tool.py +37 -0
  337. package/backend/tests/unit/tools/test_tool_registry.py +91 -0
  338. package/backend/uv.lock +1144 -0
  339. package/bin/flowent.mjs +62 -35
  340. package/dist/frontend/assets/AssistantPage-B3Xc08AS.js +1 -0
  341. package/dist/frontend/assets/ChannelsPage-ByLd28xk.js +1 -0
  342. package/dist/frontend/assets/HomePage-C0hAx9_l.js +3 -0
  343. package/dist/frontend/assets/McpPage-DkrYLvBv.js +7 -0
  344. package/dist/frontend/assets/PageScaffold-D4jO9ooX.js +1 -0
  345. package/dist/frontend/assets/PromptsPage-DWA7rRJd.js +1 -0
  346. package/dist/frontend/assets/ProvidersPage-PUWT8seJ.js +3 -0
  347. package/dist/frontend/assets/RolesPage-CqcclGRw.js +1 -0
  348. package/dist/frontend/assets/SettingsPage-8tS2cJgX.js +3 -0
  349. package/dist/frontend/assets/StatsPage-BX9khYzu.js +1 -0
  350. package/dist/frontend/assets/ToolsPage-9Tl9FdeD.js +1 -0
  351. package/dist/frontend/assets/WorkspaceCommandDialog-CCXxjDL8.js +1 -0
  352. package/dist/frontend/assets/WorkspacePanels-aMdJ7ZH7.js +1 -0
  353. package/dist/frontend/assets/alert-dialog-kFYVQ7oX.js +1 -0
  354. package/dist/frontend/assets/badge-74-3jsCg.js +1 -0
  355. package/dist/frontend/assets/constants-XUzFf6i1.js +1 -0
  356. package/dist/frontend/assets/datetime-m6_O_Ci9.js +1 -0
  357. package/dist/frontend/assets/dialog-BeGSweF6.js +1 -0
  358. package/dist/frontend/assets/elk-worker.min-C9JGDOE-.js +6312 -0
  359. package/dist/frontend/assets/graph-vendor-CHpVij2M.css +1 -0
  360. package/dist/frontend/assets/graph-vendor-DRq_-6fV.js +7 -0
  361. package/dist/frontend/assets/index-BHC1Vhy8.css +1 -0
  362. package/dist/frontend/assets/index-CL1ALZ3r.js +10 -0
  363. package/dist/frontend/assets/layout.worker-jMHqAFbP.js +24 -0
  364. package/dist/frontend/assets/markdown-vendor-DVdy_w12.js +29 -0
  365. package/dist/frontend/assets/modelParams-CaHd0903.js +1 -0
  366. package/dist/frontend/assets/react-vendor-mEs_JJxa.js +9 -0
  367. package/dist/frontend/assets/roles-2OLDeTc5.js +1 -0
  368. package/dist/frontend/assets/rolldown-runtime-BYbx6iT9.js +1 -0
  369. package/dist/frontend/assets/select-DL_LPeDj.js +1 -0
  370. package/dist/frontend/assets/shared-CMxbpLeQ.js +1 -0
  371. package/dist/frontend/assets/triState-DEr3NkXV.js +1 -0
  372. package/dist/frontend/assets/ui-vendor-Dg9NNnWX.js +51 -0
  373. package/dist/frontend/index.html +36 -0
  374. package/package.json +28 -41
  375. package/dist/.next/BUILD_ID +0 -1
  376. package/dist/.next/app-path-routes-manifest.json +0 -6
  377. package/dist/.next/build-manifest.json +0 -20
  378. package/dist/.next/package.json +0 -1
  379. package/dist/.next/prerender-manifest.json +0 -114
  380. package/dist/.next/required-server-files.json +0 -333
  381. package/dist/.next/routes-manifest.json +0 -69
  382. package/dist/.next/server/app/_global-error/page/app-paths-manifest.json +0 -3
  383. package/dist/.next/server/app/_global-error/page/build-manifest.json +0 -16
  384. package/dist/.next/server/app/_global-error/page/next-font-manifest.json +0 -6
  385. package/dist/.next/server/app/_global-error/page/react-loadable-manifest.json +0 -1
  386. package/dist/.next/server/app/_global-error/page/server-reference-manifest.json +0 -4
  387. package/dist/.next/server/app/_global-error/page.js +0 -9
  388. package/dist/.next/server/app/_global-error/page.js.map +0 -5
  389. package/dist/.next/server/app/_global-error/page.js.nft.json +0 -1
  390. package/dist/.next/server/app/_global-error/page_client-reference-manifest.js +0 -3
  391. package/dist/.next/server/app/_global-error.html +0 -1
  392. package/dist/.next/server/app/_global-error.meta +0 -15
  393. package/dist/.next/server/app/_global-error.rsc +0 -14
  394. package/dist/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +0 -5
  395. package/dist/.next/server/app/_global-error.segments/_full.segment.rsc +0 -14
  396. package/dist/.next/server/app/_global-error.segments/_head.segment.rsc +0 -5
  397. package/dist/.next/server/app/_global-error.segments/_index.segment.rsc +0 -5
  398. package/dist/.next/server/app/_global-error.segments/_tree.segment.rsc +0 -1
  399. package/dist/.next/server/app/_not-found/page/app-paths-manifest.json +0 -3
  400. package/dist/.next/server/app/_not-found/page/build-manifest.json +0 -16
  401. package/dist/.next/server/app/_not-found/page/next-font-manifest.json +0 -10
  402. package/dist/.next/server/app/_not-found/page/react-loadable-manifest.json +0 -1
  403. package/dist/.next/server/app/_not-found/page/server-reference-manifest.json +0 -4
  404. package/dist/.next/server/app/_not-found/page.js +0 -13
  405. package/dist/.next/server/app/_not-found/page.js.map +0 -5
  406. package/dist/.next/server/app/_not-found/page.js.nft.json +0 -1
  407. package/dist/.next/server/app/_not-found/page_client-reference-manifest.js +0 -3
  408. package/dist/.next/server/app/_not-found.html +0 -1
  409. package/dist/.next/server/app/_not-found.meta +0 -16
  410. package/dist/.next/server/app/_not-found.rsc +0 -16
  411. package/dist/.next/server/app/_not-found.segments/_full.segment.rsc +0 -16
  412. package/dist/.next/server/app/_not-found.segments/_head.segment.rsc +0 -6
  413. package/dist/.next/server/app/_not-found.segments/_index.segment.rsc +0 -5
  414. package/dist/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
  415. package/dist/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -5
  416. package/dist/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -2
  417. package/dist/.next/server/app/icon.svg/route/app-paths-manifest.json +0 -3
  418. package/dist/.next/server/app/icon.svg/route/build-manifest.json +0 -9
  419. package/dist/.next/server/app/icon.svg/route.js +0 -6
  420. package/dist/.next/server/app/icon.svg/route.js.map +0 -5
  421. package/dist/.next/server/app/icon.svg/route.js.nft.json +0 -1
  422. package/dist/.next/server/app/icon.svg.meta +0 -1
  423. package/dist/.next/server/app/index.html +0 -1
  424. package/dist/.next/server/app/index.meta +0 -14
  425. package/dist/.next/server/app/index.rsc +0 -15
  426. package/dist/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -5
  427. package/dist/.next/server/app/index.segments/_full.segment.rsc +0 -15
  428. package/dist/.next/server/app/index.segments/_head.segment.rsc +0 -6
  429. package/dist/.next/server/app/index.segments/_index.segment.rsc +0 -5
  430. package/dist/.next/server/app/index.segments/_tree.segment.rsc +0 -3
  431. package/dist/.next/server/app/page/app-paths-manifest.json +0 -3
  432. package/dist/.next/server/app/page/build-manifest.json +0 -16
  433. package/dist/.next/server/app/page/next-font-manifest.json +0 -10
  434. package/dist/.next/server/app/page/react-loadable-manifest.json +0 -1
  435. package/dist/.next/server/app/page/server-reference-manifest.json +0 -4
  436. package/dist/.next/server/app/page.js +0 -14
  437. package/dist/.next/server/app/page.js.map +0 -5
  438. package/dist/.next/server/app/page.js.nft.json +0 -1
  439. package/dist/.next/server/app/page_client-reference-manifest.js +0 -3
  440. package/dist/.next/server/app-paths-manifest.json +0 -6
  441. package/dist/.next/server/chunks/[externals]_next_dist_0arv.vj._.js +0 -3
  442. package/dist/.next/server/chunks/[root-of-the-server]__0vcj1q1._.js +0 -13
  443. package/dist/.next/server/chunks/[turbopack]_runtime.js +0 -903
  444. package/dist/.next/server/chunks/_next-internal_server_app_icon_svg_route_actions_0-0ehc~.js +0 -3
  445. package/dist/.next/server/chunks/ssr/05w9_next_dist_0ihu0u9._.js +0 -6
  446. package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_12u3mib._.js +0 -3
  447. package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_builtin_forbidden_04fbe_..js +0 -3
  448. package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_builtin_global-error_0brpl_..js +0 -3
  449. package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_builtin_unauthorized_0~2g66g.js +0 -3
  450. package/dist/.next/server/chunks/ssr/05w9_next_dist_esm_build_templates_app-page_0~cyr1_.js +0 -4
  451. package/dist/.next/server/chunks/ssr/05w9_next_dist_esm_build_templates_app-page_1105emf.js +0 -4
  452. package/dist/.next/server/chunks/ssr/05w9_next_dist_esm_build_templates_app-page_11uhyqv.js +0 -4
  453. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0.t9_75._.js +0 -33
  454. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0c0ud_z._.js +0 -3
  455. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0f9_8d4._.js +0 -3
  456. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0l5ko41._.js +0 -19
  457. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0mn6z7i._.js +0 -3
  458. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0npxxst._.js +0 -33
  459. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0qjhaca._.js +0 -3
  460. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0rwgw3s._.js +0 -3
  461. package/dist/.next/server/chunks/ssr/[turbopack]_runtime.js +0 -903
  462. package/dist/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_0k77kol.js +0 -3
  463. package/dist/.next/server/chunks/ssr/_next-internal_server_app__not-found_page_actions_0eq97pa.js +0 -3
  464. package/dist/.next/server/chunks/ssr/_next-internal_server_app_page_actions_09-gtaw.js +0 -3
  465. package/dist/.next/server/chunks/ssr/node_modules__pnpm_056~6.6._.js +0 -3
  466. package/dist/.next/server/chunks/ssr/node_modules__pnpm_0~j0k.e._.js +0 -33
  467. package/dist/.next/server/functions-config-manifest.json +0 -4
  468. package/dist/.next/server/middleware-build-manifest.js +0 -20
  469. package/dist/.next/server/middleware-manifest.json +0 -6
  470. package/dist/.next/server/next-font-manifest.js +0 -1
  471. package/dist/.next/server/next-font-manifest.json +0 -13
  472. package/dist/.next/server/pages/404.html +0 -1
  473. package/dist/.next/server/pages/500.html +0 -1
  474. package/dist/.next/server/pages-manifest.json +0 -4
  475. package/dist/.next/server/prefetch-hints.json +0 -1
  476. package/dist/.next/server/server-reference-manifest.js +0 -1
  477. package/dist/.next/server/server-reference-manifest.json +0 -5
  478. package/dist/.next/static/7FFlzRe2eS-D0Lw5oEpmC/_buildManifest.js +0 -11
  479. package/dist/.next/static/7FFlzRe2eS-D0Lw5oEpmC/_clientMiddlewareManifest.js +0 -1
  480. package/dist/.next/static/7FFlzRe2eS-D0Lw5oEpmC/_ssgManifest.js +0 -1
  481. package/dist/.next/static/chunks/01qk2~bgf76vu.js +0 -1
  482. package/dist/.next/static/chunks/03~yq9q893hmn.js +0 -1
  483. package/dist/.next/static/chunks/080queev.r2uy.js +0 -31
  484. package/dist/.next/static/chunks/0v3lyuj75aq50.js +0 -1
  485. package/dist/.next/static/chunks/10b~xdx5c-i7s.js +0 -5
  486. package/dist/.next/static/chunks/15~9l5n.~r-.4.css +0 -2
  487. package/dist/.next/static/chunks/turbopack-0m-970~qvs7sc.js +0 -1
  488. package/dist/.next/static/media/7178b3e590c64307-s.11.cyxs5p-0z~.woff2 +0 -0
  489. package/dist/.next/static/media/8a480f0b521d4e75-s.06d3mdzz5bre_.woff2 +0 -0
  490. package/dist/.next/static/media/caa3a2e1cccd8315-s.p.16t1db8_9y2o~.woff2 +0 -0
  491. package/dist/package.json +0 -87
  492. package/dist/server.js +0 -38
  493. /package/{dist/.next/server/app/icon.svg.body → backend/src/flowent/static/favicon.svg} +0 -0
  494. /package/dist/{.next/static/media/icon.0.r~afrtrocz9.svg → frontend/favicon.svg} +0 -0
@@ -0,0 +1,275 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import TYPE_CHECKING, Any, ClassVar
5
+
6
+ if TYPE_CHECKING:
7
+ from flowent.agent import Agent
8
+
9
+ from flowent.role_management import (
10
+ apply_role_update,
11
+ build_role_config,
12
+ ensure_role_name_available,
13
+ find_role_by_name,
14
+ normalize_optional_role_description,
15
+ normalize_optional_role_name,
16
+ remove_role,
17
+ require_role_description,
18
+ require_role_name,
19
+ resolve_role_model,
20
+ resolve_role_model_params,
21
+ resolve_role_tool_config,
22
+ sync_running_system_roles,
23
+ validate_builtin_role_update,
24
+ )
25
+ from flowent.tools import Tool
26
+
27
+
28
+ class ManageRolesTool(Tool):
29
+ name = "manage_roles"
30
+ description = (
31
+ "Manage Role configuration. Supports listing, creating, updating, "
32
+ "and deleting roles. Built-in roles cannot be deleted, renamed, "
33
+ "or modified except for model and model_params configuration."
34
+ )
35
+ parameters: ClassVar[dict[str, Any]] = {
36
+ "type": "object",
37
+ "properties": {
38
+ "action": {
39
+ "type": "string",
40
+ "enum": ["list", "create", "update", "delete"],
41
+ "description": "Role management action",
42
+ },
43
+ "name": {
44
+ "type": "string",
45
+ "description": "Role name for create, update, or delete",
46
+ },
47
+ "new_name": {
48
+ "type": "string",
49
+ "description": "New role name for update",
50
+ },
51
+ "description": {
52
+ "type": "string",
53
+ "description": "Short role description used when choosing a role",
54
+ },
55
+ "system_prompt": {
56
+ "type": "string",
57
+ "description": "Role system prompt",
58
+ },
59
+ "model": {
60
+ "type": ["object", "null"],
61
+ "description": "Optional provider and model override for this role",
62
+ "properties": {
63
+ "provider_id": {"type": "string"},
64
+ "model": {"type": "string"},
65
+ },
66
+ "required": ["provider_id", "model"],
67
+ "additionalProperties": False,
68
+ },
69
+ "model_params": {
70
+ "type": ["object", "null"],
71
+ "description": "Optional canonical model parameter overrides",
72
+ "properties": {
73
+ "reasoning_effort": {
74
+ "type": "string",
75
+ "enum": ["none", "low", "medium", "high", "xhigh"],
76
+ },
77
+ "verbosity": {
78
+ "type": "string",
79
+ "enum": ["low", "medium", "high"],
80
+ },
81
+ "max_output_tokens": {"type": "integer"},
82
+ "temperature": {"type": "number"},
83
+ "top_p": {"type": "number"},
84
+ },
85
+ "additionalProperties": False,
86
+ },
87
+ "included_tools": {
88
+ "type": "array",
89
+ "items": {"type": "string"},
90
+ "description": "Tools always included in the role",
91
+ },
92
+ "excluded_tools": {
93
+ "type": "array",
94
+ "items": {"type": "string"},
95
+ "description": "Tools always excluded from the role",
96
+ },
97
+ },
98
+ "required": ["action"],
99
+ }
100
+
101
+ def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
102
+ from flowent.providers.gateway import gateway
103
+ from flowent.settings import (
104
+ get_settings,
105
+ is_builtin_role_name,
106
+ save_settings,
107
+ serialize_role,
108
+ )
109
+
110
+ action = args.get("action")
111
+ role_name = args.get("name")
112
+ new_name = args.get("new_name")
113
+ description = args.get("description")
114
+ system_prompt = args.get("system_prompt")
115
+ role_model = args.get("model")
116
+ role_model_params = args.get("model_params")
117
+ included_tools = args.get("included_tools")
118
+ excluded_tools = args.get("excluded_tools")
119
+
120
+ if not isinstance(action, str):
121
+ return json.dumps({"error": "action must be a string"})
122
+
123
+ if role_name is not None and not isinstance(role_name, str):
124
+ return json.dumps({"error": "name must be a string"})
125
+ if new_name is not None and not isinstance(new_name, str):
126
+ return json.dumps({"error": "new_name must be a string"})
127
+ if description is not None and not isinstance(description, str):
128
+ return json.dumps({"error": "description must be a string"})
129
+ if system_prompt is not None and not isinstance(system_prompt, str):
130
+ return json.dumps({"error": "system_prompt must be a string"})
131
+ if included_tools is not None and (
132
+ not isinstance(included_tools, list)
133
+ or not all(isinstance(tool_name, str) for tool_name in included_tools)
134
+ ):
135
+ return json.dumps({"error": "included_tools must be an array of strings"})
136
+ if excluded_tools is not None and (
137
+ not isinstance(excluded_tools, list)
138
+ or not all(isinstance(tool_name, str) for tool_name in excluded_tools)
139
+ ):
140
+ return json.dumps({"error": "excluded_tools must be an array of strings"})
141
+
142
+ settings = get_settings()
143
+
144
+ if action == "list":
145
+ return json.dumps([serialize_role(role) for role in settings.roles])
146
+
147
+ if action == "create":
148
+ if not isinstance(system_prompt, str):
149
+ return json.dumps({"error": "system_prompt is required"})
150
+ try:
151
+ name = require_role_name(role_name or "")
152
+ description_text = require_role_description(description or "")
153
+ ensure_role_name_available(settings.roles, name)
154
+ next_included, next_excluded = resolve_role_tool_config(
155
+ None,
156
+ included_tools,
157
+ excluded_tools,
158
+ )
159
+ next_model = resolve_role_model(
160
+ role_model,
161
+ settings=settings,
162
+ current=None,
163
+ provided="model" in args,
164
+ invalid_type_error="model must be an object or null",
165
+ missing_provider_id_error=(
166
+ "model.provider_id must be a non-empty string"
167
+ ),
168
+ missing_model_error="model.model must be a non-empty string",
169
+ )
170
+ next_model_params = resolve_role_model_params(
171
+ role_model_params,
172
+ current=None,
173
+ provided="model_params" in args,
174
+ )
175
+ except ValueError as exc:
176
+ return json.dumps({"error": str(exc)})
177
+ new_role = build_role_config(
178
+ name=name,
179
+ description=description_text,
180
+ system_prompt=system_prompt,
181
+ model=next_model,
182
+ model_params=next_model_params,
183
+ included_tools=next_included,
184
+ excluded_tools=next_excluded,
185
+ )
186
+ settings.roles.append(new_role)
187
+ save_settings(settings)
188
+ gateway.invalidate_cache()
189
+ return json.dumps(serialize_role(new_role))
190
+
191
+ if action == "update":
192
+ if not isinstance(role_name, str) or not role_name:
193
+ return json.dumps({"error": "name is required"})
194
+
195
+ target_role = find_role_by_name(settings.roles, role_name)
196
+ if target_role is None:
197
+ return json.dumps({"error": f"Role '{role_name}' not found"})
198
+
199
+ try:
200
+ next_included, next_excluded = resolve_role_tool_config(
201
+ target_role,
202
+ included_tools,
203
+ excluded_tools,
204
+ )
205
+ next_model = resolve_role_model(
206
+ role_model,
207
+ settings=settings,
208
+ current=target_role.model,
209
+ provided="model" in args,
210
+ invalid_type_error="model must be an object or null",
211
+ missing_provider_id_error=(
212
+ "model.provider_id must be a non-empty string"
213
+ ),
214
+ missing_model_error="model.model must be a non-empty string",
215
+ )
216
+ next_model_params = resolve_role_model_params(
217
+ role_model_params,
218
+ current=target_role.model_params,
219
+ provided="model_params" in args,
220
+ )
221
+ stripped_name = normalize_optional_role_name(new_name)
222
+ stripped_description = normalize_optional_role_description(description)
223
+ if stripped_name is not None:
224
+ ensure_role_name_available(
225
+ settings.roles,
226
+ stripped_name,
227
+ current_name=target_role.name,
228
+ )
229
+ validate_builtin_role_update(
230
+ target_role,
231
+ next_name=stripped_name,
232
+ next_description=stripped_description,
233
+ next_system_prompt=system_prompt,
234
+ next_included_tools=next_included,
235
+ next_excluded_tools=next_excluded,
236
+ )
237
+ except ValueError as exc:
238
+ return json.dumps({"error": str(exc)})
239
+ target_role = apply_role_update(
240
+ settings=settings,
241
+ role=target_role,
242
+ next_name=stripped_name,
243
+ next_description=stripped_description,
244
+ next_system_prompt=system_prompt,
245
+ next_model=next_model,
246
+ update_model="model" in args,
247
+ next_model_params=next_model_params,
248
+ update_model_params="model_params" in args,
249
+ next_included_tools=next_included,
250
+ next_excluded_tools=next_excluded,
251
+ )
252
+ save_settings(settings)
253
+ sync_running_system_roles()
254
+ gateway.invalidate_cache()
255
+ return json.dumps(serialize_role(target_role))
256
+
257
+ if action == "delete":
258
+ if not isinstance(role_name, str) or not role_name:
259
+ return json.dumps({"error": "name is required"})
260
+ if is_builtin_role_name(role_name):
261
+ return json.dumps(
262
+ {"error": f"Cannot delete built-in role '{role_name}'"}
263
+ )
264
+
265
+ target_role = find_role_by_name(settings.roles, role_name)
266
+ if target_role is None:
267
+ return json.dumps({"error": f"Role '{role_name}' not found"})
268
+
269
+ remove_role(settings, target_role)
270
+ save_settings(settings)
271
+ sync_running_system_roles()
272
+ gateway.invalidate_cache()
273
+ return json.dumps({"status": "deleted"})
274
+
275
+ return json.dumps({"error": f"Unsupported action: {action}"})
@@ -0,0 +1,346 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import TYPE_CHECKING, Any, ClassVar
5
+
6
+ if TYPE_CHECKING:
7
+ from flowent.agent import Agent
8
+
9
+ from flowent.settings_management import (
10
+ MISSING,
11
+ apply_resolved_settings_update,
12
+ resolve_settings_update,
13
+ serialize_manage_settings,
14
+ )
15
+ from flowent.tools import Tool
16
+
17
+
18
+ class ManageSettingsTool(Tool):
19
+ name = "manage_settings"
20
+ description = (
21
+ "Read and update system settings, including the Assistant role, Leader "
22
+ "role, active provider and model, default model params, event log "
23
+ "timestamp format, and other runtime defaults."
24
+ )
25
+ parameters: ClassVar[dict[str, Any]] = {
26
+ "type": "object",
27
+ "properties": {
28
+ "action": {
29
+ "type": "string",
30
+ "enum": ["get", "update"],
31
+ "description": "Settings action",
32
+ },
33
+ "active_provider_id": {
34
+ "type": "string",
35
+ "description": "Active provider ID for update",
36
+ },
37
+ "assistant_role_name": {
38
+ "type": "string",
39
+ "description": "Role name used by the Assistant",
40
+ },
41
+ "assistant_allow_network": {
42
+ "type": "boolean",
43
+ "description": "Whether the Assistant may use networked tools or paths",
44
+ },
45
+ "assistant_write_dirs": {
46
+ "type": "array",
47
+ "description": "Writable directory boundaries for the Assistant",
48
+ "items": {"type": "string"},
49
+ },
50
+ "working_dir": {
51
+ "type": "string",
52
+ "description": "System working directory used as the default cwd and relative path base",
53
+ },
54
+ "leader_role_name": {
55
+ "type": "string",
56
+ "description": "Role name used by workflow Leaders",
57
+ },
58
+ "active_model": {
59
+ "type": "string",
60
+ "description": "Active model name for update",
61
+ },
62
+ "context_window_tokens": {
63
+ "type": ["integer", "null"],
64
+ "description": "Explicit context window override for the active system model",
65
+ },
66
+ "input_image": {
67
+ "type": ["boolean", "null"],
68
+ "description": "Explicit input_image override for the active system model",
69
+ },
70
+ "output_image": {
71
+ "type": ["boolean", "null"],
72
+ "description": "Explicit output_image override for the active system model",
73
+ },
74
+ "max_retries": {
75
+ "type": "integer",
76
+ "description": "Maximum retries for transient LLM call failures when retry_policy is limited",
77
+ },
78
+ "retry_initial_delay_seconds": {
79
+ "type": "number",
80
+ "description": "Initial exponential backoff delay in seconds",
81
+ },
82
+ "retry_max_delay_seconds": {
83
+ "type": "number",
84
+ "description": "Maximum exponential backoff delay in seconds",
85
+ },
86
+ "retry_backoff_cap_retries": {
87
+ "type": "integer",
88
+ "description": "Retry count where exponential growth stops doubling",
89
+ },
90
+ "auto_compact_token_limit": {
91
+ "type": ["integer", "null"],
92
+ "description": "Token-usage threshold where the runtime should auto compact before the next formal LLM call",
93
+ },
94
+ "retry_policy": {
95
+ "type": "string",
96
+ "enum": ["no_retry", "limited", "unlimited"],
97
+ "description": "System-wide retry policy for transient LLM call failures",
98
+ },
99
+ "timeout_ms": {
100
+ "type": "integer",
101
+ "description": "Single LLM request timeout in milliseconds",
102
+ },
103
+ "model_params": {
104
+ "type": ["object", "null"],
105
+ "description": "Default canonical model parameter overrides",
106
+ "properties": {
107
+ "reasoning_effort": {
108
+ "type": "string",
109
+ "enum": ["none", "low", "medium", "high", "xhigh"],
110
+ },
111
+ "verbosity": {
112
+ "type": "string",
113
+ "enum": ["low", "medium", "high"],
114
+ },
115
+ "max_output_tokens": {"type": "integer"},
116
+ "temperature": {"type": "number"},
117
+ "top_p": {"type": "number"},
118
+ },
119
+ "additionalProperties": False,
120
+ },
121
+ "timestamp_format": {
122
+ "type": "string",
123
+ "description": "Event log timestamp format for update",
124
+ },
125
+ },
126
+ "required": ["action"],
127
+ }
128
+
129
+ def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
130
+ from flowent.graph_service import sync_assistant_role, sync_tab_leaders
131
+ from flowent.providers.gateway import gateway
132
+ from flowent.settings import (
133
+ build_model_auto_compact_token_limit,
134
+ build_model_context_window_tokens,
135
+ build_model_input_image,
136
+ build_model_max_retries,
137
+ build_model_output_image,
138
+ build_model_retry_backoff_cap_retries,
139
+ build_model_retry_initial_delay_seconds,
140
+ build_model_retry_max_delay_seconds,
141
+ build_model_retry_policy,
142
+ build_model_timeout_ms,
143
+ get_settings,
144
+ save_settings,
145
+ )
146
+
147
+ action = args.get("action")
148
+ assistant_role_name = args.get("assistant_role_name")
149
+ assistant_allow_network = args.get("assistant_allow_network")
150
+ assistant_write_dirs = args.get("assistant_write_dirs")
151
+ working_dir = args.get("working_dir")
152
+ leader_role_name = args.get("leader_role_name")
153
+ active_provider_id = args.get("active_provider_id")
154
+ active_model = args.get("active_model")
155
+ timeout_ms = args.get("timeout_ms")
156
+ retry_policy = args.get("retry_policy")
157
+ max_retries = args.get("max_retries")
158
+ retry_initial_delay_seconds = args.get("retry_initial_delay_seconds")
159
+ retry_max_delay_seconds = args.get("retry_max_delay_seconds")
160
+ retry_backoff_cap_retries = args.get("retry_backoff_cap_retries")
161
+ context_window_tokens = args.get("context_window_tokens")
162
+ input_image = args.get("input_image")
163
+ output_image = args.get("output_image")
164
+ auto_compact_token_limit = args.get("auto_compact_token_limit")
165
+ model_params = args.get("model_params")
166
+ timestamp_format = args.get("timestamp_format")
167
+
168
+ if not isinstance(action, str):
169
+ return json.dumps({"error": "action must be a string"})
170
+
171
+ if assistant_role_name is not None and not isinstance(assistant_role_name, str):
172
+ return json.dumps({"error": "assistant_role_name must be a string"})
173
+ if assistant_allow_network is not None and not isinstance(
174
+ assistant_allow_network, bool
175
+ ):
176
+ return json.dumps({"error": "assistant_allow_network must be a boolean"})
177
+ if assistant_write_dirs is not None and not isinstance(
178
+ assistant_write_dirs, list
179
+ ):
180
+ return json.dumps(
181
+ {"error": "assistant_write_dirs must be an array of strings"}
182
+ )
183
+ if working_dir is not None and not isinstance(working_dir, str):
184
+ return json.dumps({"error": "working_dir must be a string"})
185
+ if leader_role_name is not None and not isinstance(leader_role_name, str):
186
+ return json.dumps({"error": "leader_role_name must be a string"})
187
+ if active_provider_id is not None and not isinstance(active_provider_id, str):
188
+ return json.dumps({"error": "active_provider_id must be a string"})
189
+ if active_model is not None and not isinstance(active_model, str):
190
+ return json.dumps({"error": "active_model must be a string"})
191
+ if retry_policy is not None:
192
+ try:
193
+ build_model_retry_policy(retry_policy, field_name="retry_policy")
194
+ except ValueError as exc:
195
+ return json.dumps({"error": str(exc)})
196
+ if timeout_ms is not None:
197
+ try:
198
+ build_model_timeout_ms(timeout_ms, field_name="timeout_ms")
199
+ except ValueError as exc:
200
+ return json.dumps({"error": str(exc)})
201
+ if max_retries is not None:
202
+ try:
203
+ build_model_max_retries(max_retries, field_name="max_retries")
204
+ except ValueError as exc:
205
+ return json.dumps({"error": str(exc)})
206
+ if retry_initial_delay_seconds is not None:
207
+ try:
208
+ build_model_retry_initial_delay_seconds(
209
+ retry_initial_delay_seconds,
210
+ field_name="retry_initial_delay_seconds",
211
+ )
212
+ except ValueError as exc:
213
+ return json.dumps({"error": str(exc)})
214
+ if retry_max_delay_seconds is not None:
215
+ try:
216
+ build_model_retry_max_delay_seconds(
217
+ retry_max_delay_seconds,
218
+ field_name="retry_max_delay_seconds",
219
+ )
220
+ except ValueError as exc:
221
+ return json.dumps({"error": str(exc)})
222
+ if retry_backoff_cap_retries is not None:
223
+ try:
224
+ build_model_retry_backoff_cap_retries(
225
+ retry_backoff_cap_retries,
226
+ field_name="retry_backoff_cap_retries",
227
+ )
228
+ except ValueError as exc:
229
+ return json.dumps({"error": str(exc)})
230
+ if "input_image" in args:
231
+ try:
232
+ build_model_input_image(input_image, field_name="input_image")
233
+ except ValueError as exc:
234
+ return json.dumps({"error": str(exc)})
235
+ if "output_image" in args:
236
+ try:
237
+ build_model_output_image(output_image, field_name="output_image")
238
+ except ValueError as exc:
239
+ return json.dumps({"error": str(exc)})
240
+ if "context_window_tokens" in args:
241
+ try:
242
+ build_model_context_window_tokens(
243
+ context_window_tokens,
244
+ field_name="context_window_tokens",
245
+ )
246
+ except ValueError as exc:
247
+ return json.dumps({"error": str(exc)})
248
+ if "auto_compact_token_limit" in args:
249
+ try:
250
+ build_model_auto_compact_token_limit(
251
+ auto_compact_token_limit,
252
+ field_name="auto_compact_token_limit",
253
+ )
254
+ except ValueError as exc:
255
+ return json.dumps({"error": str(exc)})
256
+ if model_params is not None and not isinstance(
257
+ model_params, (dict, type(None))
258
+ ):
259
+ return json.dumps({"error": "model_params must be an object or null"})
260
+ if timestamp_format is not None and not isinstance(timestamp_format, str):
261
+ return json.dumps({"error": "timestamp_format must be a string"})
262
+
263
+ settings = get_settings()
264
+
265
+ if action == "get":
266
+ return json.dumps(serialize_manage_settings(settings))
267
+
268
+ if action != "update":
269
+ return json.dumps({"error": f"Unsupported action: {action}"})
270
+
271
+ try:
272
+ resolved = resolve_settings_update(
273
+ settings,
274
+ working_dir=working_dir,
275
+ assistant_role_name=assistant_role_name,
276
+ assistant_allow_network=(
277
+ assistant_allow_network
278
+ if assistant_allow_network is not None
279
+ else MISSING
280
+ ),
281
+ assistant_write_dirs=(
282
+ assistant_write_dirs
283
+ if assistant_write_dirs is not None
284
+ else MISSING
285
+ ),
286
+ leader_role_name=leader_role_name,
287
+ active_provider_id=active_provider_id,
288
+ active_model=active_model,
289
+ context_window_tokens=(
290
+ context_window_tokens
291
+ if "context_window_tokens" in args
292
+ else MISSING
293
+ ),
294
+ input_image=input_image if "input_image" in args else MISSING,
295
+ output_image=output_image if "output_image" in args else MISSING,
296
+ max_retries=max_retries if max_retries is not None else MISSING,
297
+ retry_policy=retry_policy if retry_policy is not None else MISSING,
298
+ timeout_ms=timeout_ms if timeout_ms is not None else MISSING,
299
+ retry_initial_delay_seconds=(
300
+ retry_initial_delay_seconds
301
+ if retry_initial_delay_seconds is not None
302
+ else MISSING
303
+ ),
304
+ retry_max_delay_seconds=(
305
+ retry_max_delay_seconds
306
+ if retry_max_delay_seconds is not None
307
+ else MISSING
308
+ ),
309
+ retry_backoff_cap_retries=(
310
+ retry_backoff_cap_retries
311
+ if retry_backoff_cap_retries is not None
312
+ else MISSING
313
+ ),
314
+ auto_compact_token_limit=(
315
+ auto_compact_token_limit
316
+ if "auto_compact_token_limit" in args
317
+ else MISSING
318
+ ),
319
+ model_params=model_params if "model_params" in args else MISSING,
320
+ timestamp_format=timestamp_format,
321
+ assistant_role_field_name="assistant_role_name",
322
+ assistant_allow_network_field_name="assistant_allow_network",
323
+ assistant_write_dirs_field_name="assistant_write_dirs",
324
+ leader_role_field_name="leader_role_name",
325
+ working_dir_field_name="working_dir",
326
+ retry_policy_field_name="retry_policy",
327
+ timeout_ms_field_name="timeout_ms",
328
+ max_retries_field_name="max_retries",
329
+ retry_initial_delay_seconds_field_name="retry_initial_delay_seconds",
330
+ retry_max_delay_seconds_field_name="retry_max_delay_seconds",
331
+ retry_backoff_cap_retries_field_name="retry_backoff_cap_retries",
332
+ input_image_field_name="input_image",
333
+ output_image_field_name="output_image",
334
+ context_window_tokens_field_name="context_window_tokens",
335
+ auto_compact_token_limit_field_name="auto_compact_token_limit",
336
+ )
337
+ except ValueError as exc:
338
+ return json.dumps({"error": str(exc)})
339
+
340
+ apply_resolved_settings_update(settings, resolved)
341
+
342
+ save_settings(settings)
343
+ sync_assistant_role(reason="assistant settings updated")
344
+ sync_tab_leaders(reason="leader settings updated")
345
+ gateway.invalidate_cache()
346
+ return json.dumps(serialize_manage_settings(settings))