flowent 0.0.7 → 0.0.11

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 (387) hide show
  1. package/README.md +0 -3
  2. package/backend/README.md +0 -3
  3. package/backend/pyproject.toml +2 -8
  4. package/backend/src/flowent/__init__.py +6 -2
  5. package/backend/src/flowent/__pycache__/__init__.cpython-313.pyc +0 -0
  6. package/backend/src/flowent/__pycache__/agent.cpython-313.pyc +0 -0
  7. package/backend/src/flowent/__pycache__/context.cpython-313.pyc +0 -0
  8. package/backend/src/flowent/__pycache__/llm.cpython-313.pyc +0 -0
  9. package/backend/src/flowent/__pycache__/logging.cpython-313.pyc +0 -0
  10. package/backend/src/flowent/__pycache__/main.cpython-313.pyc +0 -0
  11. package/backend/src/flowent/__pycache__/patch.cpython-313.pyc +0 -0
  12. package/backend/src/flowent/__pycache__/paths.cpython-313.pyc +0 -0
  13. package/backend/src/flowent/__pycache__/sandbox.cpython-313.pyc +0 -0
  14. package/backend/src/flowent/__pycache__/storage.cpython-313.pyc +0 -0
  15. package/backend/src/flowent/__pycache__/tools.cpython-313.pyc +0 -0
  16. package/backend/src/flowent/agent.py +213 -3173
  17. package/backend/src/flowent/cli.py +19 -24
  18. package/backend/src/flowent/context.py +127 -0
  19. package/backend/src/flowent/llm.py +256 -0
  20. package/backend/src/flowent/logging.py +170 -129
  21. package/backend/src/flowent/main.py +321 -70
  22. package/backend/src/flowent/patch.py +182 -0
  23. package/backend/src/flowent/paths.py +11 -0
  24. package/backend/src/flowent/sandbox.py +214 -40
  25. package/backend/src/flowent/static/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
  26. package/backend/src/flowent/static/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
  27. package/backend/src/flowent/static/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
  28. package/backend/src/flowent/static/assets/index-C76K95ty.js +81 -0
  29. package/backend/src/flowent/static/assets/index-iUMNKvlU.css +2 -0
  30. package/backend/src/flowent/static/flowent.png +0 -0
  31. package/backend/src/flowent/static/index.html +5 -25
  32. package/backend/src/flowent/storage.py +302 -0
  33. package/backend/src/flowent/tools.py +376 -0
  34. package/backend/tests/__pycache__/test_agent_tools.cpython-313-pytest-9.0.3.pyc +0 -0
  35. package/backend/tests/__pycache__/test_health.cpython-313-pytest-9.0.3.pyc +0 -0
  36. package/backend/tests/__pycache__/test_llm_providers.cpython-313-pytest-9.0.3.pyc +0 -0
  37. package/backend/tests/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
  38. package/backend/tests/__pycache__/test_persistence.cpython-313-pytest-9.0.3.pyc +0 -0
  39. package/backend/tests/__pycache__/test_workspace_chat.cpython-313-pytest-9.0.3.pyc +0 -0
  40. package/backend/tests/test_agent_tools.py +477 -0
  41. package/backend/tests/test_health.py +12 -0
  42. package/backend/tests/test_llm_providers.py +113 -0
  43. package/backend/tests/test_logging.py +182 -0
  44. package/backend/tests/test_persistence.py +125 -0
  45. package/backend/tests/test_workspace_chat.py +578 -0
  46. package/backend/uv.lock +803 -99
  47. package/dist/frontend/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
  48. package/dist/frontend/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
  49. package/dist/frontend/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
  50. package/dist/frontend/assets/index-C76K95ty.js +81 -0
  51. package/dist/frontend/assets/index-iUMNKvlU.css +2 -0
  52. package/dist/frontend/flowent.png +0 -0
  53. package/dist/frontend/index.html +5 -25
  54. package/package.json +1 -2
  55. package/backend/src/flowent/__pycache__/_version.cpython-313.pyc +0 -0
  56. package/backend/src/flowent/__pycache__/access.cpython-313.pyc +0 -0
  57. package/backend/src/flowent/__pycache__/assistant_commands.cpython-313.pyc +0 -0
  58. package/backend/src/flowent/__pycache__/cli.cpython-313.pyc +0 -0
  59. package/backend/src/flowent/__pycache__/config.cpython-313.pyc +0 -0
  60. package/backend/src/flowent/__pycache__/events.cpython-313.pyc +0 -0
  61. package/backend/src/flowent/__pycache__/graph_runtime.cpython-313.pyc +0 -0
  62. package/backend/src/flowent/__pycache__/graph_service.cpython-313.pyc +0 -0
  63. package/backend/src/flowent/__pycache__/image_assets.cpython-313.pyc +0 -0
  64. package/backend/src/flowent/__pycache__/model_metadata.cpython-313.pyc +0 -0
  65. package/backend/src/flowent/__pycache__/network.cpython-313.pyc +0 -0
  66. package/backend/src/flowent/__pycache__/observability_service.cpython-313.pyc +0 -0
  67. package/backend/src/flowent/__pycache__/registry.cpython-313.pyc +0 -0
  68. package/backend/src/flowent/__pycache__/role_management.cpython-313.pyc +0 -0
  69. package/backend/src/flowent/__pycache__/runtime.cpython-313.pyc +0 -0
  70. package/backend/src/flowent/__pycache__/security.cpython-313.pyc +0 -0
  71. package/backend/src/flowent/__pycache__/settings.cpython-313.pyc +0 -0
  72. package/backend/src/flowent/__pycache__/settings_management.cpython-313.pyc +0 -0
  73. package/backend/src/flowent/__pycache__/state_db.cpython-313.pyc +0 -0
  74. package/backend/src/flowent/__pycache__/workspace_store.cpython-313.pyc +0 -0
  75. package/backend/src/flowent/access.py +0 -247
  76. package/backend/src/flowent/assistant_commands.py +0 -115
  77. package/backend/src/flowent/channels/__init__.py +0 -3
  78. package/backend/src/flowent/channels/__pycache__/__init__.cpython-313.pyc +0 -0
  79. package/backend/src/flowent/channels/__pycache__/telegram.cpython-313.pyc +0 -0
  80. package/backend/src/flowent/channels/telegram.py +0 -615
  81. package/backend/src/flowent/config.py +0 -14
  82. package/backend/src/flowent/dev.py +0 -3
  83. package/backend/src/flowent/events.py +0 -157
  84. package/backend/src/flowent/graph_runtime.py +0 -60
  85. package/backend/src/flowent/graph_service.py +0 -2401
  86. package/backend/src/flowent/image_assets.py +0 -356
  87. package/backend/src/flowent/model_metadata.py +0 -102
  88. package/backend/src/flowent/models/__init__.py +0 -125
  89. package/backend/src/flowent/models/__pycache__/__init__.cpython-313.pyc +0 -0
  90. package/backend/src/flowent/models/__pycache__/agent.cpython-313.pyc +0 -0
  91. package/backend/src/flowent/models/__pycache__/base.cpython-313.pyc +0 -0
  92. package/backend/src/flowent/models/__pycache__/blueprint.cpython-313.pyc +0 -0
  93. package/backend/src/flowent/models/__pycache__/content.cpython-313.pyc +0 -0
  94. package/backend/src/flowent/models/__pycache__/delta.cpython-313.pyc +0 -0
  95. package/backend/src/flowent/models/__pycache__/event.cpython-313.pyc +0 -0
  96. package/backend/src/flowent/models/__pycache__/graph.cpython-313.pyc +0 -0
  97. package/backend/src/flowent/models/__pycache__/history.cpython-313.pyc +0 -0
  98. package/backend/src/flowent/models/__pycache__/llm.cpython-313.pyc +0 -0
  99. package/backend/src/flowent/models/__pycache__/message.cpython-313.pyc +0 -0
  100. package/backend/src/flowent/models/__pycache__/tab.cpython-313.pyc +0 -0
  101. package/backend/src/flowent/models/__pycache__/todo.cpython-313.pyc +0 -0
  102. package/backend/src/flowent/models/agent.py +0 -34
  103. package/backend/src/flowent/models/base.py +0 -24
  104. package/backend/src/flowent/models/blueprint.py +0 -176
  105. package/backend/src/flowent/models/content.py +0 -164
  106. package/backend/src/flowent/models/delta.py +0 -44
  107. package/backend/src/flowent/models/event.py +0 -51
  108. package/backend/src/flowent/models/graph.py +0 -472
  109. package/backend/src/flowent/models/history.py +0 -272
  110. package/backend/src/flowent/models/llm.py +0 -62
  111. package/backend/src/flowent/models/message.py +0 -33
  112. package/backend/src/flowent/models/tab.py +0 -85
  113. package/backend/src/flowent/models/todo.py +0 -10
  114. package/backend/src/flowent/network.py +0 -146
  115. package/backend/src/flowent/observability_service.py +0 -218
  116. package/backend/src/flowent/prompts/__init__.py +0 -67
  117. package/backend/src/flowent/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
  118. package/backend/src/flowent/prompts/__pycache__/common.cpython-313.pyc +0 -0
  119. package/backend/src/flowent/prompts/__pycache__/steward.cpython-313.pyc +0 -0
  120. package/backend/src/flowent/prompts/common.py +0 -250
  121. package/backend/src/flowent/prompts/steward.py +0 -64
  122. package/backend/src/flowent/providers/__init__.py +0 -23
  123. package/backend/src/flowent/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  124. package/backend/src/flowent/providers/__pycache__/anthropic.cpython-313.pyc +0 -0
  125. package/backend/src/flowent/providers/__pycache__/base_url.cpython-313.pyc +0 -0
  126. package/backend/src/flowent/providers/__pycache__/configuration.cpython-313.pyc +0 -0
  127. package/backend/src/flowent/providers/__pycache__/content.cpython-313.pyc +0 -0
  128. package/backend/src/flowent/providers/__pycache__/errors.cpython-313.pyc +0 -0
  129. package/backend/src/flowent/providers/__pycache__/gateway.cpython-313.pyc +0 -0
  130. package/backend/src/flowent/providers/__pycache__/headers.cpython-313.pyc +0 -0
  131. package/backend/src/flowent/providers/__pycache__/management.cpython-313.pyc +0 -0
  132. package/backend/src/flowent/providers/__pycache__/openai.cpython-313.pyc +0 -0
  133. package/backend/src/flowent/providers/__pycache__/openai_responses.cpython-313.pyc +0 -0
  134. package/backend/src/flowent/providers/__pycache__/registry.cpython-313.pyc +0 -0
  135. package/backend/src/flowent/providers/__pycache__/sse.cpython-313.pyc +0 -0
  136. package/backend/src/flowent/providers/__pycache__/thinking.cpython-313.pyc +0 -0
  137. package/backend/src/flowent/providers/anthropic.py +0 -468
  138. package/backend/src/flowent/providers/base_url.py +0 -60
  139. package/backend/src/flowent/providers/configuration.py +0 -189
  140. package/backend/src/flowent/providers/content.py +0 -122
  141. package/backend/src/flowent/providers/errors.py +0 -223
  142. package/backend/src/flowent/providers/gateway.py +0 -169
  143. package/backend/src/flowent/providers/gemini.py +0 -447
  144. package/backend/src/flowent/providers/headers.py +0 -20
  145. package/backend/src/flowent/providers/management.py +0 -96
  146. package/backend/src/flowent/providers/ollama.py +0 -293
  147. package/backend/src/flowent/providers/openai.py +0 -422
  148. package/backend/src/flowent/providers/openai_responses.py +0 -655
  149. package/backend/src/flowent/providers/registry.py +0 -144
  150. package/backend/src/flowent/providers/sse.py +0 -31
  151. package/backend/src/flowent/providers/thinking.py +0 -79
  152. package/backend/src/flowent/registry.py +0 -73
  153. package/backend/src/flowent/role_management.py +0 -270
  154. package/backend/src/flowent/routes/__init__.py +0 -26
  155. package/backend/src/flowent/routes/__pycache__/__init__.cpython-313.pyc +0 -0
  156. package/backend/src/flowent/routes/__pycache__/access.cpython-313.pyc +0 -0
  157. package/backend/src/flowent/routes/__pycache__/assistant.cpython-313.pyc +0 -0
  158. package/backend/src/flowent/routes/__pycache__/image_assets.cpython-313.pyc +0 -0
  159. package/backend/src/flowent/routes/__pycache__/meta.cpython-313.pyc +0 -0
  160. package/backend/src/flowent/routes/__pycache__/nodes.cpython-313.pyc +0 -0
  161. package/backend/src/flowent/routes/__pycache__/prompts.cpython-313.pyc +0 -0
  162. package/backend/src/flowent/routes/__pycache__/providers_route.cpython-313.pyc +0 -0
  163. package/backend/src/flowent/routes/__pycache__/roles.cpython-313.pyc +0 -0
  164. package/backend/src/flowent/routes/__pycache__/settings.cpython-313.pyc +0 -0
  165. package/backend/src/flowent/routes/__pycache__/tabs.cpython-313.pyc +0 -0
  166. package/backend/src/flowent/routes/__pycache__/ws.cpython-313.pyc +0 -0
  167. package/backend/src/flowent/routes/access.py +0 -48
  168. package/backend/src/flowent/routes/assistant.py +0 -158
  169. package/backend/src/flowent/routes/image_assets.py +0 -33
  170. package/backend/src/flowent/routes/meta.py +0 -28
  171. package/backend/src/flowent/routes/nodes.py +0 -423
  172. package/backend/src/flowent/routes/prompts.py +0 -46
  173. package/backend/src/flowent/routes/providers_route.py +0 -365
  174. package/backend/src/flowent/routes/roles.py +0 -207
  175. package/backend/src/flowent/routes/settings.py +0 -379
  176. package/backend/src/flowent/routes/tabs.py +0 -298
  177. package/backend/src/flowent/routes/ws.py +0 -33
  178. package/backend/src/flowent/runtime.py +0 -160
  179. package/backend/src/flowent/security.py +0 -37
  180. package/backend/src/flowent/settings.py +0 -2112
  181. package/backend/src/flowent/settings_management.py +0 -394
  182. package/backend/src/flowent/state_db.py +0 -108
  183. package/backend/src/flowent/static/assets/AssistantPage-BW7XAd9I.js +0 -1
  184. package/backend/src/flowent/static/assets/ChannelsPage-tCJHgt6m.js +0 -1
  185. package/backend/src/flowent/static/assets/PageScaffold-f6g2l7XN.js +0 -1
  186. package/backend/src/flowent/static/assets/PromptsPage-C3Sxn2D7.js +0 -1
  187. package/backend/src/flowent/static/assets/ProvidersPage-BfmdXmNt.js +0 -3
  188. package/backend/src/flowent/static/assets/RolesPage-DET8wO4r.js +0 -1
  189. package/backend/src/flowent/static/assets/SettingsPage-D-g3deMm.js +0 -3
  190. package/backend/src/flowent/static/assets/ToolsPage-CDmtE2g4.js +0 -1
  191. package/backend/src/flowent/static/assets/WorkspacePage-AZsJ0sD0.js +0 -3
  192. package/backend/src/flowent/static/assets/WorkspacePanels-CteCjolX.js +0 -1
  193. package/backend/src/flowent/static/assets/alert-dialog-Duorp_S-.js +0 -1
  194. package/backend/src/flowent/static/assets/dialog-C3ixjGjN.js +0 -1
  195. package/backend/src/flowent/static/assets/elk-worker.min-C9JGDOE-.js +0 -6312
  196. package/backend/src/flowent/static/assets/graph-vendor-CHpVij2M.css +0 -1
  197. package/backend/src/flowent/static/assets/graph-vendor-DRq_-6fV.js +0 -7
  198. package/backend/src/flowent/static/assets/index--o_0fv0N.css +0 -1
  199. package/backend/src/flowent/static/assets/index-C9HuekJm.js +0 -10
  200. package/backend/src/flowent/static/assets/layout.worker-jMHqAFbP.js +0 -24
  201. package/backend/src/flowent/static/assets/markdown-vendor-C9RtvaJh.js +0 -29
  202. package/backend/src/flowent/static/assets/modelParams-DmnF2hwR.js +0 -1
  203. package/backend/src/flowent/static/assets/providerTypes-DT3Ahwl_.js +0 -1
  204. package/backend/src/flowent/static/assets/react-vendor-mEs_JJxa.js +0 -9
  205. package/backend/src/flowent/static/assets/roles-CuRT_chR.js +0 -1
  206. package/backend/src/flowent/static/assets/rolldown-runtime-BYbx6iT9.js +0 -1
  207. package/backend/src/flowent/static/assets/select-DCfeNu-F.js +0 -1
  208. package/backend/src/flowent/static/assets/surface-pWwG5ogx.js +0 -1
  209. package/backend/src/flowent/static/assets/ui-vendor-C5pJa8N7.js +0 -51
  210. package/backend/src/flowent/static/assets/useAppRoute-FgSHBKhV.js +0 -1
  211. package/backend/src/flowent/static/favicon.svg +0 -4
  212. package/backend/src/flowent/tools/__init__.py +0 -176
  213. package/backend/src/flowent/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  214. package/backend/src/flowent/tools/__pycache__/connect.cpython-313.pyc +0 -0
  215. package/backend/src/flowent/tools/__pycache__/contacts.cpython-313.pyc +0 -0
  216. package/backend/src/flowent/tools/__pycache__/create_agent.cpython-313.pyc +0 -0
  217. package/backend/src/flowent/tools/__pycache__/create_tab.cpython-313.pyc +0 -0
  218. package/backend/src/flowent/tools/__pycache__/delete_tab.cpython-313.pyc +0 -0
  219. package/backend/src/flowent/tools/__pycache__/edit.cpython-313.pyc +0 -0
  220. package/backend/src/flowent/tools/__pycache__/exec.cpython-313.pyc +0 -0
  221. package/backend/src/flowent/tools/__pycache__/fetch.cpython-313.pyc +0 -0
  222. package/backend/src/flowent/tools/__pycache__/idle.cpython-313.pyc +0 -0
  223. package/backend/src/flowent/tools/__pycache__/list_roles.cpython-313.pyc +0 -0
  224. package/backend/src/flowent/tools/__pycache__/list_tabs.cpython-313.pyc +0 -0
  225. package/backend/src/flowent/tools/__pycache__/list_tools.cpython-313.pyc +0 -0
  226. package/backend/src/flowent/tools/__pycache__/manage_prompts.cpython-313.pyc +0 -0
  227. package/backend/src/flowent/tools/__pycache__/manage_providers.cpython-313.pyc +0 -0
  228. package/backend/src/flowent/tools/__pycache__/manage_roles.cpython-313.pyc +0 -0
  229. package/backend/src/flowent/tools/__pycache__/manage_settings.cpython-313.pyc +0 -0
  230. package/backend/src/flowent/tools/__pycache__/read.cpython-313.pyc +0 -0
  231. package/backend/src/flowent/tools/__pycache__/send.cpython-313.pyc +0 -0
  232. package/backend/src/flowent/tools/__pycache__/set_permissions.cpython-313.pyc +0 -0
  233. package/backend/src/flowent/tools/__pycache__/sleep.cpython-313.pyc +0 -0
  234. package/backend/src/flowent/tools/__pycache__/todo.cpython-313.pyc +0 -0
  235. package/backend/src/flowent/tools/connect.py +0 -100
  236. package/backend/src/flowent/tools/contacts.py +0 -22
  237. package/backend/src/flowent/tools/create_agent.py +0 -191
  238. package/backend/src/flowent/tools/create_tab.py +0 -61
  239. package/backend/src/flowent/tools/delete_tab.py +0 -39
  240. package/backend/src/flowent/tools/edit.py +0 -142
  241. package/backend/src/flowent/tools/exec.py +0 -118
  242. package/backend/src/flowent/tools/fetch.py +0 -85
  243. package/backend/src/flowent/tools/idle.py +0 -27
  244. package/backend/src/flowent/tools/list_roles.py +0 -68
  245. package/backend/src/flowent/tools/list_tabs.py +0 -100
  246. package/backend/src/flowent/tools/list_tools.py +0 -28
  247. package/backend/src/flowent/tools/manage_prompts.py +0 -102
  248. package/backend/src/flowent/tools/manage_providers.py +0 -220
  249. package/backend/src/flowent/tools/manage_roles.py +0 -275
  250. package/backend/src/flowent/tools/manage_settings.py +0 -326
  251. package/backend/src/flowent/tools/read.py +0 -152
  252. package/backend/src/flowent/tools/send.py +0 -68
  253. package/backend/src/flowent/tools/set_permissions.py +0 -99
  254. package/backend/src/flowent/tools/sleep.py +0 -41
  255. package/backend/src/flowent/tools/todo.py +0 -51
  256. package/backend/src/flowent/workspace_store.py +0 -479
  257. package/backend/tests/__init__.py +0 -0
  258. package/backend/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  259. package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  260. package/backend/tests/conftest.py +0 -6
  261. package/backend/tests/integration/api/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  262. package/backend/tests/integration/api/__pycache__/test_access_api.cpython-313-pytest-9.0.3.pyc +0 -0
  263. package/backend/tests/integration/api/__pycache__/test_assistant_api.cpython-313-pytest-9.0.3.pyc +0 -0
  264. package/backend/tests/integration/api/__pycache__/test_frontend_mounting.cpython-313-pytest-9.0.3.pyc +0 -0
  265. package/backend/tests/integration/api/__pycache__/test_meta_api.cpython-313-pytest-9.0.3.pyc +0 -0
  266. package/backend/tests/integration/api/__pycache__/test_nodes_api.cpython-313-pytest-9.0.3.pyc +0 -0
  267. package/backend/tests/integration/api/__pycache__/test_prompts_api.cpython-313-pytest-9.0.3.pyc +0 -0
  268. package/backend/tests/integration/api/__pycache__/test_roles_api.cpython-313-pytest-9.0.3.pyc +0 -0
  269. package/backend/tests/integration/api/__pycache__/test_tabs_api.cpython-313-pytest-9.0.3.pyc +0 -0
  270. package/backend/tests/integration/api/conftest.py +0 -29
  271. package/backend/tests/integration/api/test_access_api.py +0 -182
  272. package/backend/tests/integration/api/test_assistant_api.py +0 -422
  273. package/backend/tests/integration/api/test_frontend_mounting.py +0 -61
  274. package/backend/tests/integration/api/test_meta_api.py +0 -32
  275. package/backend/tests/integration/api/test_nodes_api.py +0 -787
  276. package/backend/tests/integration/api/test_prompts_api.py +0 -47
  277. package/backend/tests/integration/api/test_roles_api.py +0 -228
  278. package/backend/tests/integration/api/test_tabs_api.py +0 -688
  279. package/backend/tests/unit/__pycache__/test_access.cpython-313-pytest-9.0.3.pyc +0 -0
  280. package/backend/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  281. package/backend/tests/unit/__pycache__/test_graph_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  282. package/backend/tests/unit/__pycache__/test_network.cpython-313-pytest-9.0.3.pyc +0 -0
  283. package/backend/tests/unit/__pycache__/test_state_sqlite_storage.cpython-313-pytest-9.0.3.pyc +0 -0
  284. package/backend/tests/unit/__pycache__/test_workspace_store.cpython-313-pytest-9.0.3.pyc +0 -0
  285. package/backend/tests/unit/agent/__pycache__/test_agent_public_api.cpython-313-pytest-9.0.3.pyc +0 -0
  286. package/backend/tests/unit/agent/__pycache__/test_agent_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  287. package/backend/tests/unit/agent/test_agent_public_api.py +0 -822
  288. package/backend/tests/unit/agent/test_agent_runtime.py +0 -3088
  289. package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
  290. package/backend/tests/unit/channels/test_telegram_channel.py +0 -552
  291. package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
  292. package/backend/tests/unit/logging/test_logging.py +0 -132
  293. package/backend/tests/unit/prompts/__pycache__/test_prompts.cpython-313-pytest-9.0.3.pyc +0 -0
  294. package/backend/tests/unit/prompts/test_prompts.py +0 -570
  295. package/backend/tests/unit/providers/__pycache__/test_anthropic_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  296. package/backend/tests/unit/providers/__pycache__/test_errors.cpython-313-pytest-9.0.3.pyc +0 -0
  297. package/backend/tests/unit/providers/__pycache__/test_extract_delta_parts.cpython-313-pytest-9.0.3.pyc +0 -0
  298. package/backend/tests/unit/providers/__pycache__/test_openai_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  299. package/backend/tests/unit/providers/__pycache__/test_openai_responses.cpython-313-pytest-9.0.3.pyc +0 -0
  300. package/backend/tests/unit/providers/__pycache__/test_provider_gateway.cpython-313-pytest-9.0.3.pyc +0 -0
  301. package/backend/tests/unit/providers/__pycache__/test_think_tag_parser.cpython-313-pytest-9.0.3.pyc +0 -0
  302. package/backend/tests/unit/providers/test_anthropic_provider.py +0 -185
  303. package/backend/tests/unit/providers/test_errors.py +0 -68
  304. package/backend/tests/unit/providers/test_extract_delta_parts.py +0 -22
  305. package/backend/tests/unit/providers/test_openai_provider.py +0 -139
  306. package/backend/tests/unit/providers/test_openai_responses.py +0 -402
  307. package/backend/tests/unit/providers/test_provider_gateway.py +0 -359
  308. package/backend/tests/unit/providers/test_think_tag_parser.py +0 -36
  309. package/backend/tests/unit/routes/__pycache__/test_prompts_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  310. package/backend/tests/unit/routes/__pycache__/test_providers_route.cpython-313-pytest-9.0.3.pyc +0 -0
  311. package/backend/tests/unit/routes/__pycache__/test_roles_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  312. package/backend/tests/unit/routes/__pycache__/test_settings_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  313. package/backend/tests/unit/routes/test_prompts_routes.py +0 -82
  314. package/backend/tests/unit/routes/test_providers_route.py +0 -370
  315. package/backend/tests/unit/routes/test_roles_routes.py +0 -539
  316. package/backend/tests/unit/routes/test_settings_routes.py +0 -1123
  317. package/backend/tests/unit/runtime/__pycache__/test_bootstrap_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  318. package/backend/tests/unit/runtime/test_bootstrap_runtime.py +0 -1002
  319. package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
  320. package/backend/tests/unit/sandbox/test_sandbox_tools.py +0 -78
  321. package/backend/tests/unit/security/__pycache__/test_security.cpython-313-pytest-9.0.3.pyc +0 -0
  322. package/backend/tests/unit/security/test_security.py +0 -124
  323. package/backend/tests/unit/settings/__pycache__/test_settings_roles.cpython-313-pytest-9.0.3.pyc +0 -0
  324. package/backend/tests/unit/settings/test_settings_roles.py +0 -703
  325. package/backend/tests/unit/test_access.py +0 -45
  326. package/backend/tests/unit/test_cli.py +0 -102
  327. package/backend/tests/unit/test_graph_runtime.py +0 -72
  328. package/backend/tests/unit/test_network.py +0 -51
  329. package/backend/tests/unit/test_state_sqlite_storage.py +0 -87
  330. package/backend/tests/unit/test_workspace_store.py +0 -228
  331. package/backend/tests/unit/tools/__pycache__/test_connect_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  332. package/backend/tests/unit/tools/__pycache__/test_create_agent_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  333. package/backend/tests/unit/tools/__pycache__/test_delete_tab_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  334. package/backend/tests/unit/tools/__pycache__/test_edit_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  335. package/backend/tests/unit/tools/__pycache__/test_exec_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  336. package/backend/tests/unit/tools/__pycache__/test_fetch_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  337. package/backend/tests/unit/tools/__pycache__/test_manage_prompts_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  338. package/backend/tests/unit/tools/__pycache__/test_manage_providers_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  339. package/backend/tests/unit/tools/__pycache__/test_manage_roles_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  340. package/backend/tests/unit/tools/__pycache__/test_manage_settings_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  341. package/backend/tests/unit/tools/__pycache__/test_read_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  342. package/backend/tests/unit/tools/__pycache__/test_set_permissions_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  343. package/backend/tests/unit/tools/__pycache__/test_todo_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  344. package/backend/tests/unit/tools/__pycache__/test_tool_registry.cpython-313-pytest-9.0.3.pyc +0 -0
  345. package/backend/tests/unit/tools/test_connect_tool.py +0 -228
  346. package/backend/tests/unit/tools/test_create_agent_tool.py +0 -404
  347. package/backend/tests/unit/tools/test_delete_tab_tool.py +0 -116
  348. package/backend/tests/unit/tools/test_edit_tool.py +0 -115
  349. package/backend/tests/unit/tools/test_exec_tool.py +0 -81
  350. package/backend/tests/unit/tools/test_fetch_tool.py +0 -65
  351. package/backend/tests/unit/tools/test_manage_prompts_tool.py +0 -92
  352. package/backend/tests/unit/tools/test_manage_providers_tool.py +0 -460
  353. package/backend/tests/unit/tools/test_manage_roles_tool.py +0 -411
  354. package/backend/tests/unit/tools/test_manage_settings_tool.py +0 -611
  355. package/backend/tests/unit/tools/test_read_tool.py +0 -33
  356. package/backend/tests/unit/tools/test_set_permissions_tool.py +0 -595
  357. package/backend/tests/unit/tools/test_todo_tool.py +0 -37
  358. package/backend/tests/unit/tools/test_tool_registry.py +0 -199
  359. package/dist/frontend/assets/AssistantPage-BW7XAd9I.js +0 -1
  360. package/dist/frontend/assets/ChannelsPage-tCJHgt6m.js +0 -1
  361. package/dist/frontend/assets/PageScaffold-f6g2l7XN.js +0 -1
  362. package/dist/frontend/assets/PromptsPage-C3Sxn2D7.js +0 -1
  363. package/dist/frontend/assets/ProvidersPage-BfmdXmNt.js +0 -3
  364. package/dist/frontend/assets/RolesPage-DET8wO4r.js +0 -1
  365. package/dist/frontend/assets/SettingsPage-D-g3deMm.js +0 -3
  366. package/dist/frontend/assets/ToolsPage-CDmtE2g4.js +0 -1
  367. package/dist/frontend/assets/WorkspacePage-AZsJ0sD0.js +0 -3
  368. package/dist/frontend/assets/WorkspacePanels-CteCjolX.js +0 -1
  369. package/dist/frontend/assets/alert-dialog-Duorp_S-.js +0 -1
  370. package/dist/frontend/assets/dialog-C3ixjGjN.js +0 -1
  371. package/dist/frontend/assets/elk-worker.min-C9JGDOE-.js +0 -6312
  372. package/dist/frontend/assets/graph-vendor-CHpVij2M.css +0 -1
  373. package/dist/frontend/assets/graph-vendor-DRq_-6fV.js +0 -7
  374. package/dist/frontend/assets/index--o_0fv0N.css +0 -1
  375. package/dist/frontend/assets/index-C9HuekJm.js +0 -10
  376. package/dist/frontend/assets/layout.worker-jMHqAFbP.js +0 -24
  377. package/dist/frontend/assets/markdown-vendor-C9RtvaJh.js +0 -29
  378. package/dist/frontend/assets/modelParams-DmnF2hwR.js +0 -1
  379. package/dist/frontend/assets/providerTypes-DT3Ahwl_.js +0 -1
  380. package/dist/frontend/assets/react-vendor-mEs_JJxa.js +0 -9
  381. package/dist/frontend/assets/roles-CuRT_chR.js +0 -1
  382. package/dist/frontend/assets/rolldown-runtime-BYbx6iT9.js +0 -1
  383. package/dist/frontend/assets/select-DCfeNu-F.js +0 -1
  384. package/dist/frontend/assets/surface-pWwG5ogx.js +0 -1
  385. package/dist/frontend/assets/ui-vendor-C5pJa8N7.js +0 -51
  386. package/dist/frontend/assets/useAppRoute-FgSHBKhV.js +0 -1
  387. package/dist/frontend/favicon.svg +0 -4
