flowent 0.0.7 → 0.0.10

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 (386) 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/__pycache__/__init__.cpython-313.pyc +0 -0
  5. package/backend/src/flowent/__pycache__/agent.cpython-313.pyc +0 -0
  6. package/backend/src/flowent/__pycache__/cli.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 +364 -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 +449 -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__/config.cpython-313.pyc +0 -0
  59. package/backend/src/flowent/__pycache__/events.cpython-313.pyc +0 -0
  60. package/backend/src/flowent/__pycache__/graph_runtime.cpython-313.pyc +0 -0
  61. package/backend/src/flowent/__pycache__/graph_service.cpython-313.pyc +0 -0
  62. package/backend/src/flowent/__pycache__/image_assets.cpython-313.pyc +0 -0
  63. package/backend/src/flowent/__pycache__/model_metadata.cpython-313.pyc +0 -0
  64. package/backend/src/flowent/__pycache__/network.cpython-313.pyc +0 -0
  65. package/backend/src/flowent/__pycache__/observability_service.cpython-313.pyc +0 -0
  66. package/backend/src/flowent/__pycache__/registry.cpython-313.pyc +0 -0
  67. package/backend/src/flowent/__pycache__/role_management.cpython-313.pyc +0 -0
  68. package/backend/src/flowent/__pycache__/runtime.cpython-313.pyc +0 -0
  69. package/backend/src/flowent/__pycache__/security.cpython-313.pyc +0 -0
  70. package/backend/src/flowent/__pycache__/settings.cpython-313.pyc +0 -0
  71. package/backend/src/flowent/__pycache__/settings_management.cpython-313.pyc +0 -0
  72. package/backend/src/flowent/__pycache__/state_db.cpython-313.pyc +0 -0
  73. package/backend/src/flowent/__pycache__/workspace_store.cpython-313.pyc +0 -0
  74. package/backend/src/flowent/access.py +0 -247
  75. package/backend/src/flowent/assistant_commands.py +0 -115
  76. package/backend/src/flowent/channels/__init__.py +0 -3
  77. package/backend/src/flowent/channels/__pycache__/__init__.cpython-313.pyc +0 -0
  78. package/backend/src/flowent/channels/__pycache__/telegram.cpython-313.pyc +0 -0
  79. package/backend/src/flowent/channels/telegram.py +0 -615
  80. package/backend/src/flowent/config.py +0 -14
  81. package/backend/src/flowent/dev.py +0 -3
  82. package/backend/src/flowent/events.py +0 -157
  83. package/backend/src/flowent/graph_runtime.py +0 -60
  84. package/backend/src/flowent/graph_service.py +0 -2401
  85. package/backend/src/flowent/image_assets.py +0 -356
  86. package/backend/src/flowent/model_metadata.py +0 -102
  87. package/backend/src/flowent/models/__init__.py +0 -125
  88. package/backend/src/flowent/models/__pycache__/__init__.cpython-313.pyc +0 -0
  89. package/backend/src/flowent/models/__pycache__/agent.cpython-313.pyc +0 -0
  90. package/backend/src/flowent/models/__pycache__/base.cpython-313.pyc +0 -0
  91. package/backend/src/flowent/models/__pycache__/blueprint.cpython-313.pyc +0 -0
  92. package/backend/src/flowent/models/__pycache__/content.cpython-313.pyc +0 -0
  93. package/backend/src/flowent/models/__pycache__/delta.cpython-313.pyc +0 -0
  94. package/backend/src/flowent/models/__pycache__/event.cpython-313.pyc +0 -0
  95. package/backend/src/flowent/models/__pycache__/graph.cpython-313.pyc +0 -0
  96. package/backend/src/flowent/models/__pycache__/history.cpython-313.pyc +0 -0
  97. package/backend/src/flowent/models/__pycache__/llm.cpython-313.pyc +0 -0
  98. package/backend/src/flowent/models/__pycache__/message.cpython-313.pyc +0 -0
  99. package/backend/src/flowent/models/__pycache__/tab.cpython-313.pyc +0 -0
  100. package/backend/src/flowent/models/__pycache__/todo.cpython-313.pyc +0 -0
  101. package/backend/src/flowent/models/agent.py +0 -34
  102. package/backend/src/flowent/models/base.py +0 -24
  103. package/backend/src/flowent/models/blueprint.py +0 -176
  104. package/backend/src/flowent/models/content.py +0 -164
  105. package/backend/src/flowent/models/delta.py +0 -44
  106. package/backend/src/flowent/models/event.py +0 -51
  107. package/backend/src/flowent/models/graph.py +0 -472
  108. package/backend/src/flowent/models/history.py +0 -272
  109. package/backend/src/flowent/models/llm.py +0 -62
  110. package/backend/src/flowent/models/message.py +0 -33
  111. package/backend/src/flowent/models/tab.py +0 -85
  112. package/backend/src/flowent/models/todo.py +0 -10
  113. package/backend/src/flowent/network.py +0 -146
  114. package/backend/src/flowent/observability_service.py +0 -218
  115. package/backend/src/flowent/prompts/__init__.py +0 -67
  116. package/backend/src/flowent/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
  117. package/backend/src/flowent/prompts/__pycache__/common.cpython-313.pyc +0 -0
  118. package/backend/src/flowent/prompts/__pycache__/steward.cpython-313.pyc +0 -0
  119. package/backend/src/flowent/prompts/common.py +0 -250
  120. package/backend/src/flowent/prompts/steward.py +0 -64
  121. package/backend/src/flowent/providers/__init__.py +0 -23
  122. package/backend/src/flowent/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  123. package/backend/src/flowent/providers/__pycache__/anthropic.cpython-313.pyc +0 -0
  124. package/backend/src/flowent/providers/__pycache__/base_url.cpython-313.pyc +0 -0
  125. package/backend/src/flowent/providers/__pycache__/configuration.cpython-313.pyc +0 -0
  126. package/backend/src/flowent/providers/__pycache__/content.cpython-313.pyc +0 -0
  127. package/backend/src/flowent/providers/__pycache__/errors.cpython-313.pyc +0 -0
  128. package/backend/src/flowent/providers/__pycache__/gateway.cpython-313.pyc +0 -0
  129. package/backend/src/flowent/providers/__pycache__/headers.cpython-313.pyc +0 -0
  130. package/backend/src/flowent/providers/__pycache__/management.cpython-313.pyc +0 -0
  131. package/backend/src/flowent/providers/__pycache__/openai.cpython-313.pyc +0 -0
  132. package/backend/src/flowent/providers/__pycache__/openai_responses.cpython-313.pyc +0 -0
  133. package/backend/src/flowent/providers/__pycache__/registry.cpython-313.pyc +0 -0
  134. package/backend/src/flowent/providers/__pycache__/sse.cpython-313.pyc +0 -0
  135. package/backend/src/flowent/providers/__pycache__/thinking.cpython-313.pyc +0 -0
  136. package/backend/src/flowent/providers/anthropic.py +0 -468
  137. package/backend/src/flowent/providers/base_url.py +0 -60
  138. package/backend/src/flowent/providers/configuration.py +0 -189
  139. package/backend/src/flowent/providers/content.py +0 -122
  140. package/backend/src/flowent/providers/errors.py +0 -223
  141. package/backend/src/flowent/providers/gateway.py +0 -169
  142. package/backend/src/flowent/providers/gemini.py +0 -447
  143. package/backend/src/flowent/providers/headers.py +0 -20
  144. package/backend/src/flowent/providers/management.py +0 -96
  145. package/backend/src/flowent/providers/ollama.py +0 -293
  146. package/backend/src/flowent/providers/openai.py +0 -422
  147. package/backend/src/flowent/providers/openai_responses.py +0 -655
  148. package/backend/src/flowent/providers/registry.py +0 -144
  149. package/backend/src/flowent/providers/sse.py +0 -31
  150. package/backend/src/flowent/providers/thinking.py +0 -79
  151. package/backend/src/flowent/registry.py +0 -73
  152. package/backend/src/flowent/role_management.py +0 -270
  153. package/backend/src/flowent/routes/__init__.py +0 -26
  154. package/backend/src/flowent/routes/__pycache__/__init__.cpython-313.pyc +0 -0
  155. package/backend/src/flowent/routes/__pycache__/access.cpython-313.pyc +0 -0
  156. package/backend/src/flowent/routes/__pycache__/assistant.cpython-313.pyc +0 -0
  157. package/backend/src/flowent/routes/__pycache__/image_assets.cpython-313.pyc +0 -0
  158. package/backend/src/flowent/routes/__pycache__/meta.cpython-313.pyc +0 -0
  159. package/backend/src/flowent/routes/__pycache__/nodes.cpython-313.pyc +0 -0
  160. package/backend/src/flowent/routes/__pycache__/prompts.cpython-313.pyc +0 -0
  161. package/backend/src/flowent/routes/__pycache__/providers_route.cpython-313.pyc +0 -0
  162. package/backend/src/flowent/routes/__pycache__/roles.cpython-313.pyc +0 -0
  163. package/backend/src/flowent/routes/__pycache__/settings.cpython-313.pyc +0 -0
  164. package/backend/src/flowent/routes/__pycache__/tabs.cpython-313.pyc +0 -0
  165. package/backend/src/flowent/routes/__pycache__/ws.cpython-313.pyc +0 -0
  166. package/backend/src/flowent/routes/access.py +0 -48
  167. package/backend/src/flowent/routes/assistant.py +0 -158
  168. package/backend/src/flowent/routes/image_assets.py +0 -33
  169. package/backend/src/flowent/routes/meta.py +0 -28
  170. package/backend/src/flowent/routes/nodes.py +0 -423
  171. package/backend/src/flowent/routes/prompts.py +0 -46
  172. package/backend/src/flowent/routes/providers_route.py +0 -365
  173. package/backend/src/flowent/routes/roles.py +0 -207
  174. package/backend/src/flowent/routes/settings.py +0 -379
  175. package/backend/src/flowent/routes/tabs.py +0 -298
  176. package/backend/src/flowent/routes/ws.py +0 -33
  177. package/backend/src/flowent/runtime.py +0 -160
  178. package/backend/src/flowent/security.py +0 -37
  179. package/backend/src/flowent/settings.py +0 -2112
  180. package/backend/src/flowent/settings_management.py +0 -394
  181. package/backend/src/flowent/state_db.py +0 -108
  182. package/backend/src/flowent/static/assets/AssistantPage-BW7XAd9I.js +0 -1
  183. package/backend/src/flowent/static/assets/ChannelsPage-tCJHgt6m.js +0 -1
  184. package/backend/src/flowent/static/assets/PageScaffold-f6g2l7XN.js +0 -1
  185. package/backend/src/flowent/static/assets/PromptsPage-C3Sxn2D7.js +0 -1
  186. package/backend/src/flowent/static/assets/ProvidersPage-BfmdXmNt.js +0 -3
  187. package/backend/src/flowent/static/assets/RolesPage-DET8wO4r.js +0 -1
  188. package/backend/src/flowent/static/assets/SettingsPage-D-g3deMm.js +0 -3
  189. package/backend/src/flowent/static/assets/ToolsPage-CDmtE2g4.js +0 -1
  190. package/backend/src/flowent/static/assets/WorkspacePage-AZsJ0sD0.js +0 -3
  191. package/backend/src/flowent/static/assets/WorkspacePanels-CteCjolX.js +0 -1
  192. package/backend/src/flowent/static/assets/alert-dialog-Duorp_S-.js +0 -1
  193. package/backend/src/flowent/static/assets/dialog-C3ixjGjN.js +0 -1
  194. package/backend/src/flowent/static/assets/elk-worker.min-C9JGDOE-.js +0 -6312
  195. package/backend/src/flowent/static/assets/graph-vendor-CHpVij2M.css +0 -1
  196. package/backend/src/flowent/static/assets/graph-vendor-DRq_-6fV.js +0 -7
  197. package/backend/src/flowent/static/assets/index--o_0fv0N.css +0 -1
  198. package/backend/src/flowent/static/assets/index-C9HuekJm.js +0 -10
  199. package/backend/src/flowent/static/assets/layout.worker-jMHqAFbP.js +0 -24
  200. package/backend/src/flowent/static/assets/markdown-vendor-C9RtvaJh.js +0 -29
  201. package/backend/src/flowent/static/assets/modelParams-DmnF2hwR.js +0 -1
  202. package/backend/src/flowent/static/assets/providerTypes-DT3Ahwl_.js +0 -1
  203. package/backend/src/flowent/static/assets/react-vendor-mEs_JJxa.js +0 -9
  204. package/backend/src/flowent/static/assets/roles-CuRT_chR.js +0 -1
  205. package/backend/src/flowent/static/assets/rolldown-runtime-BYbx6iT9.js +0 -1
  206. package/backend/src/flowent/static/assets/select-DCfeNu-F.js +0 -1
  207. package/backend/src/flowent/static/assets/surface-pWwG5ogx.js +0 -1
  208. package/backend/src/flowent/static/assets/ui-vendor-C5pJa8N7.js +0 -51
  209. package/backend/src/flowent/static/assets/useAppRoute-FgSHBKhV.js +0 -1
  210. package/backend/src/flowent/static/favicon.svg +0 -4
  211. package/backend/src/flowent/tools/__init__.py +0 -176
  212. package/backend/src/flowent/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  213. package/backend/src/flowent/tools/__pycache__/connect.cpython-313.pyc +0 -0
  214. package/backend/src/flowent/tools/__pycache__/contacts.cpython-313.pyc +0 -0
  215. package/backend/src/flowent/tools/__pycache__/create_agent.cpython-313.pyc +0 -0
  216. package/backend/src/flowent/tools/__pycache__/create_tab.cpython-313.pyc +0 -0
  217. package/backend/src/flowent/tools/__pycache__/delete_tab.cpython-313.pyc +0 -0
  218. package/backend/src/flowent/tools/__pycache__/edit.cpython-313.pyc +0 -0
  219. package/backend/src/flowent/tools/__pycache__/exec.cpython-313.pyc +0 -0
  220. package/backend/src/flowent/tools/__pycache__/fetch.cpython-313.pyc +0 -0
  221. package/backend/src/flowent/tools/__pycache__/idle.cpython-313.pyc +0 -0
  222. package/backend/src/flowent/tools/__pycache__/list_roles.cpython-313.pyc +0 -0
  223. package/backend/src/flowent/tools/__pycache__/list_tabs.cpython-313.pyc +0 -0
  224. package/backend/src/flowent/tools/__pycache__/list_tools.cpython-313.pyc +0 -0
  225. package/backend/src/flowent/tools/__pycache__/manage_prompts.cpython-313.pyc +0 -0
  226. package/backend/src/flowent/tools/__pycache__/manage_providers.cpython-313.pyc +0 -0
  227. package/backend/src/flowent/tools/__pycache__/manage_roles.cpython-313.pyc +0 -0
  228. package/backend/src/flowent/tools/__pycache__/manage_settings.cpython-313.pyc +0 -0
  229. package/backend/src/flowent/tools/__pycache__/read.cpython-313.pyc +0 -0
  230. package/backend/src/flowent/tools/__pycache__/send.cpython-313.pyc +0 -0
  231. package/backend/src/flowent/tools/__pycache__/set_permissions.cpython-313.pyc +0 -0
  232. package/backend/src/flowent/tools/__pycache__/sleep.cpython-313.pyc +0 -0
  233. package/backend/src/flowent/tools/__pycache__/todo.cpython-313.pyc +0 -0
  234. package/backend/src/flowent/tools/connect.py +0 -100
  235. package/backend/src/flowent/tools/contacts.py +0 -22
  236. package/backend/src/flowent/tools/create_agent.py +0 -191
  237. package/backend/src/flowent/tools/create_tab.py +0 -61
  238. package/backend/src/flowent/tools/delete_tab.py +0 -39
  239. package/backend/src/flowent/tools/edit.py +0 -142
  240. package/backend/src/flowent/tools/exec.py +0 -118
  241. package/backend/src/flowent/tools/fetch.py +0 -85
  242. package/backend/src/flowent/tools/idle.py +0 -27
  243. package/backend/src/flowent/tools/list_roles.py +0 -68
  244. package/backend/src/flowent/tools/list_tabs.py +0 -100
  245. package/backend/src/flowent/tools/list_tools.py +0 -28
  246. package/backend/src/flowent/tools/manage_prompts.py +0 -102
  247. package/backend/src/flowent/tools/manage_providers.py +0 -220
  248. package/backend/src/flowent/tools/manage_roles.py +0 -275
  249. package/backend/src/flowent/tools/manage_settings.py +0 -326
  250. package/backend/src/flowent/tools/read.py +0 -152
  251. package/backend/src/flowent/tools/send.py +0 -68
  252. package/backend/src/flowent/tools/set_permissions.py +0 -99
  253. package/backend/src/flowent/tools/sleep.py +0 -41
  254. package/backend/src/flowent/tools/todo.py +0 -51
  255. package/backend/src/flowent/workspace_store.py +0 -479
  256. package/backend/tests/__init__.py +0 -0
  257. package/backend/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  258. package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  259. package/backend/tests/conftest.py +0 -6
  260. package/backend/tests/integration/api/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  261. package/backend/tests/integration/api/__pycache__/test_access_api.cpython-313-pytest-9.0.3.pyc +0 -0
  262. package/backend/tests/integration/api/__pycache__/test_assistant_api.cpython-313-pytest-9.0.3.pyc +0 -0
  263. package/backend/tests/integration/api/__pycache__/test_frontend_mounting.cpython-313-pytest-9.0.3.pyc +0 -0
  264. package/backend/tests/integration/api/__pycache__/test_meta_api.cpython-313-pytest-9.0.3.pyc +0 -0
  265. package/backend/tests/integration/api/__pycache__/test_nodes_api.cpython-313-pytest-9.0.3.pyc +0 -0
  266. package/backend/tests/integration/api/__pycache__/test_prompts_api.cpython-313-pytest-9.0.3.pyc +0 -0
  267. package/backend/tests/integration/api/__pycache__/test_roles_api.cpython-313-pytest-9.0.3.pyc +0 -0
  268. package/backend/tests/integration/api/__pycache__/test_tabs_api.cpython-313-pytest-9.0.3.pyc +0 -0
  269. package/backend/tests/integration/api/conftest.py +0 -29
  270. package/backend/tests/integration/api/test_access_api.py +0 -182
  271. package/backend/tests/integration/api/test_assistant_api.py +0 -422
  272. package/backend/tests/integration/api/test_frontend_mounting.py +0 -61
  273. package/backend/tests/integration/api/test_meta_api.py +0 -32
  274. package/backend/tests/integration/api/test_nodes_api.py +0 -787
  275. package/backend/tests/integration/api/test_prompts_api.py +0 -47
  276. package/backend/tests/integration/api/test_roles_api.py +0 -228
  277. package/backend/tests/integration/api/test_tabs_api.py +0 -688
  278. package/backend/tests/unit/__pycache__/test_access.cpython-313-pytest-9.0.3.pyc +0 -0
  279. package/backend/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  280. package/backend/tests/unit/__pycache__/test_graph_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  281. package/backend/tests/unit/__pycache__/test_network.cpython-313-pytest-9.0.3.pyc +0 -0
  282. package/backend/tests/unit/__pycache__/test_state_sqlite_storage.cpython-313-pytest-9.0.3.pyc +0 -0
  283. package/backend/tests/unit/__pycache__/test_workspace_store.cpython-313-pytest-9.0.3.pyc +0 -0
  284. package/backend/tests/unit/agent/__pycache__/test_agent_public_api.cpython-313-pytest-9.0.3.pyc +0 -0
  285. package/backend/tests/unit/agent/__pycache__/test_agent_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  286. package/backend/tests/unit/agent/test_agent_public_api.py +0 -822
  287. package/backend/tests/unit/agent/test_agent_runtime.py +0 -3088
  288. package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
  289. package/backend/tests/unit/channels/test_telegram_channel.py +0 -552
  290. package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
  291. package/backend/tests/unit/logging/test_logging.py +0 -132
  292. package/backend/tests/unit/prompts/__pycache__/test_prompts.cpython-313-pytest-9.0.3.pyc +0 -0
  293. package/backend/tests/unit/prompts/test_prompts.py +0 -570
  294. package/backend/tests/unit/providers/__pycache__/test_anthropic_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  295. package/backend/tests/unit/providers/__pycache__/test_errors.cpython-313-pytest-9.0.3.pyc +0 -0
  296. package/backend/tests/unit/providers/__pycache__/test_extract_delta_parts.cpython-313-pytest-9.0.3.pyc +0 -0
  297. package/backend/tests/unit/providers/__pycache__/test_openai_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  298. package/backend/tests/unit/providers/__pycache__/test_openai_responses.cpython-313-pytest-9.0.3.pyc +0 -0
  299. package/backend/tests/unit/providers/__pycache__/test_provider_gateway.cpython-313-pytest-9.0.3.pyc +0 -0
  300. package/backend/tests/unit/providers/__pycache__/test_think_tag_parser.cpython-313-pytest-9.0.3.pyc +0 -0
  301. package/backend/tests/unit/providers/test_anthropic_provider.py +0 -185
  302. package/backend/tests/unit/providers/test_errors.py +0 -68
  303. package/backend/tests/unit/providers/test_extract_delta_parts.py +0 -22
  304. package/backend/tests/unit/providers/test_openai_provider.py +0 -139
  305. package/backend/tests/unit/providers/test_openai_responses.py +0 -402
  306. package/backend/tests/unit/providers/test_provider_gateway.py +0 -359
  307. package/backend/tests/unit/providers/test_think_tag_parser.py +0 -36
  308. package/backend/tests/unit/routes/__pycache__/test_prompts_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  309. package/backend/tests/unit/routes/__pycache__/test_providers_route.cpython-313-pytest-9.0.3.pyc +0 -0
  310. package/backend/tests/unit/routes/__pycache__/test_roles_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  311. package/backend/tests/unit/routes/__pycache__/test_settings_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  312. package/backend/tests/unit/routes/test_prompts_routes.py +0 -82
  313. package/backend/tests/unit/routes/test_providers_route.py +0 -370
  314. package/backend/tests/unit/routes/test_roles_routes.py +0 -539
  315. package/backend/tests/unit/routes/test_settings_routes.py +0 -1123
  316. package/backend/tests/unit/runtime/__pycache__/test_bootstrap_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  317. package/backend/tests/unit/runtime/test_bootstrap_runtime.py +0 -1002
  318. package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
  319. package/backend/tests/unit/sandbox/test_sandbox_tools.py +0 -78
  320. package/backend/tests/unit/security/__pycache__/test_security.cpython-313-pytest-9.0.3.pyc +0 -0
  321. package/backend/tests/unit/security/test_security.py +0 -124
  322. package/backend/tests/unit/settings/__pycache__/test_settings_roles.cpython-313-pytest-9.0.3.pyc +0 -0
  323. package/backend/tests/unit/settings/test_settings_roles.py +0 -703
  324. package/backend/tests/unit/test_access.py +0 -45
  325. package/backend/tests/unit/test_cli.py +0 -102
  326. package/backend/tests/unit/test_graph_runtime.py +0 -72
  327. package/backend/tests/unit/test_network.py +0 -51
  328. package/backend/tests/unit/test_state_sqlite_storage.py +0 -87
  329. package/backend/tests/unit/test_workspace_store.py +0 -228
  330. package/backend/tests/unit/tools/__pycache__/test_connect_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  331. package/backend/tests/unit/tools/__pycache__/test_create_agent_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  332. package/backend/tests/unit/tools/__pycache__/test_delete_tab_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  333. package/backend/tests/unit/tools/__pycache__/test_edit_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  334. package/backend/tests/unit/tools/__pycache__/test_exec_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  335. package/backend/tests/unit/tools/__pycache__/test_fetch_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  336. package/backend/tests/unit/tools/__pycache__/test_manage_prompts_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  337. package/backend/tests/unit/tools/__pycache__/test_manage_providers_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  338. package/backend/tests/unit/tools/__pycache__/test_manage_roles_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  339. package/backend/tests/unit/tools/__pycache__/test_manage_settings_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  340. package/backend/tests/unit/tools/__pycache__/test_read_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  341. package/backend/tests/unit/tools/__pycache__/test_set_permissions_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  342. package/backend/tests/unit/tools/__pycache__/test_todo_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  343. package/backend/tests/unit/tools/__pycache__/test_tool_registry.cpython-313-pytest-9.0.3.pyc +0 -0
  344. package/backend/tests/unit/tools/test_connect_tool.py +0 -228
  345. package/backend/tests/unit/tools/test_create_agent_tool.py +0 -404
  346. package/backend/tests/unit/tools/test_delete_tab_tool.py +0 -116
  347. package/backend/tests/unit/tools/test_edit_tool.py +0 -115
  348. package/backend/tests/unit/tools/test_exec_tool.py +0 -81
  349. package/backend/tests/unit/tools/test_fetch_tool.py +0 -65
  350. package/backend/tests/unit/tools/test_manage_prompts_tool.py +0 -92
  351. package/backend/tests/unit/tools/test_manage_providers_tool.py +0 -460
  352. package/backend/tests/unit/tools/test_manage_roles_tool.py +0 -411
  353. package/backend/tests/unit/tools/test_manage_settings_tool.py +0 -611
  354. package/backend/tests/unit/tools/test_read_tool.py +0 -33
  355. package/backend/tests/unit/tools/test_set_permissions_tool.py +0 -595
  356. package/backend/tests/unit/tools/test_todo_tool.py +0 -37
  357. package/backend/tests/unit/tools/test_tool_registry.py +0 -199
  358. package/dist/frontend/assets/AssistantPage-BW7XAd9I.js +0 -1
  359. package/dist/frontend/assets/ChannelsPage-tCJHgt6m.js +0 -1
  360. package/dist/frontend/assets/PageScaffold-f6g2l7XN.js +0 -1
  361. package/dist/frontend/assets/PromptsPage-C3Sxn2D7.js +0 -1
  362. package/dist/frontend/assets/ProvidersPage-BfmdXmNt.js +0 -3
  363. package/dist/frontend/assets/RolesPage-DET8wO4r.js +0 -1
  364. package/dist/frontend/assets/SettingsPage-D-g3deMm.js +0 -3
  365. package/dist/frontend/assets/ToolsPage-CDmtE2g4.js +0 -1
  366. package/dist/frontend/assets/WorkspacePage-AZsJ0sD0.js +0 -3
  367. package/dist/frontend/assets/WorkspacePanels-CteCjolX.js +0 -1
  368. package/dist/frontend/assets/alert-dialog-Duorp_S-.js +0 -1
  369. package/dist/frontend/assets/dialog-C3ixjGjN.js +0 -1
  370. package/dist/frontend/assets/elk-worker.min-C9JGDOE-.js +0 -6312
  371. package/dist/frontend/assets/graph-vendor-CHpVij2M.css +0 -1
  372. package/dist/frontend/assets/graph-vendor-DRq_-6fV.js +0 -7
  373. package/dist/frontend/assets/index--o_0fv0N.css +0 -1
  374. package/dist/frontend/assets/index-C9HuekJm.js +0 -10
  375. package/dist/frontend/assets/layout.worker-jMHqAFbP.js +0 -24
  376. package/dist/frontend/assets/markdown-vendor-C9RtvaJh.js +0 -29
  377. package/dist/frontend/assets/modelParams-DmnF2hwR.js +0 -1
  378. package/dist/frontend/assets/providerTypes-DT3Ahwl_.js +0 -1
  379. package/dist/frontend/assets/react-vendor-mEs_JJxa.js +0 -9
  380. package/dist/frontend/assets/roles-CuRT_chR.js +0 -1
  381. package/dist/frontend/assets/rolldown-runtime-BYbx6iT9.js +0 -1
  382. package/dist/frontend/assets/select-DCfeNu-F.js +0 -1
  383. package/dist/frontend/assets/surface-pWwG5ogx.js +0 -1
  384. package/dist/frontend/assets/ui-vendor-C5pJa8N7.js +0 -51
  385. package/dist/frontend/assets/useAppRoute-FgSHBKhV.js +0 -1
  386. package/dist/frontend/favicon.svg +0 -4
