flowent 0.0.5 → 0.0.7

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 (265) hide show
  1. package/README.md +1 -1
  2. package/backend/README.md +1 -1
  3. package/backend/pyproject.toml +1 -1
  4. package/backend/src/flowent/__pycache__/__init__.cpython-313.pyc +0 -0
  5. package/backend/src/flowent/__pycache__/_version.cpython-313.pyc +0 -0
  6. package/backend/src/flowent/__pycache__/access.cpython-313.pyc +0 -0
  7. package/backend/src/flowent/__pycache__/agent.cpython-313.pyc +0 -0
  8. package/backend/src/flowent/__pycache__/assistant_commands.cpython-313.pyc +0 -0
  9. package/backend/src/flowent/__pycache__/cli.cpython-313.pyc +0 -0
  10. package/backend/src/flowent/__pycache__/config.cpython-313.pyc +0 -0
  11. package/backend/src/flowent/__pycache__/events.cpython-313.pyc +0 -0
  12. package/backend/src/flowent/__pycache__/graph_runtime.cpython-313.pyc +0 -0
  13. package/backend/src/flowent/__pycache__/graph_service.cpython-313.pyc +0 -0
  14. package/backend/src/flowent/__pycache__/image_assets.cpython-313.pyc +0 -0
  15. package/backend/src/flowent/__pycache__/logging.cpython-313.pyc +0 -0
  16. package/backend/src/flowent/__pycache__/main.cpython-313.pyc +0 -0
  17. package/backend/src/flowent/__pycache__/model_metadata.cpython-313.pyc +0 -0
  18. package/backend/src/flowent/__pycache__/network.cpython-313.pyc +0 -0
  19. package/backend/src/flowent/__pycache__/observability_service.cpython-313.pyc +0 -0
  20. package/backend/src/flowent/__pycache__/registry.cpython-313.pyc +0 -0
  21. package/backend/src/flowent/__pycache__/role_management.cpython-313.pyc +0 -0
  22. package/backend/src/flowent/__pycache__/runtime.cpython-313.pyc +0 -0
  23. package/backend/src/flowent/__pycache__/sandbox.cpython-313.pyc +0 -0
  24. package/backend/src/flowent/__pycache__/security.cpython-313.pyc +0 -0
  25. package/backend/src/flowent/__pycache__/settings.cpython-313.pyc +0 -0
  26. package/backend/src/flowent/__pycache__/settings_management.cpython-313.pyc +0 -0
  27. package/backend/src/flowent/__pycache__/state_db.cpython-313.pyc +0 -0
  28. package/backend/src/flowent/__pycache__/workspace_store.cpython-313.pyc +0 -0
  29. package/backend/src/flowent/agent.py +91 -8
  30. package/backend/src/flowent/channels/__pycache__/__init__.cpython-313.pyc +0 -0
  31. package/backend/src/flowent/channels/__pycache__/telegram.cpython-313.pyc +0 -0
  32. package/backend/src/flowent/graph_service.py +3 -110
  33. package/backend/src/flowent/models/__pycache__/__init__.cpython-313.pyc +0 -0
  34. package/backend/src/flowent/models/__pycache__/agent.cpython-313.pyc +0 -0
  35. package/backend/src/flowent/models/__pycache__/base.cpython-313.pyc +0 -0
  36. package/backend/src/flowent/models/__pycache__/blueprint.cpython-313.pyc +0 -0
  37. package/backend/src/flowent/models/__pycache__/content.cpython-313.pyc +0 -0
  38. package/backend/src/flowent/models/__pycache__/delta.cpython-313.pyc +0 -0
  39. package/backend/src/flowent/models/__pycache__/event.cpython-313.pyc +0 -0
  40. package/backend/src/flowent/models/__pycache__/graph.cpython-313.pyc +0 -0
  41. package/backend/src/flowent/models/__pycache__/history.cpython-313.pyc +0 -0
  42. package/backend/src/flowent/models/__pycache__/llm.cpython-313.pyc +0 -0
  43. package/backend/src/flowent/models/__pycache__/message.cpython-313.pyc +0 -0
  44. package/backend/src/flowent/models/__pycache__/tab.cpython-313.pyc +0 -0
  45. package/backend/src/flowent/models/__pycache__/todo.cpython-313.pyc +0 -0
  46. package/backend/src/flowent/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
  47. package/backend/src/flowent/prompts/__pycache__/common.cpython-313.pyc +0 -0
  48. package/backend/src/flowent/prompts/__pycache__/steward.cpython-313.pyc +0 -0
  49. package/backend/src/flowent/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  50. package/backend/src/flowent/providers/__pycache__/anthropic.cpython-313.pyc +0 -0
  51. package/backend/src/flowent/providers/__pycache__/base_url.cpython-313.pyc +0 -0
  52. package/backend/src/flowent/providers/__pycache__/configuration.cpython-313.pyc +0 -0
  53. package/backend/src/flowent/providers/__pycache__/content.cpython-313.pyc +0 -0
  54. package/backend/src/flowent/providers/__pycache__/errors.cpython-313.pyc +0 -0
  55. package/backend/src/flowent/providers/__pycache__/gateway.cpython-313.pyc +0 -0
  56. package/backend/src/flowent/providers/__pycache__/headers.cpython-313.pyc +0 -0
  57. package/backend/src/flowent/providers/__pycache__/management.cpython-313.pyc +0 -0
  58. package/backend/src/flowent/providers/__pycache__/openai.cpython-313.pyc +0 -0
  59. package/backend/src/flowent/providers/__pycache__/openai_responses.cpython-313.pyc +0 -0
  60. package/backend/src/flowent/providers/__pycache__/registry.cpython-313.pyc +0 -0
  61. package/backend/src/flowent/providers/__pycache__/sse.cpython-313.pyc +0 -0
  62. package/backend/src/flowent/providers/__pycache__/thinking.cpython-313.pyc +0 -0
  63. package/backend/src/flowent/role_management.py +9 -6
  64. package/backend/src/flowent/routes/__init__.py +0 -2
  65. package/backend/src/flowent/routes/__pycache__/__init__.cpython-313.pyc +0 -0
  66. package/backend/src/flowent/routes/__pycache__/access.cpython-313.pyc +0 -0
  67. package/backend/src/flowent/routes/__pycache__/assistant.cpython-313.pyc +0 -0
  68. package/backend/src/flowent/routes/__pycache__/image_assets.cpython-313.pyc +0 -0
  69. package/backend/src/flowent/routes/__pycache__/meta.cpython-313.pyc +0 -0
  70. package/backend/src/flowent/routes/__pycache__/nodes.cpython-313.pyc +0 -0
  71. package/backend/src/flowent/routes/__pycache__/prompts.cpython-313.pyc +0 -0
  72. package/backend/src/flowent/routes/__pycache__/providers_route.cpython-313.pyc +0 -0
  73. package/backend/src/flowent/routes/__pycache__/roles.cpython-313.pyc +0 -0
  74. package/backend/src/flowent/routes/__pycache__/settings.cpython-313.pyc +0 -0
  75. package/backend/src/flowent/routes/__pycache__/tabs.cpython-313.pyc +0 -0
  76. package/backend/src/flowent/routes/__pycache__/ws.cpython-313.pyc +0 -0
  77. package/backend/src/flowent/routes/assistant.py +3 -0
  78. package/backend/src/flowent/routes/nodes.py +11 -1
  79. package/backend/src/flowent/routes/settings.py +169 -118
  80. package/backend/src/flowent/routes/tabs.py +0 -12
  81. package/backend/src/flowent/runtime.py +0 -5
  82. package/backend/src/flowent/security.py +1 -21
  83. package/backend/src/flowent/settings.py +15 -421
  84. package/backend/src/flowent/settings_management.py +260 -164
  85. package/backend/src/flowent/state_db.py +2 -14
  86. package/backend/src/flowent/static/assets/AssistantPage-BW7XAd9I.js +1 -0
  87. package/backend/src/flowent/static/assets/ChannelsPage-tCJHgt6m.js +1 -0
  88. package/backend/src/flowent/static/assets/{PageScaffold-DteOA8V7.js → PageScaffold-f6g2l7XN.js} +1 -1
  89. package/backend/src/flowent/static/assets/PromptsPage-C3Sxn2D7.js +1 -0
  90. package/backend/src/flowent/static/assets/ProvidersPage-BfmdXmNt.js +3 -0
  91. package/backend/src/flowent/static/assets/RolesPage-DET8wO4r.js +1 -0
  92. package/backend/src/flowent/static/assets/SettingsPage-D-g3deMm.js +3 -0
  93. package/backend/src/flowent/static/assets/ToolsPage-CDmtE2g4.js +1 -0
  94. package/backend/src/flowent/static/assets/WorkspacePage-AZsJ0sD0.js +3 -0
  95. package/backend/src/flowent/static/assets/WorkspacePanels-CteCjolX.js +1 -0
  96. package/backend/src/flowent/static/assets/{alert-dialog-DIBUCmqM.js → alert-dialog-Duorp_S-.js} +1 -1
  97. package/backend/src/flowent/static/assets/{dialog-BOvHIBrg.js → dialog-C3ixjGjN.js} +1 -1
  98. package/backend/src/flowent/static/assets/index--o_0fv0N.css +1 -0
  99. package/backend/src/flowent/static/assets/index-C9HuekJm.js +10 -0
  100. package/backend/src/flowent/static/assets/{modelParams-DcEhGnu0.js → modelParams-DmnF2hwR.js} +1 -1
  101. package/backend/src/flowent/static/assets/providerTypes-DT3Ahwl_.js +1 -0
  102. package/backend/src/flowent/static/assets/roles-CuRT_chR.js +1 -0
  103. package/{dist/frontend/assets/select-D9SwnlXF.js → backend/src/flowent/static/assets/select-DCfeNu-F.js} +1 -1
  104. package/backend/src/flowent/static/assets/surface-pWwG5ogx.js +1 -0
  105. package/backend/src/flowent/static/assets/{ui-vendor-UazN8rcv.js → ui-vendor-C5pJa8N7.js} +15 -15
  106. package/backend/src/flowent/static/assets/useAppRoute-FgSHBKhV.js +1 -0
  107. package/backend/src/flowent/static/index.html +3 -3
  108. package/backend/src/flowent/tools/__init__.py +2 -101
  109. package/backend/src/flowent/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  110. package/backend/src/flowent/tools/__pycache__/connect.cpython-313.pyc +0 -0
  111. package/backend/src/flowent/tools/__pycache__/contacts.cpython-313.pyc +0 -0
  112. package/backend/src/flowent/tools/__pycache__/create_agent.cpython-313.pyc +0 -0
  113. package/backend/src/flowent/tools/__pycache__/create_tab.cpython-313.pyc +0 -0
  114. package/backend/src/flowent/tools/__pycache__/delete_tab.cpython-313.pyc +0 -0
  115. package/backend/src/flowent/tools/__pycache__/edit.cpython-313.pyc +0 -0
  116. package/backend/src/flowent/tools/__pycache__/exec.cpython-313.pyc +0 -0
  117. package/backend/src/flowent/tools/__pycache__/fetch.cpython-313.pyc +0 -0
  118. package/backend/src/flowent/tools/__pycache__/idle.cpython-313.pyc +0 -0
  119. package/backend/src/flowent/tools/__pycache__/list_roles.cpython-313.pyc +0 -0
  120. package/backend/src/flowent/tools/__pycache__/list_tabs.cpython-313.pyc +0 -0
  121. package/backend/src/flowent/tools/__pycache__/list_tools.cpython-313.pyc +0 -0
  122. package/backend/src/flowent/tools/__pycache__/manage_prompts.cpython-313.pyc +0 -0
  123. package/backend/src/flowent/tools/__pycache__/manage_providers.cpython-313.pyc +0 -0
  124. package/backend/src/flowent/tools/__pycache__/manage_roles.cpython-313.pyc +0 -0
  125. package/backend/src/flowent/tools/__pycache__/manage_settings.cpython-313.pyc +0 -0
  126. package/backend/src/flowent/tools/__pycache__/read.cpython-313.pyc +0 -0
  127. package/backend/src/flowent/tools/__pycache__/send.cpython-313.pyc +0 -0
  128. package/backend/src/flowent/tools/__pycache__/set_permissions.cpython-313.pyc +0 -0
  129. package/backend/src/flowent/tools/__pycache__/sleep.cpython-313.pyc +0 -0
  130. package/backend/src/flowent/tools/__pycache__/todo.cpython-313.pyc +0 -0
  131. package/backend/src/flowent/tools/list_roles.py +2 -9
  132. package/backend/src/flowent/tools/manage_settings.py +134 -172
  133. package/backend/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  134. package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  135. package/backend/tests/integration/api/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  136. package/backend/tests/integration/api/__pycache__/test_access_api.cpython-313-pytest-9.0.3.pyc +0 -0
  137. package/backend/tests/integration/api/__pycache__/test_assistant_api.cpython-313-pytest-9.0.3.pyc +0 -0
  138. package/backend/tests/integration/api/__pycache__/test_frontend_mounting.cpython-313-pytest-9.0.3.pyc +0 -0
  139. package/backend/tests/integration/api/__pycache__/test_meta_api.cpython-313-pytest-9.0.3.pyc +0 -0
  140. package/backend/tests/integration/api/__pycache__/test_nodes_api.cpython-313-pytest-9.0.3.pyc +0 -0
  141. package/backend/tests/integration/api/__pycache__/test_prompts_api.cpython-313-pytest-9.0.3.pyc +0 -0
  142. package/backend/tests/integration/api/__pycache__/test_roles_api.cpython-313-pytest-9.0.3.pyc +0 -0
  143. package/backend/tests/integration/api/__pycache__/test_tabs_api.cpython-313-pytest-9.0.3.pyc +0 -0
  144. package/backend/tests/integration/api/test_assistant_api.py +68 -0
  145. package/backend/tests/integration/api/test_meta_api.py +0 -1
  146. package/backend/tests/integration/api/test_nodes_api.py +73 -8
  147. package/backend/tests/integration/api/test_tabs_api.py +0 -114
  148. package/backend/tests/unit/__pycache__/test_access.cpython-313-pytest-9.0.3.pyc +0 -0
  149. package/backend/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  150. package/backend/tests/unit/__pycache__/test_graph_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  151. package/backend/tests/unit/__pycache__/test_network.cpython-313-pytest-9.0.3.pyc +0 -0
  152. package/backend/tests/unit/__pycache__/test_state_sqlite_storage.cpython-313-pytest-9.0.3.pyc +0 -0
  153. package/backend/tests/unit/__pycache__/test_workspace_store.cpython-313-pytest-9.0.3.pyc +0 -0
  154. package/backend/tests/unit/agent/__pycache__/test_agent_public_api.cpython-313-pytest-9.0.3.pyc +0 -0
  155. package/backend/tests/unit/agent/__pycache__/test_agent_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  156. package/backend/tests/unit/agent/test_agent_public_api.py +0 -15
  157. package/backend/tests/unit/agent/test_agent_runtime.py +148 -2
  158. package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
  159. package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
  160. package/backend/tests/unit/prompts/__pycache__/test_prompts.cpython-313-pytest-9.0.3.pyc +0 -0
  161. package/backend/tests/unit/providers/__pycache__/test_anthropic_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  162. package/backend/tests/unit/providers/__pycache__/test_errors.cpython-313-pytest-9.0.3.pyc +0 -0
  163. package/backend/tests/unit/providers/__pycache__/test_extract_delta_parts.cpython-313-pytest-9.0.3.pyc +0 -0
  164. package/backend/tests/unit/providers/__pycache__/test_openai_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  165. package/backend/tests/unit/providers/__pycache__/test_openai_responses.cpython-313-pytest-9.0.3.pyc +0 -0
  166. package/backend/tests/unit/providers/__pycache__/test_provider_gateway.cpython-313-pytest-9.0.3.pyc +0 -0
  167. package/backend/tests/unit/providers/__pycache__/test_think_tag_parser.cpython-313-pytest-9.0.3.pyc +0 -0
  168. package/backend/tests/unit/routes/__pycache__/test_prompts_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  169. package/backend/tests/unit/routes/__pycache__/test_providers_route.cpython-313-pytest-9.0.3.pyc +0 -0
  170. package/backend/tests/unit/routes/__pycache__/test_roles_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  171. package/backend/tests/unit/routes/__pycache__/test_settings_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  172. package/backend/tests/unit/routes/test_prompts_routes.py +0 -22
  173. package/backend/tests/unit/routes/test_roles_routes.py +6 -2
  174. package/backend/tests/unit/routes/test_settings_routes.py +0 -19
  175. package/backend/tests/unit/runtime/__pycache__/test_bootstrap_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  176. package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
  177. package/backend/tests/unit/security/__pycache__/test_security.cpython-313-pytest-9.0.3.pyc +0 -0
  178. package/backend/tests/unit/settings/__pycache__/test_settings_roles.cpython-313-pytest-9.0.3.pyc +0 -0
  179. package/backend/tests/unit/settings/test_settings_roles.py +3 -51
  180. package/backend/tests/unit/test_cli.py +0 -22
  181. package/backend/tests/unit/test_state_sqlite_storage.py +27 -99
  182. package/backend/tests/unit/test_workspace_store.py +0 -3
  183. package/backend/tests/unit/tools/__pycache__/test_connect_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  184. package/backend/tests/unit/tools/__pycache__/test_create_agent_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  185. package/backend/tests/unit/tools/__pycache__/test_delete_tab_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  186. package/backend/tests/unit/tools/__pycache__/test_edit_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  187. package/backend/tests/unit/tools/__pycache__/test_exec_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  188. package/backend/tests/unit/tools/__pycache__/test_fetch_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  189. package/backend/tests/unit/tools/__pycache__/test_manage_prompts_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  190. package/backend/tests/unit/tools/__pycache__/test_manage_providers_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  191. package/backend/tests/unit/tools/__pycache__/test_manage_roles_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  192. package/backend/tests/unit/tools/__pycache__/test_manage_settings_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  193. package/backend/tests/unit/tools/__pycache__/test_read_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  194. package/backend/tests/unit/tools/__pycache__/test_set_permissions_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  195. package/backend/tests/unit/tools/__pycache__/test_todo_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  196. package/backend/tests/unit/tools/__pycache__/test_tool_registry.cpython-313-pytest-9.0.3.pyc +0 -0
  197. package/backend/tests/unit/tools/test_create_agent_tool.py +0 -32
  198. package/backend/tests/unit/tools/test_manage_prompts_tool.py +5 -30
  199. package/backend/tests/unit/tools/test_tool_registry.py +45 -40
  200. package/backend/uv.lock +1 -1
  201. package/dist/frontend/assets/AssistantPage-BW7XAd9I.js +1 -0
  202. package/dist/frontend/assets/ChannelsPage-tCJHgt6m.js +1 -0
  203. package/dist/frontend/assets/{PageScaffold-DteOA8V7.js → PageScaffold-f6g2l7XN.js} +1 -1
  204. package/dist/frontend/assets/PromptsPage-C3Sxn2D7.js +1 -0
  205. package/dist/frontend/assets/ProvidersPage-BfmdXmNt.js +3 -0
  206. package/dist/frontend/assets/RolesPage-DET8wO4r.js +1 -0
  207. package/dist/frontend/assets/SettingsPage-D-g3deMm.js +3 -0
  208. package/dist/frontend/assets/ToolsPage-CDmtE2g4.js +1 -0
  209. package/dist/frontend/assets/WorkspacePage-AZsJ0sD0.js +3 -0
  210. package/dist/frontend/assets/WorkspacePanels-CteCjolX.js +1 -0
  211. package/dist/frontend/assets/{alert-dialog-DIBUCmqM.js → alert-dialog-Duorp_S-.js} +1 -1
  212. package/dist/frontend/assets/{dialog-BOvHIBrg.js → dialog-C3ixjGjN.js} +1 -1
  213. package/dist/frontend/assets/index--o_0fv0N.css +1 -0
  214. package/dist/frontend/assets/index-C9HuekJm.js +10 -0
  215. package/dist/frontend/assets/{modelParams-DcEhGnu0.js → modelParams-DmnF2hwR.js} +1 -1
  216. package/dist/frontend/assets/providerTypes-DT3Ahwl_.js +1 -0
  217. package/dist/frontend/assets/roles-CuRT_chR.js +1 -0
  218. package/{backend/src/flowent/static/assets/select-D9SwnlXF.js → dist/frontend/assets/select-DCfeNu-F.js} +1 -1
  219. package/dist/frontend/assets/surface-pWwG5ogx.js +1 -0
  220. package/dist/frontend/assets/{ui-vendor-UazN8rcv.js → ui-vendor-C5pJa8N7.js} +15 -15
  221. package/dist/frontend/assets/useAppRoute-FgSHBKhV.js +1 -0
  222. package/dist/frontend/index.html +3 -3
  223. package/package.json +1 -1
  224. package/backend/src/flowent/__pycache__/mcp_service.cpython-313.pyc +0 -0
  225. package/backend/src/flowent/mcp_service.py +0 -1918
  226. package/backend/src/flowent/routes/__pycache__/mcp.cpython-313.pyc +0 -0
  227. package/backend/src/flowent/routes/mcp.py +0 -125
  228. package/backend/src/flowent/static/assets/AssistantPage-VBohhz4d.js +0 -1
  229. package/backend/src/flowent/static/assets/ChannelsPage-CIydPZA_.js +0 -1
  230. package/backend/src/flowent/static/assets/McpPage-CHPm2TPY.js +0 -7
  231. package/backend/src/flowent/static/assets/PromptsPage-CSmJ3sZg.js +0 -1
  232. package/backend/src/flowent/static/assets/ProvidersPage-sl2jeG4e.js +0 -3
  233. package/backend/src/flowent/static/assets/RolesPage-DCe7W6Km.js +0 -1
  234. package/backend/src/flowent/static/assets/SettingsPage-Bix9e63E.js +0 -3
  235. package/backend/src/flowent/static/assets/ToolsPage-favNkj5C.js +0 -1
  236. package/backend/src/flowent/static/assets/WorkspaceCommandDialog-DRS6wiD6.js +0 -1
  237. package/backend/src/flowent/static/assets/WorkspacePage-KuaDjt_D.js +0 -3
  238. package/backend/src/flowent/static/assets/WorkspacePanels-BZxBw8M5.js +0 -1
  239. package/backend/src/flowent/static/assets/datetime-eJqd0V2S.js +0 -1
  240. package/backend/src/flowent/static/assets/index-Biio-CoI.js +0 -10
  241. package/backend/src/flowent/static/assets/index-CmQvO7sl.css +0 -1
  242. package/backend/src/flowent/static/assets/roles-BbIEIMeG.js +0 -1
  243. package/backend/src/flowent/static/assets/surface-Bzr1FRG4.js +0 -1
  244. package/backend/src/flowent/static/assets/triState-DgLlKdRR.js +0 -1
  245. package/backend/src/flowent/tools/__pycache__/mcp.cpython-313.pyc +0 -0
  246. package/backend/src/flowent/tools/mcp.py +0 -199
  247. package/backend/tests/integration/api/__pycache__/test_mcp_api.cpython-313-pytest-9.0.3.pyc +0 -0
  248. package/backend/tests/integration/api/test_mcp_api.py +0 -116
  249. package/dist/frontend/assets/AssistantPage-VBohhz4d.js +0 -1
  250. package/dist/frontend/assets/ChannelsPage-CIydPZA_.js +0 -1
  251. package/dist/frontend/assets/McpPage-CHPm2TPY.js +0 -7
  252. package/dist/frontend/assets/PromptsPage-CSmJ3sZg.js +0 -1
  253. package/dist/frontend/assets/ProvidersPage-sl2jeG4e.js +0 -3
  254. package/dist/frontend/assets/RolesPage-DCe7W6Km.js +0 -1
  255. package/dist/frontend/assets/SettingsPage-Bix9e63E.js +0 -3
  256. package/dist/frontend/assets/ToolsPage-favNkj5C.js +0 -1
  257. package/dist/frontend/assets/WorkspaceCommandDialog-DRS6wiD6.js +0 -1
  258. package/dist/frontend/assets/WorkspacePage-KuaDjt_D.js +0 -3
  259. package/dist/frontend/assets/WorkspacePanels-BZxBw8M5.js +0 -1
  260. package/dist/frontend/assets/datetime-eJqd0V2S.js +0 -1
  261. package/dist/frontend/assets/index-Biio-CoI.js +0 -10
  262. package/dist/frontend/assets/index-CmQvO7sl.css +0 -1
  263. package/dist/frontend/assets/roles-BbIEIMeG.js +0 -1
  264. package/dist/frontend/assets/surface-Bzr1FRG4.js +0 -1
  265. package/dist/frontend/assets/triState-DgLlKdRR.js +0 -1