@@ -1,1123 +0,0 @@
1
- import asyncio
2
- from pathlib import Path
3
-
4
- import pytest
5
- from fastapi import HTTPException
6
-
7
- from flowent.access import set_access_code, verify_access_code
8
- from flowent.agent import Agent
9
- from flowent.models import NodeConfig, NodeType, SystemEntry
10
- from flowent.prompts.steward import STEWARD_ROLE_SYSTEM_PROMPT
11
- from flowent.registry import registry
12
- from flowent.routes.settings import (
13
- UpdateSettingsRequest,
14
- UpdateTelegramSettingsRequest,
15
- approve_telegram_chat,
16
- delete_pending_telegram_chat,
17
- delete_telegram_chat,
18
- get_settings_api,
19
- get_settings_bootstrap,
20
- get_telegram_settings,
21
- update_settings,
22
- update_telegram_settings,
23
- )
24
- from flowent.settings import (
25
- ProviderConfig,
26
- RoleConfig,
27
- Settings,
28
- TelegramApprovedChat,
29
- TelegramPendingChat,
30
- TelegramSettings,
31
- build_default_assistant_write_dirs,
32
- )
33
-
34
-
35
- def test_get_settings_returns_assistant_configuration(monkeypatch):
36
- settings = Settings(
37
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
38
- )
39
-
40
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
41
-
42
- result = asyncio.run(get_settings_api())
43
-
44
- assert result["assistant"] == {
45
- "role_name": "Steward",
46
- "allow_network": True,
47
- "write_dirs": build_default_assistant_write_dirs(),
48
- }
49
- assert result["leader"] == {"role_name": "Conductor"}
50
-
51
-
52
- def test_get_settings_bootstrap_returns_related_resources(monkeypatch):
53
- settings = Settings(
54
- providers=[
55
- ProviderConfig(
56
- id="provider-1",
57
- name="Primary",
58
- type="openai_compatible",
59
- base_url="https://api.example.com/v1",
60
- api_key="secret",
61
- )
62
- ],
63
- roles=[
64
- RoleConfig(
65
- name="Steward",
66
- description="Default assistant role.",
67
- system_prompt="Default assistant role.",
68
- )
69
- ],
70
- )
71
-
72
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
73
- monkeypatch.setattr("flowent._version.__version__", "1.2.3")
74
-
75
- result = asyncio.run(get_settings_bootstrap())
76
-
77
- assert result == {
78
- "settings": {
79
- "app_data_dir": settings.app_data_dir,
80
- "working_dir": settings.working_dir,
81
- "event_log": {"timestamp_format": "absolute"},
82
- "access": {"configured": False},
83
- "assistant": {
84
- "role_name": "Steward",
85
- "allow_network": True,
86
- "write_dirs": build_default_assistant_write_dirs(),
87
- },
88
- "leader": {"role_name": "Conductor"},
89
- "telegram": {
90
- "bot_token": "",
91
- "pending_chats": [],
92
- "approved_chats": [],
93
- },
94
- "model": {
95
- "active_provider_id": "",
96
- "active_model": "",
97
- "input_image": None,
98
- "output_image": None,
99
- "structured_output": None,
100
- "capabilities": None,
101
- "context_window_tokens": None,
102
- "resolved_context_window_tokens": None,
103
- "timeout_ms": 10000,
104
- "retry_policy": "limited",
105
- "max_retries": 5,
106
- "retry_initial_delay_seconds": 0.5,
107
- "retry_max_delay_seconds": 8.0,
108
- "retry_backoff_cap_retries": 5,
109
- "auto_compact_token_limit": None,
110
- "params": {
111
- "reasoning_effort": None,
112
- "verbosity": None,
113
- "max_output_tokens": None,
114
- "temperature": None,
115
- "top_p": None,
116
- },
117
- },
118
- "custom_prompt": "",
119
- "custom_post_prompt": "",
120
- "providers": [
121
- {
122
- "id": "provider-1",
123
- "name": "Primary",
124
- "type": "openai_compatible",
125
- "base_url": "https://api.example.com/v1",
126
- "api_key": "secret",
127
- "headers": {},
128
- "retry_429_delay_seconds": 0,
129
- "models": [],
130
- }
131
- ],
132
- "roles": [
133
- {
134
- "name": "Steward",
135
- "description": "Default assistant role.",
136
- "system_prompt": "Default assistant role.",
137
- "model": None,
138
- "model_params": None,
139
- "included_tools": [],
140
- "excluded_tools": [],
141
- }
142
- ],
143
- },
144
- "providers": [
145
- {
146
- "id": "provider-1",
147
- "name": "Primary",
148
- "type": "openai_compatible",
149
- "base_url": "https://api.example.com/v1",
150
- "api_key": "secret",
151
- "headers": {},
152
- "retry_429_delay_seconds": 0,
153
- "models": [],
154
- }
155
- ],
156
- "roles": [
157
- {
158
- "name": "Steward",
159
- "description": "Default assistant role.",
160
- "system_prompt": "Default assistant role.",
161
- "model": None,
162
- "model_params": None,
163
- "included_tools": [],
164
- "excluded_tools": [],
165
- "is_builtin": True,
166
- }
167
- ],
168
- "version": "1.2.3",
169
- }
170
-
171
-
172
- def test_get_telegram_settings_masks_bot_token(monkeypatch):
173
- settings = Settings(
174
- telegram=TelegramSettings(
175
- bot_token="123456:ABCDE",
176
- pending_chats=[
177
- TelegramPendingChat(
178
- chat_id=1001,
179
- username="alice",
180
- display_name="Alice",
181
- first_seen_at=1.0,
182
- last_seen_at=2.0,
183
- )
184
- ],
185
- approved_chats=[
186
- TelegramApprovedChat(
187
- chat_id=-2002,
188
- username="bob",
189
- display_name="Bob",
190
- approved_at=3.0,
191
- )
192
- ],
193
- )
194
- )
195
-
196
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
197
-
198
- result = asyncio.run(get_telegram_settings())
199
-
200
- assert result == {
201
- "bot_token": "sk-...BCDE",
202
- "pending_chats": [
203
- {
204
- "chat_id": 1001,
205
- "username": "alice",
206
- "display_name": "Alice",
207
- "first_seen_at": 1.0,
208
- "last_seen_at": 2.0,
209
- }
210
- ],
211
- "approved_chats": [
212
- {
213
- "chat_id": -2002,
214
- "username": "bob",
215
- "display_name": "Bob",
216
- "approved_at": 3.0,
217
- }
218
- ],
219
- }
220
-
221
-
222
- def test_update_telegram_settings_restarts_channel_when_token_changes(monkeypatch):
223
- settings = Settings(
224
- telegram=TelegramSettings(
225
- bot_token="old-token",
226
- pending_chats=[
227
- TelegramPendingChat(
228
- chat_id=1001,
229
- username="alice",
230
- display_name="Alice",
231
- first_seen_at=1.0,
232
- last_seen_at=2.0,
233
- )
234
- ],
235
- approved_chats=[
236
- TelegramApprovedChat(
237
- chat_id=-2002,
238
- username="bob",
239
- display_name="Bob",
240
- approved_at=3.0,
241
- )
242
- ],
243
- )
244
- )
245
- saved: list[Settings] = []
246
- restarted: list[str] = []
247
-
248
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
249
- monkeypatch.setattr(
250
- "flowent.routes.settings.save_settings",
251
- lambda current: saved.append(current),
252
- )
253
- monkeypatch.setattr(
254
- "flowent.runtime.restart_telegram_channel",
255
- lambda: restarted.append("restart"),
256
- )
257
-
258
- result = asyncio.run(
259
- update_telegram_settings(
260
- UpdateTelegramSettingsRequest(
261
- bot_token="new-token",
262
- )
263
- )
264
- )
265
-
266
- assert settings.telegram == TelegramSettings(
267
- bot_token="new-token",
268
- pending_chats=[
269
- TelegramPendingChat(
270
- chat_id=1001,
271
- username="alice",
272
- display_name="Alice",
273
- first_seen_at=1.0,
274
- last_seen_at=2.0,
275
- )
276
- ],
277
- approved_chats=[
278
- TelegramApprovedChat(
279
- chat_id=-2002,
280
- username="bob",
281
- display_name="Bob",
282
- approved_at=3.0,
283
- )
284
- ],
285
- )
286
- assert saved == [settings]
287
- assert restarted == ["restart"]
288
- assert result == {
289
- "status": "saved",
290
- "telegram": {
291
- "bot_token": "sk-...oken",
292
- "pending_chats": [
293
- {
294
- "chat_id": 1001,
295
- "username": "alice",
296
- "display_name": "Alice",
297
- "first_seen_at": 1.0,
298
- "last_seen_at": 2.0,
299
- }
300
- ],
301
- "approved_chats": [
302
- {
303
- "chat_id": -2002,
304
- "username": "bob",
305
- "display_name": "Bob",
306
- "approved_at": 3.0,
307
- }
308
- ],
309
- },
310
- }
311
-
312
-
313
- def test_approve_telegram_chat_moves_pending_chat_to_approved(monkeypatch):
314
- settings = Settings(
315
- telegram=TelegramSettings(
316
- bot_token="token",
317
- pending_chats=[
318
- TelegramPendingChat(
319
- chat_id=3003,
320
- username="alice",
321
- display_name="Alice",
322
- first_seen_at=1.0,
323
- last_seen_at=2.0,
324
- )
325
- ],
326
- approved_chats=[],
327
- )
328
- )
329
- saved: list[Settings] = []
330
-
331
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
332
- monkeypatch.setattr(
333
- "flowent.routes.settings.save_settings",
334
- lambda current: saved.append(current),
335
- )
336
- monkeypatch.setattr("flowent.routes.settings.time.time", lambda: 42.0)
337
-
338
- result = asyncio.run(approve_telegram_chat(3003))
339
-
340
- assert settings.telegram.pending_chats == []
341
- assert settings.telegram.approved_chats == [
342
- TelegramApprovedChat(
343
- chat_id=3003,
344
- username="alice",
345
- display_name="Alice",
346
- approved_at=42.0,
347
- )
348
- ]
349
- assert saved == [settings]
350
- assert result == {
351
- "status": "approved",
352
- "telegram": {
353
- "bot_token": "sk-...oken",
354
- "pending_chats": [],
355
- "approved_chats": [
356
- {
357
- "chat_id": 3003,
358
- "username": "alice",
359
- "display_name": "Alice",
360
- "approved_at": 42.0,
361
- }
362
- ],
363
- },
364
- }
365
-
366
-
367
- def test_delete_pending_telegram_chat_removes_pending_chat(monkeypatch):
368
- settings = Settings(
369
- telegram=TelegramSettings(
370
- bot_token="token",
371
- pending_chats=[
372
- TelegramPendingChat(
373
- chat_id=3003,
374
- username="alice",
375
- display_name="Alice",
376
- first_seen_at=1.0,
377
- last_seen_at=2.0,
378
- )
379
- ],
380
- )
381
- )
382
- saved: list[Settings] = []
383
-
384
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
385
- monkeypatch.setattr(
386
- "flowent.routes.settings.save_settings",
387
- lambda current: saved.append(current),
388
- )
389
-
390
- result = asyncio.run(delete_pending_telegram_chat(3003))
391
-
392
- assert settings.telegram.pending_chats == []
393
- assert saved == [settings]
394
- assert result == {
395
- "status": "deleted",
396
- "telegram": {
397
- "bot_token": "sk-...oken",
398
- "pending_chats": [],
399
- "approved_chats": [],
400
- },
401
- }
402
-
403
-
404
- def test_delete_telegram_chat_removes_approved_chat(monkeypatch):
405
- settings = Settings(
406
- telegram=TelegramSettings(
407
- bot_token="token",
408
- approved_chats=[
409
- TelegramApprovedChat(
410
- chat_id=-2002,
411
- username="bob",
412
- display_name="Bob",
413
- approved_at=3.0,
414
- ),
415
- TelegramApprovedChat(
416
- chat_id=3003,
417
- username="alice",
418
- display_name="Alice",
419
- approved_at=4.0,
420
- ),
421
- ],
422
- )
423
- )
424
- saved: list[Settings] = []
425
-
426
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
427
- monkeypatch.setattr(
428
- "flowent.routes.settings.save_settings",
429
- lambda current: saved.append(current),
430
- )
431
-
432
- result = asyncio.run(delete_telegram_chat(-2002))
433
-
434
- assert settings.telegram.approved_chats == [
435
- TelegramApprovedChat(
436
- chat_id=3003,
437
- username="alice",
438
- display_name="Alice",
439
- approved_at=4.0,
440
- )
441
- ]
442
- assert saved == [settings]
443
- assert result == {
444
- "status": "deleted",
445
- "telegram": {
446
- "bot_token": "sk-...oken",
447
- "pending_chats": [],
448
- "approved_chats": [
449
- {
450
- "chat_id": 3003,
451
- "username": "alice",
452
- "display_name": "Alice",
453
- "approved_at": 4.0,
454
- }
455
- ],
456
- },
457
- }
458
-
459
-
460
- def test_update_settings_accepts_xhigh_reasoning_effort(monkeypatch):
461
- settings = Settings(
462
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
463
- )
464
- saved: list[Settings] = []
465
-
466
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
467
- monkeypatch.setattr(
468
- "flowent.routes.settings.save_settings",
469
- lambda current: saved.append(current),
470
- )
471
- monkeypatch.setattr(
472
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
473
- )
474
-
475
- result = asyncio.run(
476
- update_settings(
477
- UpdateSettingsRequest(
478
- model={"params": {"reasoning_effort": "xhigh"}},
479
- )
480
- )
481
- )
482
-
483
- assert settings.model.params.reasoning_effort == "xhigh"
484
- assert result["settings"]["model"]["params"]["reasoning_effort"] == "xhigh"
485
- assert saved == [settings]
486
-
487
-
488
- def test_update_settings_rotates_access_code_and_requires_reauth(monkeypatch):
489
- settings = Settings(
490
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
491
- )
492
- set_access_code(settings, "OLD-ACCESS-CODE")
493
- saved: list[Settings] = []
494
- closed: list[dict[str, object]] = []
495
-
496
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
497
- monkeypatch.setattr(
498
- "flowent.routes.settings.save_settings",
499
- lambda current: saved.append(current),
500
- )
501
- monkeypatch.setattr(
502
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
503
- )
504
- monkeypatch.setattr(
505
- "flowent.routes.settings.event_bus.close_all_connections",
506
- lambda **kwargs: closed.append(kwargs),
507
- )
508
-
509
- result = asyncio.run(
510
- update_settings(
511
- UpdateSettingsRequest(
512
- access={
513
- "new_code": "NEW-ACCESS-CODE",
514
- "confirm_code": "NEW-ACCESS-CODE",
515
- }
516
- )
517
- )
518
- )
519
-
520
- assert verify_access_code(settings.access, "NEW-ACCESS-CODE")
521
- assert not verify_access_code(settings.access, "OLD-ACCESS-CODE")
522
- assert saved == [settings]
523
- assert result["reauth_required"] is True
524
- assert result["settings"]["access"] == {"configured": True}
525
- assert closed == [{"code": 4001, "reason": "Access code rotated"}]
526
-
527
-
528
- def test_update_settings_does_not_mutate_cached_access_when_save_fails(monkeypatch):
529
- settings = Settings(
530
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
531
- )
532
- set_access_code(settings, "OLD-ACCESS-CODE")
533
-
534
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
535
- monkeypatch.setattr(
536
- "flowent.routes.settings.save_settings",
537
- lambda current: (_ for _ in ()).throw(RuntimeError("disk full")),
538
- )
539
-
540
- with pytest.raises(RuntimeError, match="disk full"):
541
- asyncio.run(
542
- update_settings(
543
- UpdateSettingsRequest(
544
- access={
545
- "new_code": "NEW-ACCESS-CODE",
546
- "confirm_code": "NEW-ACCESS-CODE",
547
- }
548
- )
549
- )
550
- )
551
-
552
- assert verify_access_code(settings.access, "OLD-ACCESS-CODE")
553
- assert not verify_access_code(settings.access, "NEW-ACCESS-CODE")
554
-
555
-
556
- def test_update_settings_accepts_model_max_retries(monkeypatch):
557
- settings = Settings(
558
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
559
- )
560
- saved: list[Settings] = []
561
-
562
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
563
- monkeypatch.setattr(
564
- "flowent.routes.settings.save_settings",
565
- lambda current: saved.append(current),
566
- )
567
- monkeypatch.setattr(
568
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
569
- )
570
-
571
- result = asyncio.run(
572
- update_settings(
573
- UpdateSettingsRequest(
574
- model={"max_retries": 8},
575
- )
576
- )
577
- )
578
-
579
- assert settings.model.max_retries == 8
580
- assert result["settings"]["model"]["max_retries"] == 8
581
- assert saved == [settings]
582
-
583
-
584
- def test_update_settings_accepts_model_metadata_overrides_and_token_limit(
585
- monkeypatch,
586
- ):
587
- settings = Settings(
588
- providers=[
589
- ProviderConfig(
590
- id="provider-1",
591
- name="Primary",
592
- type="openai_responses",
593
- base_url="https://api.example.com/v1",
594
- api_key="secret",
595
- )
596
- ],
597
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")],
598
- )
599
- settings.model.active_provider_id = "provider-1"
600
- settings.model.active_model = "gpt-5.2"
601
- saved: list[Settings] = []
602
-
603
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
604
- monkeypatch.setattr(
605
- "flowent.routes.settings.save_settings",
606
- lambda current: saved.append(current),
607
- )
608
- monkeypatch.setattr(
609
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
610
- )
611
-
612
- result = asyncio.run(
613
- update_settings(
614
- UpdateSettingsRequest(
615
- model={
616
- "context_window_tokens": 64000,
617
- "input_image": True,
618
- "output_image": False,
619
- "structured_output": True,
620
- "auto_compact_token_limit": 48000,
621
- },
622
- )
623
- )
624
- )
625
-
626
- assert settings.model.context_window_tokens == 64000
627
- assert settings.model.input_image is True
628
- assert settings.model.output_image is False
629
- assert settings.model.structured_output is True
630
- assert settings.model.auto_compact_token_limit == 48000
631
- assert result["settings"]["model"]["context_window_tokens"] == 64000
632
- assert result["settings"]["model"]["resolved_context_window_tokens"] == 64000
633
- assert result["settings"]["model"]["capabilities"] == {
634
- "input_image": True,
635
- "output_image": False,
636
- "structured_output": True,
637
- }
638
- assert result["settings"]["model"]["auto_compact_token_limit"] == 48000
639
- assert saved == [settings]
640
-
641
-
642
- def test_update_settings_accepts_model_retry_policy(monkeypatch):
643
- settings = Settings(
644
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
645
- )
646
- saved: list[Settings] = []
647
-
648
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
649
- monkeypatch.setattr(
650
- "flowent.routes.settings.save_settings",
651
- lambda current: saved.append(current),
652
- )
653
- monkeypatch.setattr(
654
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
655
- )
656
-
657
- result = asyncio.run(
658
- update_settings(
659
- UpdateSettingsRequest(
660
- model={"retry_policy": "unlimited"},
661
- )
662
- )
663
- )
664
-
665
- assert settings.model.retry_policy == "unlimited"
666
- assert result["settings"]["model"]["retry_policy"] == "unlimited"
667
- assert saved == [settings]
668
-
669
-
670
- def test_update_settings_rejects_invalid_model_retry_policy(monkeypatch):
671
- settings = Settings(
672
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
673
- )
674
-
675
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
676
-
677
- with pytest.raises(HTTPException) as excinfo:
678
- asyncio.run(
679
- update_settings(
680
- UpdateSettingsRequest(
681
- model={"retry_policy": "forever"},
682
- )
683
- )
684
- )
685
-
686
- assert excinfo.value.status_code == 400
687
- assert (
688
- excinfo.value.detail
689
- == "model.retry_policy must be one of: limited, no_retry, unlimited"
690
- )
691
-
692
-
693
- def test_update_settings_accepts_model_timeout_ms(monkeypatch):
694
- settings = Settings(
695
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
696
- )
697
- saved: list[Settings] = []
698
-
699
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
700
- monkeypatch.setattr(
701
- "flowent.routes.settings.save_settings",
702
- lambda current: saved.append(current),
703
- )
704
- monkeypatch.setattr(
705
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
706
- )
707
-
708
- result = asyncio.run(
709
- update_settings(
710
- UpdateSettingsRequest(
711
- model={"timeout_ms": 15000},
712
- )
713
- )
714
- )
715
-
716
- assert settings.model.timeout_ms == 15000
717
- assert result["settings"]["model"]["timeout_ms"] == 15000
718
- assert saved == [settings]
719
-
720
-
721
- def test_update_settings_accepts_retry_backoff_fields(monkeypatch):
722
- settings = Settings(
723
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
724
- )
725
- saved: list[Settings] = []
726
-
727
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
728
- monkeypatch.setattr(
729
- "flowent.routes.settings.save_settings",
730
- lambda current: saved.append(current),
731
- )
732
- monkeypatch.setattr(
733
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
734
- )
735
-
736
- result = asyncio.run(
737
- update_settings(
738
- UpdateSettingsRequest(
739
- model={
740
- "retry_initial_delay_seconds": 0.75,
741
- "retry_max_delay_seconds": 12.0,
742
- "retry_backoff_cap_retries": 3,
743
- },
744
- )
745
- )
746
- )
747
-
748
- assert settings.model.retry_initial_delay_seconds == 0.75
749
- assert settings.model.retry_max_delay_seconds == 12.0
750
- assert settings.model.retry_backoff_cap_retries == 3
751
- assert result["settings"]["model"]["retry_initial_delay_seconds"] == 0.75
752
- assert result["settings"]["model"]["retry_max_delay_seconds"] == 12.0
753
- assert result["settings"]["model"]["retry_backoff_cap_retries"] == 3
754
- assert saved == [settings]
755
-
756
-
757
- def test_update_settings_rejects_retry_backoff_when_max_below_initial(monkeypatch):
758
- settings = Settings(
759
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
760
- )
761
-
762
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
763
-
764
- with pytest.raises(HTTPException) as excinfo:
765
- asyncio.run(
766
- update_settings(
767
- UpdateSettingsRequest(
768
- model={
769
- "retry_initial_delay_seconds": 1.5,
770
- "retry_max_delay_seconds": 1.0,
771
- },
772
- )
773
- )
774
- )
775
-
776
- assert excinfo.value.status_code == 400
777
- assert (
778
- excinfo.value.detail
779
- == "model.retry_max_delay_seconds must be greater than or equal to model.retry_initial_delay_seconds"
780
- )
781
-
782
-
783
- def test_update_settings_rejects_non_positive_model_timeout_ms(monkeypatch):
784
- settings = Settings(
785
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
786
- )
787
-
788
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
789
-
790
- with pytest.raises(HTTPException) as excinfo:
791
- asyncio.run(
792
- update_settings(
793
- UpdateSettingsRequest(
794
- model={"timeout_ms": 0},
795
- )
796
- )
797
- )
798
-
799
- assert excinfo.value.status_code == 400
800
- assert excinfo.value.detail == "model.timeout_ms must be greater than 0"
801
-
802
-
803
- def test_update_settings_persists_assistant_role(monkeypatch):
804
- settings = Settings(
805
- roles=[
806
- RoleConfig(name="Steward", system_prompt="Default assistant role."),
807
- RoleConfig(name="Reviewer", system_prompt="Review carefully."),
808
- ]
809
- )
810
- saved: list[Settings] = []
811
-
812
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
813
- monkeypatch.setattr(
814
- "flowent.routes.settings.save_settings",
815
- lambda current: saved.append(current),
816
- )
817
- monkeypatch.setattr(
818
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
819
- )
820
-
821
- result = asyncio.run(
822
- update_settings(
823
- UpdateSettingsRequest(assistant={"role_name": "Reviewer"}),
824
- )
825
- )
826
-
827
- assert settings.assistant.role_name == "Reviewer"
828
- assert result["settings"]["assistant"] == {
829
- "role_name": "Reviewer",
830
- "allow_network": True,
831
- "write_dirs": build_default_assistant_write_dirs(),
832
- }
833
- assert saved == [settings]
834
-
835
-
836
- def test_update_settings_keeps_live_assistant_entry_semantics_for_non_steward_role(
837
- monkeypatch,
838
- ):
839
- registry.reset()
840
- settings = Settings(
841
- roles=[
842
- RoleConfig(name="Steward", system_prompt="Default assistant role."),
843
- RoleConfig(
844
- name="Reviewer",
845
- system_prompt="Review carefully.",
846
- included_tools=["read"],
847
- ),
848
- ]
849
- )
850
- assistant = Agent(
851
- NodeConfig(
852
- node_type=NodeType.ASSISTANT,
853
- role_name="Steward",
854
- tools=[
855
- "create_workflow",
856
- "delete_workflow",
857
- "set_permissions",
858
- "manage_settings",
859
- ],
860
- )
861
- )
862
- saved: list[Settings] = []
863
-
864
- registry.register(assistant)
865
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
866
- monkeypatch.setattr("flowent.settings.get_settings", lambda: settings)
867
- monkeypatch.setattr(
868
- "flowent.routes.settings.save_settings",
869
- lambda current: saved.append(current),
870
- )
871
- monkeypatch.setattr("flowent.graph_service.sync_tab_leaders", lambda reason: None)
872
- monkeypatch.setattr(
873
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
874
- )
875
-
876
- try:
877
- result = asyncio.run(
878
- update_settings(
879
- UpdateSettingsRequest(assistant={"role_name": "Reviewer"}),
880
- )
881
- )
882
-
883
- assert result["settings"]["assistant"] == {
884
- "role_name": "Reviewer",
885
- "allow_network": True,
886
- "write_dirs": build_default_assistant_write_dirs(),
887
- }
888
- assert assistant.config.role_name == "Reviewer"
889
- assert "create_workflow" in assistant.config.tools
890
- assert "delete_workflow" in assistant.config.tools
891
- assert "set_permissions" in assistant.config.tools
892
- assert "manage_settings" in assistant.config.tools
893
- assert "read" in assistant.config.tools
894
- system_prompt = next(
895
- entry.content
896
- for entry in assistant.history
897
- if isinstance(entry, SystemEntry)
898
- )
899
- assert STEWARD_ROLE_SYSTEM_PROMPT in system_prompt
900
- assert "## Selected Role Overlay" in system_prompt
901
- assert "Review carefully." in system_prompt
902
- assert saved == [settings]
903
- finally:
904
- registry.reset()
905
-
906
-
907
- def test_update_settings_persists_assistant_permissions(monkeypatch):
908
- settings = Settings(
909
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
910
- )
911
- saved: list[Settings] = []
912
-
913
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
914
- monkeypatch.setattr(
915
- "flowent.routes.settings.save_settings",
916
- lambda current: saved.append(current),
917
- )
918
- monkeypatch.setattr(
919
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
920
- )
921
-
922
- result = asyncio.run(
923
- update_settings(
924
- UpdateSettingsRequest(
925
- assistant={
926
- "allow_network": False,
927
- "write_dirs": [" ./tmp ", "./tmp/", ""],
928
- }
929
- ),
930
- )
931
- )
932
-
933
- expected_write_dirs = [str((Path.cwd() / "tmp").resolve())]
934
- assert settings.assistant.allow_network is False
935
- assert settings.assistant.write_dirs == expected_write_dirs
936
- assert result["settings"]["assistant"] == {
937
- "role_name": "Steward",
938
- "allow_network": False,
939
- "write_dirs": expected_write_dirs,
940
- }
941
- assert saved == [settings]
942
-
943
-
944
- def test_update_settings_persists_working_dir(monkeypatch, tmp_path):
945
- settings = Settings(
946
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
947
- )
948
- saved: list[Settings] = []
949
-
950
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
951
- monkeypatch.setattr(
952
- "flowent.routes.settings.save_settings",
953
- lambda current: saved.append(current),
954
- )
955
- monkeypatch.setattr(
956
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
957
- )
958
-
959
- result = asyncio.run(
960
- update_settings(
961
- UpdateSettingsRequest(working_dir=str(tmp_path)),
962
- )
963
- )
964
-
965
- assert settings.working_dir == str(tmp_path.resolve())
966
- assert result["settings"]["working_dir"] == str(tmp_path.resolve())
967
- assert saved == [settings]
968
-
969
-
970
- def test_update_settings_resolves_assistant_write_dirs_against_new_working_dir(
971
- monkeypatch,
972
- tmp_path,
973
- ):
974
- settings = Settings(
975
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
976
- )
977
- saved: list[Settings] = []
978
- target_dir = tmp_path / "project"
979
- target_dir.mkdir()
980
-
981
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
982
- monkeypatch.setattr(
983
- "flowent.routes.settings.save_settings",
984
- lambda current: saved.append(current),
985
- )
986
- monkeypatch.setattr(
987
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
988
- )
989
-
990
- result = asyncio.run(
991
- update_settings(
992
- UpdateSettingsRequest(
993
- working_dir=str(target_dir),
994
- assistant={"write_dirs": ["./out"]},
995
- ),
996
- )
997
- )
998
-
999
- assert settings.working_dir == str(target_dir.resolve())
1000
- assert settings.assistant.write_dirs == [str((target_dir / "out").resolve())]
1001
- assert result["settings"]["assistant"]["write_dirs"] == [
1002
- str((target_dir / "out").resolve())
1003
- ]
1004
- assert saved == [settings]
1005
-
1006
-
1007
- def test_update_settings_rejects_blank_working_dir(monkeypatch):
1008
- settings = Settings(
1009
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
1010
- )
1011
-
1012
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
1013
-
1014
- with pytest.raises(HTTPException) as excinfo:
1015
- asyncio.run(
1016
- update_settings(
1017
- UpdateSettingsRequest(working_dir=" "),
1018
- )
1019
- )
1020
-
1021
- assert excinfo.value.status_code == 400
1022
- assert excinfo.value.detail == "working_dir must not be empty"
1023
-
1024
-
1025
- def test_update_settings_rejects_missing_working_dir(monkeypatch):
1026
- settings = Settings(
1027
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
1028
- )
1029
-
1030
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
1031
-
1032
- with pytest.raises(HTTPException) as excinfo:
1033
- asyncio.run(
1034
- update_settings(
1035
- UpdateSettingsRequest(
1036
- working_dir="/definitely/missing/flowent-working-dir"
1037
- ),
1038
- )
1039
- )
1040
-
1041
- assert excinfo.value.status_code == 400
1042
- assert excinfo.value.detail == "working_dir must be an existing directory"
1043
-
1044
-
1045
- def test_update_settings_persists_leader_role(monkeypatch):
1046
- settings = Settings(
1047
- roles=[
1048
- RoleConfig(name="Conductor", system_prompt="Default leader role."),
1049
- RoleConfig(name="Reviewer", system_prompt="Review carefully."),
1050
- ]
1051
- )
1052
- saved: list[Settings] = []
1053
-
1054
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
1055
- monkeypatch.setattr(
1056
- "flowent.routes.settings.save_settings",
1057
- lambda current: saved.append(current),
1058
- )
1059
- monkeypatch.setattr(
1060
- "flowent.providers.gateway.gateway.invalidate_cache", lambda: None
1061
- )
1062
-
1063
- result = asyncio.run(
1064
- update_settings(
1065
- UpdateSettingsRequest(leader={"role_name": "Reviewer"}),
1066
- )
1067
- )
1068
-
1069
- assert settings.leader.role_name == "Reviewer"
1070
- assert result["settings"]["leader"] == {"role_name": "Reviewer"}
1071
- assert saved == [settings]
1072
-
1073
-
1074
- def test_update_settings_rejects_unknown_assistant_role(monkeypatch):
1075
- settings = Settings(
1076
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
1077
- )
1078
-
1079
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
1080
-
1081
- with pytest.raises(HTTPException) as excinfo:
1082
- asyncio.run(
1083
- update_settings(
1084
- UpdateSettingsRequest(assistant={"role_name": "Ghost"}),
1085
- )
1086
- )
1087
-
1088
- assert excinfo.value.status_code == 400
1089
- assert excinfo.value.detail == "Role 'Ghost' not found"
1090
-
1091
-
1092
- def test_update_settings_rejects_invalid_assistant_allow_network(monkeypatch):
1093
- settings = Settings(
1094
- roles=[RoleConfig(name="Steward", system_prompt="Default assistant role.")]
1095
- )
1096
-
1097
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
1098
-
1099
- with pytest.raises(HTTPException) as excinfo:
1100
- asyncio.run(
1101
- update_settings(
1102
- UpdateSettingsRequest(assistant={"allow_network": "yes"}),
1103
- )
1104
- )
1105
-
1106
- assert excinfo.value.status_code == 400
1107
- assert excinfo.value.detail == "assistant.allow_network must be a boolean"
1108
-
1109
-
1110
- def test_update_settings_rejects_unknown_leader_role(monkeypatch):
1111
- settings = Settings(roles=[RoleConfig(name="Conductor", system_prompt="Default.")])
1112
-
1113
- monkeypatch.setattr("flowent.routes.settings.get_settings", lambda: settings)
1114
-
1115
- with pytest.raises(HTTPException) as excinfo:
1116
- asyncio.run(
1117
- update_settings(
1118
- UpdateSettingsRequest(leader={"role_name": "Ghost"}),
1119
- )
1120
- )
1121
-
1122
- assert excinfo.value.status_code == 400
1123
- assert excinfo.value.detail == "Role 'Ghost' not found"