@@ -1,822 +0,0 @@
1
- import json
2
- import threading
3
-
4
- from flowent.agent import Agent
5
- from flowent.graph_service import build_workflow_node_definition, create_edge
6
- from flowent.models import (
7
- AgentState,
8
- Message,
9
- NodeConfig,
10
- NodeType,
11
- PortDirection,
12
- PortType,
13
- ReceivedMessage,
14
- Tab,
15
- TodoItem,
16
- WorkflowDefinition,
17
- WorkflowNodeKind,
18
- WorkflowPort,
19
- )
20
- from flowent.registry import registry
21
- from flowent.settings import RoleConfig, Settings
22
- from flowent.tools.contacts import ContactsTool
23
- from flowent.tools.idle import IdleTool
24
- from flowent.tools.list_roles import ListRolesTool
25
- from flowent.tools.list_tabs import ListTabsTool
26
- from flowent.tools.list_tools import ListToolsTool
27
- from flowent.tools.sleep import SleepTool
28
- from flowent.tools.todo import TodoTool
29
- from flowent.workspace_store import workspace_store
30
-
31
-
32
- def test_idle_tool_uses_request_idle(monkeypatch):
33
- agent = Agent(NodeConfig(node_type=NodeType.AGENT, tools=["idle"]))
34
- called: list[str | None] = []
35
-
36
- def fake_request_idle(*, tool_call_id: str | None = None) -> str:
37
- called.append(tool_call_id)
38
- return "idle 1.25s"
39
-
40
- monkeypatch.setattr(agent, "request_idle", fake_request_idle)
41
-
42
- result = IdleTool().execute(agent, {})
43
-
44
- assert result == "idle 1.25s"
45
- assert called == [None]
46
-
47
-
48
- def test_idle_tool_blocks_until_message_and_returns_idle_duration():
49
- agent = Agent(NodeConfig(node_type=NodeType.AGENT, tools=["idle"]), uuid="agent-a")
50
- result: list[str] = []
51
-
52
- thread = threading.Thread(
53
- target=lambda: result.append(IdleTool().execute(agent, {})),
54
- daemon=True,
55
- )
56
- thread.start()
57
-
58
- agent.enqueue_message(
59
- Message(from_id="tester", to_id=agent.uuid, content="hello from queue")
60
- )
61
- thread.join(timeout=1.0)
62
-
63
- assert not thread.is_alive()
64
- assert len(result) == 1
65
- assert result[0].startswith("idle ")
66
- assert result[0].endswith("s")
67
- received_entries = [
68
- entry for entry in agent.history if isinstance(entry, ReceivedMessage)
69
- ]
70
- assert len(received_entries) == 1
71
- assert received_entries[0].from_id == "tester"
72
- assert received_entries[0].content == "hello from queue"
73
-
74
-
75
- def test_idle_tool_returns_immediately_when_runtime_notice_is_pending():
76
- agent = Agent(NodeConfig(node_type=NodeType.AGENT, tools=["idle"]), uuid="agent-a")
77
- agent.set_state(AgentState.RUNNING, "processing")
78
- agent._queue_runtime_notice("fix the previous routing mistake")
79
-
80
- result = IdleTool().execute(agent, {})
81
-
82
- assert result == ""
83
- assert agent.state == AgentState.RUNNING
84
- assert agent._build_messages()[-1] == {
85
- "role": "user",
86
- "content": "<system>fix the previous routing mistake</system>",
87
- }
88
-
89
-
90
- def test_sleep_tool_uses_request_sleep(monkeypatch):
91
- agent = Agent(NodeConfig(node_type=NodeType.AGENT, tools=["sleep"]))
92
- called: list[float] = []
93
-
94
- def fake_request_sleep(*, seconds: float) -> str:
95
- called.append(seconds)
96
- return "slept 0.25s"
97
-
98
- monkeypatch.setattr(agent, "request_sleep", fake_request_sleep)
99
-
100
- result = SleepTool().execute(agent, {"seconds": 0.25})
101
-
102
- assert result == "slept 0.25s"
103
- assert called == [0.25]
104
-
105
-
106
- def test_sleep_tool_rejects_negative_seconds():
107
- agent = Agent(NodeConfig(node_type=NodeType.AGENT, tools=["sleep"]))
108
-
109
- result = json.loads(SleepTool().execute(agent, {"seconds": -1}))
110
-
111
- assert result == {"error": "seconds must be a non-negative number"}
112
-
113
-
114
- def test_sleep_tool_waits_and_returns_duration():
115
- agent = Agent(NodeConfig(node_type=NodeType.AGENT, tools=["sleep"]))
116
-
117
- result = SleepTool().execute(agent, {"seconds": 0.01})
118
-
119
- assert result.startswith("slept ")
120
- assert result.endswith("s")
121
-
122
-
123
- def test_enqueue_message_enqueues_wake_signal_immediately():
124
- agent = Agent(NodeConfig(node_type=NodeType.AGENT), uuid="agent-a")
125
-
126
- agent.enqueue_message(
127
- Message(from_id="tester", to_id=agent.uuid, content="hello from queue")
128
- )
129
-
130
- signal = agent._wake_queue.get_nowait()
131
-
132
- assert signal.reason == "message"
133
- assert signal.payload == {
134
- "message": {
135
- "from": "tester",
136
- "content": "hello from queue",
137
- "parts": [{"type": "text", "text": "hello from queue"}],
138
- "history_recorded": False,
139
- }
140
- }
141
- assert signal.resume_reason == "received message from tester"
142
-
143
-
144
- def test_request_termination_enqueues_wake_signal_immediately():
145
- agent = Agent(NodeConfig(node_type=NodeType.AGENT), uuid="agent-a")
146
-
147
- agent.request_termination("stop")
148
-
149
- signal = agent._wake_queue.get_nowait()
150
-
151
- assert agent._terminate.is_set()
152
- assert agent._termination_reason == "stop"
153
- assert signal.reason == "termination"
154
- assert signal.payload == {}
155
- assert signal.resume_reason == "termination requested"
156
-
157
-
158
- def test_request_interrupt_marks_running_agent():
159
- agent = Agent(NodeConfig(node_type=NodeType.AGENT), uuid="agent-a")
160
- agent.set_state(AgentState.RUNNING, "processing")
161
-
162
- interrupted = agent.request_interrupt()
163
-
164
- assert interrupted is True
165
- assert agent._interrupt_requested.is_set()
166
-
167
-
168
- def test_request_interrupt_marks_sleeping_agent():
169
- agent = Agent(NodeConfig(node_type=NodeType.AGENT), uuid="agent-a")
170
- agent.set_state(AgentState.SLEEPING, "waiting for reply")
171
-
172
- interrupted = agent.request_interrupt()
173
-
174
- assert interrupted is True
175
- assert agent._interrupt_requested.is_set()
176
-
177
-
178
- def test_request_interrupt_ignores_idle_agent():
179
- agent = Agent(NodeConfig(node_type=NodeType.AGENT), uuid="agent-a")
180
-
181
- interrupted = agent.request_interrupt()
182
-
183
- assert interrupted is False
184
- assert not agent._interrupt_requested.is_set()
185
-
186
-
187
- def test_state_changes_update_current_state_without_history_entries():
188
- agent = Agent(NodeConfig(node_type=NodeType.AGENT), uuid="agent-a")
189
-
190
- agent.set_state(AgentState.RUNNING, "processing")
191
- agent.set_state(AgentState.IDLE, "completed")
192
-
193
- assert agent.state == AgentState.IDLE
194
- assert agent.get_history_snapshot() == []
195
-
196
-
197
- def test_contacts_tool_uses_agent_public_api(monkeypatch):
198
- agent = Agent(
199
- NodeConfig(node_type=NodeType.AGENT, tools=["contacts"]),
200
- uuid="agent-a",
201
- )
202
- expected = [
203
- {
204
- "id": "agent-b",
205
- "node_type": "agent",
206
- "role_name": "Worker",
207
- "name": "Worker",
208
- "state": "idle",
209
- }
210
- ]
211
-
212
- monkeypatch.setattr(agent, "get_contacts_info", lambda: expected)
213
-
214
- result = json.loads(ContactsTool().execute(agent, {}))
215
-
216
- assert result == {"contacts": expected}
217
-
218
-
219
- def test_agent_get_contacts_info_is_empty_without_output_paths():
220
- registry.reset()
221
- workspace_store.reset_cache()
222
- workspace_store.upsert_tab(
223
- Tab(
224
- id="tab-1",
225
- title="Task",
226
- leader_id="leader-a",
227
- definition=WorkflowDefinition(
228
- nodes=[
229
- build_workflow_node_definition(
230
- node_id="agent-a",
231
- node_kind=WorkflowNodeKind.AGENT,
232
- ),
233
- build_workflow_node_definition(
234
- node_id="agent-b",
235
- node_kind=WorkflowNodeKind.AGENT,
236
- ),
237
- ]
238
- ),
239
- )
240
- )
241
- leader = Agent(
242
- NodeConfig(
243
- node_type=NodeType.AGENT,
244
- role_name="Conductor",
245
- name="Leader",
246
- tab_id="tab-1",
247
- ),
248
- uuid="leader-a",
249
- )
250
- agent = Agent(
251
- NodeConfig(node_type=NodeType.AGENT, tab_id="tab-1"),
252
- uuid="agent-a",
253
- )
254
- assistant = Agent(
255
- NodeConfig(node_type=NodeType.ASSISTANT, role_name="Steward", name="Assistant"),
256
- uuid="assistant-a",
257
- )
258
- peer = Agent(
259
- NodeConfig(
260
- node_type=NodeType.AGENT,
261
- role_name="Worker",
262
- name="Worker",
263
- tab_id="tab-1",
264
- ),
265
- uuid="agent-b",
266
- )
267
- registry.register(leader)
268
- registry.register(agent)
269
- registry.register(assistant)
270
- registry.register(peer)
271
- agent.add_connection(peer.uuid)
272
-
273
- try:
274
- assert agent.get_contacts_info() == []
275
- finally:
276
- registry.reset()
277
- workspace_store.reset_cache()
278
-
279
-
280
- def test_agent_get_contacts_info_lists_output_port_paths():
281
- registry.reset()
282
- workspace_store.reset_cache()
283
- workspace_store.upsert_tab(
284
- Tab(
285
- id="tab-1",
286
- title="Task",
287
- leader_id="leader-a",
288
- definition=WorkflowDefinition(
289
- nodes=[
290
- build_workflow_node_definition(
291
- node_id="agent-a",
292
- node_kind=WorkflowNodeKind.AGENT,
293
- ),
294
- build_workflow_node_definition(
295
- node_id="agent-b",
296
- node_kind=WorkflowNodeKind.AGENT,
297
- config={"role_name": "Reviewer", "name": "Reviewer"},
298
- ),
299
- build_workflow_node_definition(
300
- node_id="code-a",
301
- node_kind=WorkflowNodeKind.CODE,
302
- config={
303
- "inputs": [
304
- {
305
- "key": "payload",
306
- "direction": "in",
307
- "type": "json",
308
- "required": True,
309
- }
310
- ],
311
- "outputs": [],
312
- },
313
- ),
314
- ]
315
- ),
316
- )
317
- )
318
- leader = Agent(
319
- NodeConfig(
320
- node_type=NodeType.AGENT,
321
- role_name="Conductor",
322
- name="Leader",
323
- tab_id="tab-1",
324
- ),
325
- uuid="leader-a",
326
- )
327
- agent = Agent(
328
- NodeConfig(node_type=NodeType.AGENT, tab_id="tab-1"),
329
- uuid="agent-a",
330
- )
331
- peer = Agent(
332
- NodeConfig(
333
- node_type=NodeType.AGENT,
334
- role_name="Reviewer",
335
- name="Reviewer",
336
- tab_id="tab-1",
337
- ),
338
- uuid="agent-b",
339
- )
340
- registry.register(leader)
341
- registry.register(agent)
342
- registry.register(peer)
343
- tab = workspace_store.get_tab("tab-1")
344
- assert tab is not None
345
- source = tab.definition.get_node("agent-a")
346
- assert source is not None
347
- source.outputs.append(
348
- WorkflowPort(
349
- key="json",
350
- direction=PortDirection.OUTPUT,
351
- type=PortType.JSON,
352
- multiple=True,
353
- )
354
- )
355
- workspace_store.upsert_tab(tab)
356
- peer_edge, error = create_edge(
357
- tab_id="tab-1",
358
- from_node_id="agent-a",
359
- to_node_id="agent-b",
360
- )
361
- assert error is None and peer_edge is not None
362
- code_edge, error = create_edge(
363
- tab_id="tab-1",
364
- from_node_id="agent-a",
365
- from_port_key="json",
366
- to_node_id="code-a",
367
- to_port_key="payload",
368
- )
369
- assert error is None and code_edge is not None
370
-
371
- try:
372
- assert agent.get_contacts_info() == [
373
- {
374
- "id": "agent-b",
375
- "target_id": "agent-b",
376
- "node_type": "agent",
377
- "role_name": "Reviewer",
378
- "name": "Reviewer",
379
- "state": "initializing",
380
- "is_leader": False,
381
- "from_output_port_key": "out",
382
- "to_input_port_key": "in",
383
- "port_type": "parts",
384
- "edge_id": peer_edge.id,
385
- },
386
- {
387
- "id": "code-a",
388
- "target_id": "code-a",
389
- "node_type": "code",
390
- "role_name": None,
391
- "name": None,
392
- "state": None,
393
- "is_leader": False,
394
- "from_output_port_key": "json",
395
- "to_input_port_key": "payload",
396
- "port_type": "json",
397
- "edge_id": code_edge.id,
398
- },
399
- ]
400
- finally:
401
- registry.reset()
402
- workspace_store.reset_cache()
403
-
404
-
405
- def test_agent_get_contacts_info_ignores_peer_only_connections():
406
- registry.reset()
407
- workspace_store.reset_cache()
408
- workspace_store.upsert_tab(Tab(id="tab-1", title="Task", leader_id="leader-a"))
409
- leader = Agent(
410
- NodeConfig(
411
- node_type=NodeType.AGENT,
412
- role_name="Conductor",
413
- name="Leader",
414
- tab_id="tab-1",
415
- ),
416
- uuid="leader-a",
417
- )
418
- agent = Agent(
419
- NodeConfig(node_type=NodeType.AGENT, tab_id="tab-1"),
420
- uuid="agent-a",
421
- )
422
- assistant = Agent(
423
- NodeConfig(node_type=NodeType.ASSISTANT, role_name="Steward", name="Assistant"),
424
- uuid="assistant-a",
425
- )
426
- peer = Agent(
427
- NodeConfig(
428
- node_type=NodeType.AGENT,
429
- role_name="Reviewer",
430
- name="Reviewer",
431
- tab_id="tab-1",
432
- ),
433
- uuid="agent-b",
434
- )
435
- registry.register(leader)
436
- registry.register(agent)
437
- registry.register(assistant)
438
- registry.register(peer)
439
- peer.add_connection(agent.uuid)
440
-
441
- try:
442
- assert agent.get_contacts_info() == []
443
- finally:
444
- registry.reset()
445
- workspace_store.reset_cache()
446
-
447
-
448
- def test_workflow_node_ref_resolves_unique_agent_role_name():
449
- from flowent.graph_service import resolve_workflow_node_ref
450
-
451
- registry.reset()
452
- workspace_store.reset_cache()
453
- workspace_store.upsert_tab(
454
- Tab(
455
- id="tab-1",
456
- title="Task",
457
- leader_id="leader-a",
458
- definition=WorkflowDefinition(
459
- nodes=[
460
- build_workflow_node_definition(
461
- node_id="agent-a",
462
- node_kind=WorkflowNodeKind.AGENT,
463
- config={"role_name": "Reviewer"},
464
- ),
465
- build_workflow_node_definition(
466
- node_id="agent-b",
467
- node_kind=WorkflowNodeKind.AGENT,
468
- config={"role_name": "Writer"},
469
- ),
470
- ]
471
- ),
472
- )
473
- )
474
-
475
- try:
476
- assert (
477
- resolve_workflow_node_ref(tab_id="tab-1", node_ref="Reviewer") == "agent-a"
478
- )
479
- finally:
480
- registry.reset()
481
- workspace_store.reset_cache()
482
-
483
-
484
- def test_leader_get_contacts_info_includes_assistant_and_all_tab_agents():
485
- registry.reset()
486
- workspace_store.reset_cache()
487
- workspace_store.upsert_tab(Tab(id="tab-1", title="Task", leader_id="leader-a"))
488
- assistant = Agent(
489
- NodeConfig(node_type=NodeType.ASSISTANT, role_name="Steward", name="Assistant"),
490
- uuid="assistant-a",
491
- )
492
- leader = Agent(
493
- NodeConfig(
494
- node_type=NodeType.AGENT,
495
- role_name="Conductor",
496
- name="Leader",
497
- tab_id="tab-1",
498
- ),
499
- uuid="leader-a",
500
- )
501
- outgoing_worker = Agent(
502
- NodeConfig(
503
- node_type=NodeType.AGENT,
504
- role_name="Worker",
505
- name="Writer",
506
- tab_id="tab-1",
507
- ),
508
- uuid="agent-b",
509
- )
510
- incoming_worker = Agent(
511
- NodeConfig(
512
- node_type=NodeType.AGENT,
513
- role_name="Reviewer",
514
- name="Reviewer",
515
- tab_id="tab-1",
516
- ),
517
- uuid="agent-c",
518
- )
519
- detached_worker = Agent(
520
- NodeConfig(
521
- node_type=NodeType.AGENT,
522
- role_name="Analyst",
523
- name="Analyst",
524
- tab_id="tab-1",
525
- ),
526
- uuid="agent-d",
527
- )
528
- registry.register(assistant)
529
- registry.register(leader)
530
- registry.register(outgoing_worker)
531
- registry.register(incoming_worker)
532
- registry.register(detached_worker)
533
- leader.add_connection(outgoing_worker.uuid)
534
- incoming_worker.add_connection(leader.uuid)
535
-
536
- try:
537
- assert leader.get_contacts_info() == [
538
- {
539
- "id": "assistant-a",
540
- "node_type": "assistant",
541
- "role_name": "Steward",
542
- "name": "Assistant",
543
- "state": "initializing",
544
- "is_leader": False,
545
- },
546
- {
547
- "id": "agent-b",
548
- "node_type": "agent",
549
- "role_name": "Worker",
550
- "name": "Writer",
551
- "state": "initializing",
552
- "is_leader": False,
553
- },
554
- {
555
- "id": "agent-c",
556
- "node_type": "agent",
557
- "role_name": "Reviewer",
558
- "name": "Reviewer",
559
- "state": "initializing",
560
- "is_leader": False,
561
- },
562
- {
563
- "id": "agent-d",
564
- "node_type": "agent",
565
- "role_name": "Analyst",
566
- "name": "Analyst",
567
- "state": "initializing",
568
- "is_leader": False,
569
- },
570
- ]
571
- finally:
572
- registry.reset()
573
- workspace_store.reset_cache()
574
-
575
-
576
- def test_assistant_get_contacts_info_lists_registered_tab_leaders():
577
- registry.reset()
578
- workspace_store.reset_cache()
579
- workspace_store.upsert_tab(Tab(id="tab-1", title="Task", leader_id="leader-a"))
580
- assistant = Agent(
581
- NodeConfig(node_type=NodeType.ASSISTANT, role_name="Steward", name="Assistant"),
582
- uuid="assistant-a",
583
- )
584
- leader = Agent(
585
- NodeConfig(
586
- node_type=NodeType.AGENT,
587
- role_name="Conductor",
588
- name="Leader",
589
- tab_id="tab-1",
590
- ),
591
- uuid="leader-a",
592
- )
593
- worker = Agent(
594
- NodeConfig(
595
- node_type=NodeType.AGENT,
596
- role_name="Worker",
597
- name="Worker",
598
- tab_id="tab-1",
599
- ),
600
- uuid="agent-b",
601
- )
602
- registry.register(assistant)
603
- registry.register(leader)
604
- registry.register(worker)
605
-
606
- try:
607
- assert assistant.get_contacts_info() == [
608
- {
609
- "id": "leader-a",
610
- "node_type": "agent",
611
- "role_name": "Conductor",
612
- "name": "Leader",
613
- "state": "initializing",
614
- "is_leader": True,
615
- }
616
- ]
617
- finally:
618
- registry.reset()
619
- workspace_store.reset_cache()
620
-
621
-
622
- def test_list_roles_tool_returns_registered_roles(monkeypatch):
623
- agent = Agent(
624
- NodeConfig(
625
- node_type=NodeType.AGENT,
626
- role_name="Conductor",
627
- tools=["list_roles"],
628
- )
629
- )
630
-
631
- monkeypatch.setattr(
632
- "flowent.settings.get_settings",
633
- lambda: Settings(
634
- roles=[
635
- RoleConfig(
636
- name="Worker",
637
- description="General execution role",
638
- system_prompt="Do work.",
639
- included_tools=["read", "exec"],
640
- ),
641
- RoleConfig(
642
- name="Reviewer",
643
- description="Review code carefully",
644
- system_prompt="Review code.",
645
- excluded_tools=["fetch"],
646
- ),
647
- ]
648
- ),
649
- )
650
-
651
- result = json.loads(ListRolesTool().execute(agent, {}))
652
-
653
- assert result == [
654
- {
655
- "name": "Worker",
656
- "description": "General execution role",
657
- "system_prompt": "Do work.",
658
- "builtin_tools": [
659
- "idle",
660
- "sleep",
661
- "todo",
662
- "contacts",
663
- "send",
664
- "read",
665
- "exec",
666
- ],
667
- "optional_tools": [
668
- "set_permissions",
669
- "create_agent",
670
- "connect",
671
- "edit",
672
- "fetch",
673
- "list_roles",
674
- "list_tools",
675
- ],
676
- },
677
- {
678
- "name": "Reviewer",
679
- "description": "Review code carefully",
680
- "system_prompt": "Review code.",
681
- "builtin_tools": [
682
- "idle",
683
- "sleep",
684
- "todo",
685
- "contacts",
686
- "send",
687
- ],
688
- "optional_tools": [
689
- "set_permissions",
690
- "create_agent",
691
- "connect",
692
- "read",
693
- "edit",
694
- "exec",
695
- "list_roles",
696
- "list_tools",
697
- ],
698
- },
699
- ]
700
-
701
-
702
- def test_list_tools_tool_returns_registered_tool_names_and_descriptions():
703
- agent = Agent(
704
- NodeConfig(
705
- node_type=NodeType.AGENT,
706
- role_name="Conductor",
707
- tools=["list_tools"],
708
- )
709
- )
710
-
711
- result = json.loads(ListToolsTool().execute(agent, {}))
712
-
713
- assert {item["name"] for item in result} == {
714
- "idle",
715
- "sleep",
716
- "todo",
717
- "contacts",
718
- "send",
719
- "read",
720
- "edit",
721
- "exec",
722
- "fetch",
723
- "set_permissions",
724
- "create_agent",
725
- "connect",
726
- "list_roles",
727
- "list_tools",
728
- }
729
- assert all(
730
- {"name", "description", "source", "parameters"} <= set(item) for item in result
731
- )
732
- assert all(
733
- isinstance(item["description"], str) and item["description"] for item in result
734
- )
735
-
736
-
737
- def test_list_tabs_tool_returns_summaries_and_details(monkeypatch, tmp_path):
738
- import flowent.settings as settings_module
739
- from flowent.graph_service import create_agent_node, create_edge, create_tab
740
- from flowent.workspace_store import workspace_store
741
-
742
- settings_file = tmp_path / "settings.json"
743
- settings_file.write_text(
744
- json.dumps(
745
- {
746
- "event_log": {"timestamp_format": "absolute"},
747
- "model": {"active_provider_id": "", "active_model": ""},
748
- "custom_prompt": "",
749
- "custom_post_prompt": "",
750
- "providers": [],
751
- "roles": [],
752
- }
753
- ),
754
- encoding="utf-8",
755
- )
756
- monkeypatch.setattr(settings_module, "_SETTINGS_FILE", settings_file)
757
- monkeypatch.setattr(settings_module, "_cached_settings", None)
758
- monkeypatch.setattr(
759
- "flowent.settings.get_settings",
760
- lambda: Settings(
761
- roles=[
762
- RoleConfig(
763
- name="Worker",
764
- system_prompt="Do work.",
765
- included_tools=["read"],
766
- )
767
- ]
768
- ),
769
- )
770
- workspace_store.reset_cache()
771
- registry.reset()
772
-
773
- try:
774
- tab = create_tab(title="Review")
775
- left, error = create_agent_node(role_name="Worker", tab_id=tab.id, name="Left")
776
- assert error is None and left is not None
777
- right, error = create_agent_node(
778
- role_name="Worker", tab_id=tab.id, name="Right"
779
- )
780
- assert error is None and right is not None
781
- edge, error = create_edge(from_node_id=left.id, to_node_id=right.id)
782
- assert error is None and edge is not None
783
-
784
- agent = Agent(
785
- NodeConfig(node_type=NodeType.ASSISTANT, tools=["list_workflows"]),
786
- uuid="assistant",
787
- )
788
-
789
- summaries = json.loads(ListTabsTool().execute(agent, {}))
790
- assert len(summaries) == 1
791
- assert summaries[0]["id"] == tab.id
792
- assert summaries[0]["title"] == tab.title
793
- assert summaries[0]["created_at"] == tab.created_at
794
- assert isinstance(summaries[0]["updated_at"], float)
795
- assert summaries[0]["node_count"] == 2
796
- assert summaries[0]["edge_count"] == 1
797
-
798
- detail = json.loads(ListTabsTool().execute(agent, {"workflow_id": tab.id}))
799
- assert detail["workflow"]["id"] == tab.id
800
- assert {node["name"] for node in detail["nodes"]} == {"Left", "Right"}
801
- assert detail["edges"] == [edge.serialize()]
802
- finally:
803
- registry.reset()
804
- workspace_store.reset_cache()
805
- monkeypatch.setattr(settings_module, "_cached_settings", None)
806
-
807
-
808
- def test_todo_tool_writes_via_set_todos(monkeypatch):
809
- agent = Agent(NodeConfig(node_type=NodeType.AGENT, tools=["todo"]))
810
- applied: list[list[TodoItem]] = []
811
-
812
- def fake_set_todos(todos: list[TodoItem]) -> None:
813
- applied.append(todos)
814
- agent.todos = list(todos)
815
-
816
- monkeypatch.setattr(agent, "set_todos", fake_set_todos)
817
-
818
- result = json.loads(TodoTool().execute(agent, {"todos": ["step 1", "step 2"]}))
819
-
820
- assert result == {"status": "updated"}
821
- assert len(applied) == 1
822
- assert [item.text for item in applied[0]] == ["step 1", "step 2"]