@@ -1,13 +1,17 @@
1
1
  import base64
2
+ import threading
2
3
  from uuid import UUID
3
4
 
4
5
  from flowent.models import (
6
+ AgentState,
5
7
  AssistantText,
8
+ ErrorEntry,
6
9
  ImagePart,
7
10
  LLMResponse,
8
11
  ReceivedMessage,
9
12
  TextPart,
10
13
  )
14
+ from flowent.providers.errors import LLMProviderError
11
15
  from flowent.registry import registry
12
16
 
13
17
  _ONE_PIXEL_PNG = base64.b64decode(
@@ -77,6 +81,70 @@ def test_clear_command_clears_history_back_to_empty_state(client):
77
81
  )
78
82
 
79
83
 
84
+ def test_assistant_accepts_new_message_after_error(monkeypatch, client):
85
+ assistant_id = _get_assistant_id(client)
86
+ assistant = registry.get(assistant_id)
87
+ assert assistant is not None
88
+ chat_started = threading.Event()
89
+ release_chat = threading.Event()
90
+
91
+ def fake_chat(**kwargs):
92
+ chat_started.set()
93
+ release_chat.wait(timeout=2)
94
+ raise LLMProviderError(
95
+ "LLM API error\nDetail: still invalid",
96
+ transient=False,
97
+ )
98
+
99
+ monkeypatch.setattr("flowent.agent.gateway.chat", fake_chat)
100
+ assistant.history.append(ErrorEntry(content="LLM API error\nDetail: Invalid key"))
101
+ assistant.set_state(AgentState.ERROR, "LLM API error")
102
+
103
+ try:
104
+ response = client.post(
105
+ "/api/assistant/message",
106
+ json={"content": "Continue with a safer plan"},
107
+ )
108
+
109
+ assert response.status_code == 200
110
+ message_id = response.json()["message_id"]
111
+ assert isinstance(message_id, str)
112
+ assert chat_started.wait(timeout=1)
113
+
114
+ detail = client.get(f"/api/nodes/{assistant_id}").json()
115
+ history = detail["history"]
116
+ assert any(
117
+ entry["type"] == "ErrorEntry"
118
+ and entry["content"] == "LLM API error\nDetail: Invalid key"
119
+ for entry in history
120
+ )
121
+ assert any(
122
+ entry["type"] == "ReceivedMessage"
123
+ and entry["from_id"] == "human"
124
+ and entry["message_id"] == message_id
125
+ and entry["content"] == "Continue with a safer plan"
126
+ for entry in history
127
+ )
128
+ assert registry.get(assistant_id).state == AgentState.RUNNING
129
+ finally:
130
+ release_chat.set()
131
+
132
+
133
+ def test_assistant_rejects_message_when_terminated(client):
134
+ assistant_id = _get_assistant_id(client)
135
+ assistant = registry.get(assistant_id)
136
+ assert assistant is not None
137
+ assistant.set_state(AgentState.TERMINATED, "done")
138
+
139
+ response = client.post(
140
+ "/api/assistant/message",
141
+ json={"content": "Continue"},
142
+ )
143
+
144
+ assert response.status_code == 409
145
+ assert response.json()["detail"] == "Assistant is no longer available"
146
+
147
+
80
148
  def test_compact_command_replaces_history_with_summary(monkeypatch, client):
81
149
  assistant_id = _get_assistant_id(client)
82
150
  assistant = registry.get(assistant_id)
@@ -6,7 +6,6 @@ def test_tools_api_shows_agent_visible_management_tools(client: TestClient):
6
6
 
7
7
  assert response.status_code == 200
8
8
  tool_names = {tool["name"] for tool in response.json()["tools"]}
9
- assert "create_root" not in tool_names
10
9
  assert "manage_providers" in tool_names
11
10
  assert "manage_roles" in tool_names
12
11
  assert "manage_settings" in tool_names
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import threading
2
3
  import time
3
4
  from uuid import UUID
4
5
 
@@ -7,13 +8,14 @@ from fastapi.testclient import TestClient
7
8
  from flowent.models import (
8
9
  AgentState,
9
10
  AssistantText,
11
+ ErrorEntry,
10
12
  ImagePart,
11
13
  LLMResponse,
12
14
  ReceivedMessage,
13
15
  TextPart,
14
16
  )
17
+ from flowent.providers.errors import LLMProviderError
15
18
  from flowent.registry import registry
16
- from flowent.routes.nodes import router as nodes_router
17
19
  from flowent.settings import STEWARD_ROLE_INCLUDED_TOOLS
18
20
  from flowent.tools import MINIMUM_TOOLS
19
21
 
@@ -135,13 +137,6 @@ def test_worker_contacts_follow_output_paths(
135
137
  ]
136
138
 
137
139
 
138
- def test_direct_node_message_api_is_not_available(client: TestClient):
139
- assert not any(
140
- getattr(route, "path", None) == "/api/nodes/{node_id}/message"
141
- for route in nodes_router.routes
142
- )
143
-
144
-
145
140
  def test_only_assistant_node_exists_at_startup(client: TestClient):
146
141
  list_response = client.get("/api/nodes")
147
142
 
@@ -308,6 +303,76 @@ def test_unknown_slash_input_can_be_sent_directly_to_workflow_leader(
308
303
  )
309
304
 
310
305
 
306
+ def test_leader_accepts_new_message_after_error(monkeypatch, client: TestClient):
307
+ tab = client.post(
308
+ "/api/workflows",
309
+ json={"title": "Execution", "allow_network": True},
310
+ ).json()
311
+ leader = registry.get(tab["leader_id"])
312
+ assert leader is not None
313
+ chat_started = threading.Event()
314
+ release_chat = threading.Event()
315
+
316
+ def fake_chat(**kwargs):
317
+ chat_started.set()
318
+ release_chat.wait(timeout=2)
319
+ raise LLMProviderError(
320
+ "LLM API error\nDetail: still invalid",
321
+ transient=False,
322
+ )
323
+
324
+ monkeypatch.setattr("flowent.agent.gateway.chat", fake_chat)
325
+ leader.history.append(ErrorEntry(content="LLM API error\nDetail: Invalid key"))
326
+ leader.set_state(AgentState.ERROR, "LLM API error")
327
+
328
+ try:
329
+ response = client.post(
330
+ f"/api/nodes/{tab['leader_id']}/messages",
331
+ json={"content": "Continue the workflow chat"},
332
+ )
333
+
334
+ assert response.status_code == 200
335
+ message_id = response.json()["message_id"]
336
+ assert isinstance(message_id, str)
337
+ assert chat_started.wait(timeout=1)
338
+
339
+ detail = client.get(f"/api/nodes/{tab['leader_id']}").json()
340
+ history = detail["history"]
341
+ assert any(
342
+ entry["type"] == "ErrorEntry"
343
+ and entry["content"] == "LLM API error\nDetail: Invalid key"
344
+ for entry in history
345
+ )
346
+ assert any(
347
+ entry["type"] == "ReceivedMessage"
348
+ and entry["from_id"] == "human"
349
+ and entry["message_id"] == message_id
350
+ and entry["content"] == "Continue the workflow chat"
351
+ for entry in history
352
+ )
353
+ assert registry.get(tab["leader_id"]).state == AgentState.RUNNING
354
+ finally:
355
+ release_chat.set()
356
+
357
+
358
+ def test_leader_rejects_message_when_terminated(client: TestClient):
359
+ tab = client.post(
360
+ "/api/workflows",
361
+ json={"title": "Execution"},
362
+ ).json()
363
+ leader = registry.get(tab["leader_id"])
364
+ assert leader is not None
365
+ leader.set_state(AgentState.TERMINATED, "done")
366
+
367
+ response = client.post(
368
+ f"/api/nodes/{tab['leader_id']}/messages",
369
+ json={"content": "Continue the workflow chat"},
370
+ )
371
+
372
+ assert response.status_code == 409
373
+ assert response.json()["detail"] == "This workflow chat is no longer available"
374
+
375
+
311
376
  def test_leader_help_command_returns_visible_command_feedback(client: TestClient):
312
377
  tab = client.post(
313
378
  "/api/workflows",
@@ -46,24 +46,6 @@ def test_list_tabs_is_empty_at_startup(client: TestClient):
46
46
  assert response.json() == {"workflows": []}
47
47
 
48
48
 
49
- def test_create_tab_rejects_removed_mcp_servers_field(client: TestClient):
50
- response = client.post(
51
- "/api/workflows",
52
- json={"title": "Review Task", "mcp_servers": ["filesystem"]},
53
- )
54
-
55
- assert response.status_code == 422
56
-
57
-
58
- def test_create_tab_rejects_removed_goal_field(client: TestClient):
59
- response = client.post(
60
- "/api/workflows",
61
- json={"title": "Review Task", "goal": "Inspect changed files"},
62
- )
63
-
64
- assert response.status_code == 422
65
-
66
-
67
49
  def test_create_tab_node_and_edge_round_trip(client: TestClient):
68
50
  create_tab_response = client.post(
69
51
  "/api/workflows",
@@ -335,78 +317,6 @@ def test_tab_edge_creation_enforces_directed_ports_and_single_input(
335
317
  )
336
318
 
337
319
 
338
- def test_duplicate_tab_copies_definition_and_runtime_agents(client: TestClient):
339
- source_tab = client.post(
340
- "/api/workflows",
341
- json={
342
- "title": "Original Workflow",
343
- "allow_network": True,
344
- "write_dirs": ["/tmp"],
345
- },
346
- ).json()
347
- source_tab_id = source_tab["id"]
348
-
349
- reviewer = _create_agent_node(client, tab_id=source_tab_id, name="Reviewer")
350
- formatter = _create_graph_node(
351
- client,
352
- tab_id=source_tab_id,
353
- node_type="code",
354
- name="Formatter",
355
- config={"language": "python"},
356
- )
357
- source_detail = client.get(f"/api/workflows/{source_tab_id}").json()
358
- source_definition = deepcopy(source_detail["workflow"]["definition"])
359
- source_definition["view"] = {
360
- "positions": {
361
- reviewer["id"]: {"x": 20, "y": 40},
362
- formatter["id"]: {"x": 180, "y": 40},
363
- }
364
- }
365
- source_definition["edges"] = [
366
- {
367
- "id": "edge-review",
368
- "from_node_id": reviewer["id"],
369
- "from_port_key": "out",
370
- "to_node_id": formatter["id"],
371
- "to_port_key": "in",
372
- }
373
- ]
374
- update_response = client.put(
375
- f"/api/workflows/{source_tab_id}/definition",
376
- json={"definition": source_definition},
377
- )
378
- assert update_response.status_code == 200
379
-
380
- duplicate_response = client.post(f"/api/workflows/{source_tab_id}/duplicate")
381
-
382
- assert duplicate_response.status_code == 200
383
- duplicated_tab = duplicate_response.json()
384
- assert duplicated_tab["title"] == "Original Workflow Copy"
385
- assert "goal" not in duplicated_tab
386
- assert duplicated_tab["node_count"] == 2
387
- assert duplicated_tab["edge_count"] == 1
388
- assert duplicated_tab["id"] != source_tab_id
389
- assert duplicated_tab["leader_id"] != source_tab["leader_id"]
390
- assert duplicated_tab["activation_state"] == "inactive"
391
- assert duplicated_tab["allow_network"] is True
392
- assert duplicated_tab["write_dirs"] == ["/tmp"]
393
-
394
- duplicated_detail = client.get(f"/api/workflows/{duplicated_tab['id']}").json()
395
- assert "goal" not in duplicated_detail["workflow"]
396
- assert duplicated_detail["workflow"]["allow_network"] is True
397
- assert duplicated_detail["workflow"]["write_dirs"] == ["/tmp"]
398
- assert {node["name"] for node in duplicated_detail["nodes"]} == {
399
- "Reviewer",
400
- "Formatter",
401
- }
402
- duplicated_node_ids = {node["id"] for node in duplicated_detail["nodes"]}
403
- source_node_ids = {node["id"] for node in source_detail["nodes"]}
404
- assert duplicated_node_ids.isdisjoint(source_node_ids)
405
- assert duplicated_detail["edges"][0]["from_node_id"] in duplicated_node_ids
406
- assert duplicated_detail["edges"][0]["to_node_id"] in duplicated_node_ids
407
- assert duplicated_detail["workflow"]["definition"]["view"]["positions"]
408
-
409
-
410
320
  def test_update_tab_definition_updates_metadata_and_positions(client: TestClient):
411
321
  tab = client.post(
412
322
  "/api/workflows",
@@ -554,30 +464,6 @@ def test_activate_agent_only_workflow_succeeds_without_trigger(client: TestClien
554
464
  assert detail["nodes"][0]["id"] == worker["id"]
555
465
 
556
466
 
557
- def test_activate_legacy_agent_only_workflow_succeeds_without_trigger(
558
- client: TestClient,
559
- ):
560
- tab = client.post("/api/workflows", json={"title": "Legacy Collaborative"}).json()
561
- worker = _create_agent_node(client, tab_id=tab["id"], name="Worker")
562
- definition = deepcopy(
563
- client.get(f"/api/workflows/{tab['id']}").json()["workflow"]["definition"]
564
- )
565
- definition["nodes"][0]["inputs"][0]["required"] = True
566
- update_response = client.put(
567
- f"/api/workflows/{tab['id']}/definition",
568
- json={"definition": definition},
569
- )
570
- assert update_response.status_code == 200
571
-
572
- response = client.post(f"/api/workflows/{tab['id']}/activate")
573
-
574
- assert response.status_code == 200
575
- assert response.json()["activation_state"] == "active"
576
- detail = client.get(f"/api/workflows/{tab['id']}").json()
577
- assert detail["workflow"]["activation_state"] == "active"
578
- assert detail["nodes"][0]["id"] == worker["id"]
579
-
580
-
581
467
  def test_activate_valid_manual_trigger_graph_succeeds(client: TestClient):
582
468
  tab = client.post("/api/workflows", json={"title": "Manual"}).json()
583
469
  trigger = _create_graph_node(
@@ -672,11 +672,6 @@ def test_list_roles_tool_returns_registered_roles(monkeypatch):
672
672
  "fetch",
673
673
  "list_roles",
674
674
  "list_tools",
675
- "list_mcp_resources",
676
- "list_mcp_resource_templates",
677
- "read_mcp_resource",
678
- "list_mcp_prompts",
679
- "get_mcp_prompt",
680
675
  ],
681
676
  },
682
677
  {
@@ -699,11 +694,6 @@ def test_list_roles_tool_returns_registered_roles(monkeypatch):
699
694
  "exec",
700
695
  "list_roles",
701
696
  "list_tools",
702
- "list_mcp_resources",
703
- "list_mcp_resource_templates",
704
- "read_mcp_resource",
705
- "list_mcp_prompts",
706
- "get_mcp_prompt",
707
697
  ],
708
698
  },
709
699
  ]
@@ -735,11 +725,6 @@ def test_list_tools_tool_returns_registered_tool_names_and_descriptions():
735
725
  "connect",
736
726
  "list_roles",
737
727
  "list_tools",
738
- "list_mcp_resources",
739
- "list_mcp_resource_templates",
740
- "read_mcp_resource",
741
- "list_mcp_prompts",
742
- "get_mcp_prompt",
743
728
  }
744
729
  assert all(
745
730
  {"name", "description", "source", "parameters"} <= set(item) for item in result
@@ -1118,6 +1118,50 @@ def test_execute_compact_command_replaces_history_with_summary(monkeypatch):
1118
1118
  assert "Compacted this chat for future replies." not in serialized
1119
1119
 
1120
1120
 
1121
+ def test_compacted_context_normalizes_legacy_traceback_summary(monkeypatch):
1122
+ monkeypatch.setattr("flowent.agent.get_settings", lambda: Settings())
1123
+
1124
+ assistant = Agent(NodeConfig(node_type=NodeType.ASSISTANT), uuid="assistant")
1125
+ assistant._set_execution_context(
1126
+ summary=(
1127
+ "## Current Goal\nRecover the chat.\n\n"
1128
+ "Traceback (most recent call last):\n"
1129
+ ' File "/tmp/example.py", line 1, in <module>\n'
1130
+ "RuntimeError: boom"
1131
+ ),
1132
+ history_cutoff=0,
1133
+ )
1134
+
1135
+ messages = assistant._build_messages()
1136
+ serialized = json.dumps(messages)
1137
+
1138
+ assert "Compacted execution context" in serialized
1139
+ assert "Recover the chat." in serialized
1140
+ assert "Traceback" not in serialized
1141
+ assert "/tmp/example.py" not in serialized
1142
+
1143
+
1144
+ def test_compacted_context_normalizes_legacy_html_summary(monkeypatch):
1145
+ monkeypatch.setattr("flowent.agent.get_settings", lambda: Settings())
1146
+
1147
+ assistant = Agent(NodeConfig(node_type=NodeType.ASSISTANT), uuid="assistant")
1148
+ assistant._set_execution_context(
1149
+ summary=(
1150
+ "## Current Goal\nRecover the chat.\n\n"
1151
+ "<!doctype html><html><body>Bad Gateway</body></html>"
1152
+ ),
1153
+ history_cutoff=0,
1154
+ )
1155
+
1156
+ messages = assistant._build_messages()
1157
+ serialized = json.dumps(messages)
1158
+
1159
+ assert "Compacted execution context" in serialized
1160
+ assert "unexpected error response" in serialized
1161
+ assert "<!doctype html" not in serialized
1162
+ assert "<html" not in serialized
1163
+
1164
+
1121
1165
  def test_compact_command_excludes_queued_messages_from_summary(monkeypatch):
1122
1166
  assistant = Agent(NodeConfig(node_type=NodeType.ASSISTANT), uuid="assistant")
1123
1167
  assistant.history.extend(
@@ -2668,18 +2712,120 @@ def test_build_messages_keeps_error_entries_in_context(monkeypatch):
2668
2712
 
2669
2713
  agent = Agent(NodeConfig(node_type=NodeType.AGENT), uuid="agent")
2670
2714
  agent._append_history(ReceivedMessage(content="begin", from_id="human"))
2671
- agent._append_history(ErrorEntry(content="RuntimeError: boom\n\ntraceback"))
2715
+ agent._append_history(ErrorEntry(content="RuntimeError: boom"))
2716
+
2717
+ messages = agent._build_messages()
2718
+
2719
+ assert any(
2720
+ msg.get("role") == "user"
2721
+ and msg.get("content")
2722
+ == "<system>Previous runtime error:\nRuntimeError: boom</system>"
2723
+ for msg in messages
2724
+ )
2725
+
2726
+
2727
+ def test_build_messages_normalizes_legacy_traceback_errors(monkeypatch):
2728
+ monkeypatch.setattr("flowent.agent.get_settings", lambda: Settings())
2729
+
2730
+ agent = Agent(NodeConfig(node_type=NodeType.AGENT), uuid="agent")
2731
+ agent._append_history(
2732
+ ErrorEntry(
2733
+ content=(
2734
+ "RuntimeError: boom\n\n"
2735
+ "Traceback (most recent call last):\n"
2736
+ ' File "/tmp/example.py", line 1, in <module>\n'
2737
+ "RuntimeError: boom"
2738
+ )
2739
+ )
2740
+ )
2741
+
2742
+ messages = agent._build_messages()
2743
+
2744
+ assert any(
2745
+ msg.get("role") == "user"
2746
+ and msg.get("content")
2747
+ == "<system>Previous runtime error:\nRuntimeError: boom</system>"
2748
+ for msg in messages
2749
+ )
2750
+ assert not any("Traceback" in str(msg.get("content")) for msg in messages)
2751
+
2752
+
2753
+ def test_build_messages_normalizes_legacy_html_errors(monkeypatch):
2754
+ monkeypatch.setattr("flowent.agent.get_settings", lambda: Settings())
2755
+
2756
+ agent = Agent(NodeConfig(node_type=NodeType.AGENT), uuid="agent")
2757
+ agent._append_history(
2758
+ ErrorEntry(content="<html><body><pre>Internal Server Error</pre></body></html>")
2759
+ )
2760
+
2761
+ messages = agent._build_messages()
2762
+ serialized = json.dumps(messages)
2763
+
2764
+ assert "Upstream returned an unexpected error response" in serialized
2765
+ assert "<html" not in serialized
2766
+ assert "<pre>" not in serialized
2767
+
2768
+
2769
+ def test_build_messages_keeps_provider_error_text_in_context(monkeypatch):
2770
+ monkeypatch.setattr("flowent.agent.get_settings", lambda: Settings())
2771
+
2772
+ agent = Agent(NodeConfig(node_type=NodeType.AGENT), uuid="agent")
2773
+ agent._append_history(ErrorEntry(content="LLMProviderError: provider offline"))
2672
2774
 
2673
2775
  messages = agent._build_messages()
2674
2776
 
2675
2777
  assert any(
2676
2778
  msg.get("role") == "user"
2677
2779
  and msg.get("content")
2678
- == "<system>Previous runtime error:\nRuntimeError: boom\n\ntraceback</system>"
2780
+ == (
2781
+ "<system>Previous runtime error:\n"
2782
+ "LLMProviderError: provider offline</system>"
2783
+ )
2679
2784
  for msg in messages
2680
2785
  )
2681
2786
 
2682
2787
 
2788
+ def test_unhandled_runtime_error_history_omits_traceback(monkeypatch):
2789
+ agent = Agent(NodeConfig(node_type=NodeType.AGENT, allow_network=True))
2790
+ wait_calls = 0
2791
+ chat_calls = 0
2792
+
2793
+ def fake_wait_for_input() -> None:
2794
+ nonlocal wait_calls
2795
+ wait_calls += 1
2796
+ if wait_calls == 1:
2797
+ agent._append_history(ReceivedMessage(content="start", from_id="human"))
2798
+ agent.set_state(AgentState.RUNNING, "received message from human")
2799
+ return
2800
+ agent.request_termination("done")
2801
+
2802
+ def fake_chat(
2803
+ messages,
2804
+ tools=None,
2805
+ on_chunk=None,
2806
+ register_interrupt=None,
2807
+ role_name=None,
2808
+ ):
2809
+ nonlocal chat_calls
2810
+ chat_calls += 1
2811
+ raise RuntimeError("boom")
2812
+
2813
+ monkeypatch.setattr(agent, "_wait_for_input", fake_wait_for_input)
2814
+ monkeypatch.setattr("flowent.agent.gateway.chat", fake_chat)
2815
+
2816
+ agent._run()
2817
+
2818
+ assert chat_calls == 1
2819
+ assert any(
2820
+ isinstance(entry, ErrorEntry) and entry.content == "RuntimeError: boom"
2821
+ for entry in agent.get_history_snapshot()
2822
+ )
2823
+ assert not any(
2824
+ isinstance(entry, ErrorEntry) and "traceback" in entry.content.lower()
2825
+ for entry in agent.get_history_snapshot()
2826
+ )
2827
+
2828
+
2683
2829
  def test_assistant_emits_human_content_for_plain_text_with_target_like_prefix(
2684
2830
  monkeypatch,
2685
2831
  ):
@@ -80,25 +80,3 @@ def test_update_prompts_allows_custom_post_prompt_only(monkeypatch):
80
80
  "custom_prompt": "Keep this.",
81
81
  "custom_post_prompt": "Append this after history.",
82
82
  }
83
-
84
-
85
- def test_update_prompts_accepts_legacy_post_prompt_alias(monkeypatch):
86
- settings = Settings(custom_prompt="Keep this.", custom_post_prompt="")
87
-
88
- monkeypatch.setattr("flowent.routes.prompts.get_settings", lambda: settings)
89
- monkeypatch.setattr("flowent.routes.prompts.save_settings", lambda current: None)
90
-
91
- result = asyncio.run(
92
- update_prompts(
93
- UpdatePromptSettingsRequest.model_validate(
94
- {"post_prompt": "Append this after history."}
95
- )
96
- )
97
- )
98
-
99
- assert settings.custom_prompt == "Keep this."
100
- assert settings.custom_post_prompt == "Append this after history."
101
- assert result.model_dump() == {
102
- "custom_prompt": "Keep this.",
103
- "custom_post_prompt": "Append this after history.",
104
- }