flock-core 0.5.0b28__py3-none-any.whl → 0.5.0b51__py3-none-any.whl

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.

Potentially problematic release.


This version of flock-core might be problematic. Click here for more details.

Files changed (469) hide show
  1. flock/__init__.py +12 -217
  2. flock/agent.py +678 -0
  3. flock/api/themes.py +71 -0
  4. flock/artifacts.py +79 -0
  5. flock/cli.py +75 -0
  6. flock/components.py +173 -0
  7. flock/dashboard/__init__.py +28 -0
  8. flock/dashboard/collector.py +283 -0
  9. flock/dashboard/events.py +182 -0
  10. flock/dashboard/launcher.py +230 -0
  11. flock/dashboard/service.py +537 -0
  12. flock/dashboard/websocket.py +235 -0
  13. flock/engines/__init__.py +6 -0
  14. flock/engines/dspy_engine.py +856 -0
  15. flock/examples.py +128 -0
  16. flock/frontend/README.md +678 -0
  17. flock/frontend/docs/DESIGN_SYSTEM.md +1980 -0
  18. flock/frontend/index.html +12 -0
  19. flock/frontend/package-lock.json +4347 -0
  20. flock/frontend/package.json +48 -0
  21. flock/frontend/src/App.tsx +79 -0
  22. flock/frontend/src/__tests__/e2e/critical-scenarios.test.tsx +587 -0
  23. flock/frontend/src/__tests__/integration/filtering-e2e.test.tsx +387 -0
  24. flock/frontend/src/__tests__/integration/graph-rendering.test.tsx +640 -0
  25. flock/frontend/src/__tests__/integration/indexeddb-persistence.test.tsx +699 -0
  26. flock/frontend/src/components/common/BuildInfo.tsx +39 -0
  27. flock/frontend/src/components/common/EmptyState.module.css +115 -0
  28. flock/frontend/src/components/common/EmptyState.tsx +128 -0
  29. flock/frontend/src/components/common/ErrorBoundary.module.css +169 -0
  30. flock/frontend/src/components/common/ErrorBoundary.tsx +118 -0
  31. flock/frontend/src/components/common/KeyboardShortcutsDialog.css +251 -0
  32. flock/frontend/src/components/common/KeyboardShortcutsDialog.tsx +151 -0
  33. flock/frontend/src/components/common/LoadingSpinner.module.css +97 -0
  34. flock/frontend/src/components/common/LoadingSpinner.tsx +29 -0
  35. flock/frontend/src/components/controls/PublishControl.css +547 -0
  36. flock/frontend/src/components/controls/PublishControl.test.tsx +543 -0
  37. flock/frontend/src/components/controls/PublishControl.tsx +432 -0
  38. flock/frontend/src/components/details/DetailWindowContainer.tsx +62 -0
  39. flock/frontend/src/components/details/LiveOutputTab.test.tsx +792 -0
  40. flock/frontend/src/components/details/LiveOutputTab.tsx +220 -0
  41. flock/frontend/src/components/details/MessageHistoryTab.tsx +299 -0
  42. flock/frontend/src/components/details/NodeDetailWindow.test.tsx +501 -0
  43. flock/frontend/src/components/details/NodeDetailWindow.tsx +218 -0
  44. flock/frontend/src/components/details/RunStatusTab.tsx +307 -0
  45. flock/frontend/src/components/details/tabs.test.tsx +1015 -0
  46. flock/frontend/src/components/filters/CorrelationIDFilter.module.css +102 -0
  47. flock/frontend/src/components/filters/CorrelationIDFilter.test.tsx +197 -0
  48. flock/frontend/src/components/filters/CorrelationIDFilter.tsx +121 -0
  49. flock/frontend/src/components/filters/FilterBar.module.css +29 -0
  50. flock/frontend/src/components/filters/FilterBar.test.tsx +133 -0
  51. flock/frontend/src/components/filters/FilterBar.tsx +33 -0
  52. flock/frontend/src/components/filters/FilterPills.module.css +79 -0
  53. flock/frontend/src/components/filters/FilterPills.test.tsx +173 -0
  54. flock/frontend/src/components/filters/FilterPills.tsx +67 -0
  55. flock/frontend/src/components/filters/TimeRangeFilter.module.css +91 -0
  56. flock/frontend/src/components/filters/TimeRangeFilter.test.tsx +154 -0
  57. flock/frontend/src/components/filters/TimeRangeFilter.tsx +105 -0
  58. flock/frontend/src/components/graph/AgentNode.test.tsx +75 -0
  59. flock/frontend/src/components/graph/AgentNode.tsx +322 -0
  60. flock/frontend/src/components/graph/GraphCanvas.tsx +406 -0
  61. flock/frontend/src/components/graph/MessageFlowEdge.tsx +128 -0
  62. flock/frontend/src/components/graph/MessageNode.test.tsx +62 -0
  63. flock/frontend/src/components/graph/MessageNode.tsx +116 -0
  64. flock/frontend/src/components/graph/MiniMap.tsx +47 -0
  65. flock/frontend/src/components/graph/TransformEdge.tsx +123 -0
  66. flock/frontend/src/components/layout/DashboardLayout.css +407 -0
  67. flock/frontend/src/components/layout/DashboardLayout.tsx +300 -0
  68. flock/frontend/src/components/layout/Header.module.css +88 -0
  69. flock/frontend/src/components/layout/Header.tsx +52 -0
  70. flock/frontend/src/components/modules/EventLogModule.test.tsx +401 -0
  71. flock/frontend/src/components/modules/EventLogModule.tsx +396 -0
  72. flock/frontend/src/components/modules/EventLogModuleWrapper.tsx +17 -0
  73. flock/frontend/src/components/modules/ModuleRegistry.test.ts +333 -0
  74. flock/frontend/src/components/modules/ModuleRegistry.ts +85 -0
  75. flock/frontend/src/components/modules/ModuleWindow.tsx +155 -0
  76. flock/frontend/src/components/modules/registerModules.ts +20 -0
  77. flock/frontend/src/components/settings/AdvancedSettings.tsx +175 -0
  78. flock/frontend/src/components/settings/AppearanceSettings.tsx +185 -0
  79. flock/frontend/src/components/settings/GraphSettings.tsx +110 -0
  80. flock/frontend/src/components/settings/SettingsPanel.css +327 -0
  81. flock/frontend/src/components/settings/SettingsPanel.tsx +131 -0
  82. flock/frontend/src/components/settings/ThemeSelector.tsx +298 -0
  83. flock/frontend/src/hooks/useKeyboardShortcuts.ts +148 -0
  84. flock/frontend/src/hooks/useModulePersistence.test.ts +442 -0
  85. flock/frontend/src/hooks/useModulePersistence.ts +154 -0
  86. flock/frontend/src/hooks/useModules.ts +139 -0
  87. flock/frontend/src/hooks/usePersistence.ts +139 -0
  88. flock/frontend/src/main.tsx +13 -0
  89. flock/frontend/src/services/api.ts +213 -0
  90. flock/frontend/src/services/indexeddb.test.ts +793 -0
  91. flock/frontend/src/services/indexeddb.ts +794 -0
  92. flock/frontend/src/services/layout.test.ts +437 -0
  93. flock/frontend/src/services/layout.ts +146 -0
  94. flock/frontend/src/services/themeApplicator.ts +140 -0
  95. flock/frontend/src/services/themeService.ts +77 -0
  96. flock/frontend/src/services/websocket.test.ts +595 -0
  97. flock/frontend/src/services/websocket.ts +685 -0
  98. flock/frontend/src/store/filterStore.test.ts +242 -0
  99. flock/frontend/src/store/filterStore.ts +103 -0
  100. flock/frontend/src/store/graphStore.test.ts +186 -0
  101. flock/frontend/src/store/graphStore.ts +414 -0
  102. flock/frontend/src/store/moduleStore.test.ts +253 -0
  103. flock/frontend/src/store/moduleStore.ts +57 -0
  104. flock/frontend/src/store/settingsStore.ts +188 -0
  105. flock/frontend/src/store/streamStore.ts +68 -0
  106. flock/frontend/src/store/uiStore.test.ts +54 -0
  107. flock/frontend/src/store/uiStore.ts +110 -0
  108. flock/frontend/src/store/wsStore.ts +34 -0
  109. flock/frontend/src/styles/index.css +15 -0
  110. flock/frontend/src/styles/scrollbar.css +47 -0
  111. flock/frontend/src/styles/variables.css +488 -0
  112. flock/frontend/src/test/setup.ts +1 -0
  113. flock/frontend/src/types/filters.ts +14 -0
  114. flock/frontend/src/types/graph.ts +55 -0
  115. flock/frontend/src/types/modules.ts +7 -0
  116. flock/frontend/src/types/theme.ts +55 -0
  117. flock/frontend/src/utils/mockData.ts +85 -0
  118. flock/frontend/src/utils/performance.ts +16 -0
  119. flock/frontend/src/utils/transforms.test.ts +860 -0
  120. flock/frontend/src/utils/transforms.ts +323 -0
  121. flock/frontend/src/vite-env.d.ts +17 -0
  122. flock/frontend/tsconfig.json +27 -0
  123. flock/frontend/tsconfig.node.json +11 -0
  124. flock/frontend/vite.config.ts +25 -0
  125. flock/frontend/vitest.config.ts +11 -0
  126. flock/{core/util → helper}/cli_helper.py +4 -3
  127. flock/{core/logging → logging}/__init__.py +2 -3
  128. flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
  129. flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
  130. flock/{core/logging → logging}/formatters/themed_formatter.py +69 -115
  131. flock/{core/logging → logging}/logging.py +77 -61
  132. flock/{core/logging → logging}/telemetry.py +20 -26
  133. flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
  134. flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +6 -9
  135. flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
  136. flock/{core/logging → logging}/trace_and_logged.py +20 -24
  137. flock/mcp/__init__.py +91 -0
  138. flock/{core/mcp/mcp_client.py → mcp/client.py} +103 -154
  139. flock/{core/mcp/mcp_config.py → mcp/config.py} +62 -117
  140. flock/mcp/manager.py +255 -0
  141. flock/mcp/servers/sse/__init__.py +1 -1
  142. flock/mcp/servers/sse/flock_sse_server.py +11 -53
  143. flock/mcp/servers/stdio/__init__.py +1 -1
  144. flock/mcp/servers/stdio/flock_stdio_server.py +8 -48
  145. flock/mcp/servers/streamable_http/flock_streamable_http_server.py +17 -62
  146. flock/mcp/servers/websockets/flock_websocket_server.py +7 -40
  147. flock/{core/mcp/flock_mcp_tool.py → mcp/tool.py} +16 -26
  148. flock/mcp/types/__init__.py +42 -0
  149. flock/{core/mcp → mcp}/types/callbacks.py +9 -15
  150. flock/{core/mcp → mcp}/types/factories.py +7 -6
  151. flock/{core/mcp → mcp}/types/handlers.py +13 -18
  152. flock/{core/mcp → mcp}/types/types.py +70 -74
  153. flock/{core/mcp → mcp}/util/helpers.py +1 -1
  154. flock/orchestrator.py +645 -0
  155. flock/registry.py +148 -0
  156. flock/runtime.py +262 -0
  157. flock/service.py +140 -0
  158. flock/store.py +69 -0
  159. flock/subscription.py +111 -0
  160. flock/themes/andromeda.toml +1 -1
  161. flock/themes/apple-system-colors.toml +1 -1
  162. flock/themes/arcoiris.toml +1 -1
  163. flock/themes/atomonelight.toml +1 -1
  164. flock/themes/ayu copy.toml +1 -1
  165. flock/themes/ayu-light.toml +1 -1
  166. flock/themes/belafonte-day.toml +1 -1
  167. flock/themes/belafonte-night.toml +1 -1
  168. flock/themes/blulocodark.toml +1 -1
  169. flock/themes/breeze.toml +1 -1
  170. flock/themes/broadcast.toml +1 -1
  171. flock/themes/brogrammer.toml +1 -1
  172. flock/themes/builtin-dark.toml +1 -1
  173. flock/themes/builtin-pastel-dark.toml +1 -1
  174. flock/themes/catppuccin-latte.toml +1 -1
  175. flock/themes/catppuccin-macchiato.toml +1 -1
  176. flock/themes/catppuccin-mocha.toml +1 -1
  177. flock/themes/cga.toml +1 -1
  178. flock/themes/chalk.toml +1 -1
  179. flock/themes/ciapre.toml +1 -1
  180. flock/themes/coffee-theme.toml +1 -1
  181. flock/themes/cyberpunkscarletprotocol.toml +1 -1
  182. flock/themes/dark+.toml +1 -1
  183. flock/themes/darkermatrix.toml +1 -1
  184. flock/themes/darkside.toml +1 -1
  185. flock/themes/desert.toml +1 -1
  186. flock/themes/django.toml +1 -1
  187. flock/themes/djangosmooth.toml +1 -1
  188. flock/themes/doomone.toml +1 -1
  189. flock/themes/dotgov.toml +1 -1
  190. flock/themes/dracula+.toml +1 -1
  191. flock/themes/duckbones.toml +1 -1
  192. flock/themes/encom.toml +1 -1
  193. flock/themes/espresso.toml +1 -1
  194. flock/themes/everblush.toml +1 -1
  195. flock/themes/fairyfloss.toml +1 -1
  196. flock/themes/fideloper.toml +1 -1
  197. flock/themes/fishtank.toml +1 -1
  198. flock/themes/flexoki-light.toml +1 -1
  199. flock/themes/floraverse.toml +1 -1
  200. flock/themes/framer.toml +1 -1
  201. flock/themes/galizur.toml +1 -1
  202. flock/themes/github.toml +1 -1
  203. flock/themes/grass.toml +1 -1
  204. flock/themes/grey-green.toml +1 -1
  205. flock/themes/gruvboxlight.toml +1 -1
  206. flock/themes/guezwhoz.toml +1 -1
  207. flock/themes/harper.toml +1 -1
  208. flock/themes/hax0r-blue.toml +1 -1
  209. flock/themes/hopscotch.256.toml +1 -1
  210. flock/themes/ic-green-ppl.toml +1 -1
  211. flock/themes/iceberg-dark.toml +1 -1
  212. flock/themes/japanesque.toml +1 -1
  213. flock/themes/jubi.toml +1 -1
  214. flock/themes/kibble.toml +1 -1
  215. flock/themes/kolorit.toml +1 -1
  216. flock/themes/kurokula.toml +1 -1
  217. flock/themes/materialdesigncolors.toml +1 -1
  218. flock/themes/matrix.toml +1 -1
  219. flock/themes/mellifluous.toml +1 -1
  220. flock/themes/midnight-in-mojave.toml +1 -1
  221. flock/themes/monokai-remastered.toml +1 -1
  222. flock/themes/monokai-soda.toml +1 -1
  223. flock/themes/neon.toml +1 -1
  224. flock/themes/neopolitan.toml +1 -1
  225. flock/themes/nord-light.toml +1 -1
  226. flock/themes/ocean.toml +1 -1
  227. flock/themes/onehalfdark.toml +1 -1
  228. flock/themes/onehalflight.toml +1 -1
  229. flock/themes/palenighthc.toml +1 -1
  230. flock/themes/paulmillr.toml +1 -1
  231. flock/themes/pencildark.toml +1 -1
  232. flock/themes/pnevma.toml +1 -1
  233. flock/themes/purple-rain.toml +1 -1
  234. flock/themes/purplepeter.toml +1 -1
  235. flock/themes/raycast-dark.toml +1 -1
  236. flock/themes/red-sands.toml +1 -1
  237. flock/themes/relaxed.toml +1 -1
  238. flock/themes/retro.toml +1 -1
  239. flock/themes/rose-pine.toml +1 -1
  240. flock/themes/royal.toml +1 -1
  241. flock/themes/ryuuko.toml +1 -1
  242. flock/themes/sakura.toml +1 -1
  243. flock/themes/scarlet-protocol.toml +1 -1
  244. flock/themes/seoulbones-dark.toml +1 -1
  245. flock/themes/shades-of-purple.toml +1 -1
  246. flock/themes/smyck.toml +1 -1
  247. flock/themes/softserver.toml +1 -1
  248. flock/themes/solarized-darcula.toml +1 -1
  249. flock/themes/square.toml +1 -1
  250. flock/themes/sugarplum.toml +1 -1
  251. flock/themes/thayer-bright.toml +1 -1
  252. flock/themes/tokyonight.toml +1 -1
  253. flock/themes/tomorrow.toml +1 -1
  254. flock/themes/ubuntu.toml +1 -1
  255. flock/themes/ultradark.toml +1 -1
  256. flock/themes/ultraviolent.toml +1 -1
  257. flock/themes/unikitty.toml +1 -1
  258. flock/themes/urple.toml +1 -1
  259. flock/themes/vesper.toml +1 -1
  260. flock/themes/vimbones.toml +1 -1
  261. flock/themes/wildcherry.toml +1 -1
  262. flock/themes/wilmersdorf.toml +1 -1
  263. flock/themes/wryan.toml +1 -1
  264. flock/themes/xcodedarkhc.toml +1 -1
  265. flock/themes/xcodelight.toml +1 -1
  266. flock/themes/zenbones-light.toml +1 -1
  267. flock/themes/zenwritten-dark.toml +1 -1
  268. flock/utilities.py +301 -0
  269. flock/{components/utility → utility}/output_utility_component.py +68 -53
  270. flock/visibility.py +107 -0
  271. flock_core-0.5.0b51.dist-info/METADATA +747 -0
  272. flock_core-0.5.0b51.dist-info/RECORD +508 -0
  273. flock_core-0.5.0b51.dist-info/entry_points.txt +2 -0
  274. {flock_core-0.5.0b28.dist-info → flock_core-0.5.0b51.dist-info}/licenses/LICENSE +1 -1
  275. flock/adapter/__init__.py +0 -14
  276. flock/adapter/azure_adapter.py +0 -68
  277. flock/adapter/chroma_adapter.py +0 -73
  278. flock/adapter/faiss_adapter.py +0 -97
  279. flock/adapter/pinecone_adapter.py +0 -51
  280. flock/adapter/vector_base.py +0 -47
  281. flock/cli/assets/release_notes.md +0 -140
  282. flock/cli/config.py +0 -8
  283. flock/cli/constants.py +0 -36
  284. flock/cli/create_agent.py +0 -1
  285. flock/cli/create_flock.py +0 -280
  286. flock/cli/execute_flock.py +0 -620
  287. flock/cli/load_agent.py +0 -1
  288. flock/cli/load_examples.py +0 -1
  289. flock/cli/load_flock.py +0 -192
  290. flock/cli/load_release_notes.py +0 -20
  291. flock/cli/loaded_flock_cli.py +0 -254
  292. flock/cli/manage_agents.py +0 -459
  293. flock/cli/registry_management.py +0 -889
  294. flock/cli/runner.py +0 -41
  295. flock/cli/settings.py +0 -857
  296. flock/cli/utils.py +0 -135
  297. flock/cli/view_results.py +0 -29
  298. flock/cli/yaml_editor.py +0 -396
  299. flock/components/__init__.py +0 -30
  300. flock/components/evaluation/__init__.py +0 -9
  301. flock/components/evaluation/declarative_evaluation_component.py +0 -606
  302. flock/components/routing/__init__.py +0 -15
  303. flock/components/routing/conditional_routing_component.py +0 -494
  304. flock/components/routing/default_routing_component.py +0 -103
  305. flock/components/routing/llm_routing_component.py +0 -206
  306. flock/components/utility/__init__.py +0 -22
  307. flock/components/utility/example_utility_component.py +0 -250
  308. flock/components/utility/feedback_utility_component.py +0 -206
  309. flock/components/utility/memory_utility_component.py +0 -550
  310. flock/components/utility/metrics_utility_component.py +0 -700
  311. flock/config.py +0 -61
  312. flock/core/__init__.py +0 -110
  313. flock/core/agent/__init__.py +0 -16
  314. flock/core/agent/default_agent.py +0 -216
  315. flock/core/agent/flock_agent_components.py +0 -104
  316. flock/core/agent/flock_agent_execution.py +0 -101
  317. flock/core/agent/flock_agent_integration.py +0 -260
  318. flock/core/agent/flock_agent_lifecycle.py +0 -186
  319. flock/core/agent/flock_agent_serialization.py +0 -381
  320. flock/core/api/__init__.py +0 -10
  321. flock/core/api/custom_endpoint.py +0 -45
  322. flock/core/api/endpoints.py +0 -254
  323. flock/core/api/main.py +0 -162
  324. flock/core/api/models.py +0 -97
  325. flock/core/api/run_store.py +0 -224
  326. flock/core/api/runner.py +0 -44
  327. flock/core/api/service.py +0 -214
  328. flock/core/component/__init__.py +0 -15
  329. flock/core/component/agent_component_base.py +0 -309
  330. flock/core/component/evaluation_component.py +0 -62
  331. flock/core/component/routing_component.py +0 -74
  332. flock/core/component/utility_component.py +0 -69
  333. flock/core/config/flock_agent_config.py +0 -58
  334. flock/core/config/scheduled_agent_config.py +0 -40
  335. flock/core/context/context.py +0 -213
  336. flock/core/context/context_manager.py +0 -37
  337. flock/core/context/context_vars.py +0 -10
  338. flock/core/evaluation/utils.py +0 -396
  339. flock/core/execution/batch_executor.py +0 -369
  340. flock/core/execution/evaluation_executor.py +0 -438
  341. flock/core/execution/local_executor.py +0 -31
  342. flock/core/execution/opik_executor.py +0 -103
  343. flock/core/execution/temporal_executor.py +0 -164
  344. flock/core/flock.py +0 -634
  345. flock/core/flock_agent.py +0 -336
  346. flock/core/flock_factory.py +0 -613
  347. flock/core/flock_scheduler.py +0 -166
  348. flock/core/flock_server_manager.py +0 -136
  349. flock/core/interpreter/python_interpreter.py +0 -689
  350. flock/core/mcp/__init__.py +0 -1
  351. flock/core/mcp/flock_mcp_server.py +0 -680
  352. flock/core/mcp/mcp_client_manager.py +0 -201
  353. flock/core/mcp/types/__init__.py +0 -1
  354. flock/core/mixin/dspy_integration.py +0 -403
  355. flock/core/mixin/prompt_parser.py +0 -125
  356. flock/core/orchestration/__init__.py +0 -15
  357. flock/core/orchestration/flock_batch_processor.py +0 -94
  358. flock/core/orchestration/flock_evaluator.py +0 -113
  359. flock/core/orchestration/flock_execution.py +0 -295
  360. flock/core/orchestration/flock_initialization.py +0 -149
  361. flock/core/orchestration/flock_server_manager.py +0 -67
  362. flock/core/orchestration/flock_web_server.py +0 -117
  363. flock/core/registry/__init__.py +0 -45
  364. flock/core/registry/agent_registry.py +0 -69
  365. flock/core/registry/callable_registry.py +0 -139
  366. flock/core/registry/component_discovery.py +0 -142
  367. flock/core/registry/component_registry.py +0 -64
  368. flock/core/registry/config_mapping.py +0 -64
  369. flock/core/registry/decorators.py +0 -137
  370. flock/core/registry/registry_hub.py +0 -205
  371. flock/core/registry/server_registry.py +0 -57
  372. flock/core/registry/type_registry.py +0 -86
  373. flock/core/serialization/__init__.py +0 -13
  374. flock/core/serialization/callable_registry.py +0 -52
  375. flock/core/serialization/flock_serializer.py +0 -832
  376. flock/core/serialization/json_encoder.py +0 -41
  377. flock/core/serialization/secure_serializer.py +0 -175
  378. flock/core/serialization/serializable.py +0 -342
  379. flock/core/serialization/serialization_utils.py +0 -412
  380. flock/core/util/file_path_utils.py +0 -223
  381. flock/core/util/hydrator.py +0 -309
  382. flock/core/util/input_resolver.py +0 -164
  383. flock/core/util/loader.py +0 -59
  384. flock/core/util/splitter.py +0 -219
  385. flock/di.py +0 -27
  386. flock/platform/docker_tools.py +0 -49
  387. flock/platform/jaeger_install.py +0 -86
  388. flock/webapp/__init__.py +0 -1
  389. flock/webapp/app/__init__.py +0 -0
  390. flock/webapp/app/api/__init__.py +0 -0
  391. flock/webapp/app/api/agent_management.py +0 -241
  392. flock/webapp/app/api/execution.py +0 -709
  393. flock/webapp/app/api/flock_management.py +0 -129
  394. flock/webapp/app/api/registry_viewer.py +0 -30
  395. flock/webapp/app/chat.py +0 -665
  396. flock/webapp/app/config.py +0 -104
  397. flock/webapp/app/dependencies.py +0 -117
  398. flock/webapp/app/main.py +0 -1070
  399. flock/webapp/app/middleware.py +0 -113
  400. flock/webapp/app/models_ui.py +0 -7
  401. flock/webapp/app/services/__init__.py +0 -0
  402. flock/webapp/app/services/feedback_file_service.py +0 -363
  403. flock/webapp/app/services/flock_service.py +0 -337
  404. flock/webapp/app/services/sharing_models.py +0 -81
  405. flock/webapp/app/services/sharing_store.py +0 -762
  406. flock/webapp/app/templates/theme_mapper.html +0 -326
  407. flock/webapp/app/theme_mapper.py +0 -812
  408. flock/webapp/app/utils.py +0 -85
  409. flock/webapp/run.py +0 -215
  410. flock/webapp/static/css/chat.css +0 -301
  411. flock/webapp/static/css/components.css +0 -167
  412. flock/webapp/static/css/header.css +0 -39
  413. flock/webapp/static/css/layout.css +0 -46
  414. flock/webapp/static/css/sidebar.css +0 -127
  415. flock/webapp/static/css/two-pane.css +0 -48
  416. flock/webapp/templates/base.html +0 -200
  417. flock/webapp/templates/chat.html +0 -152
  418. flock/webapp/templates/chat_settings.html +0 -19
  419. flock/webapp/templates/flock_editor.html +0 -16
  420. flock/webapp/templates/index.html +0 -12
  421. flock/webapp/templates/partials/_agent_detail_form.html +0 -93
  422. flock/webapp/templates/partials/_agent_list.html +0 -18
  423. flock/webapp/templates/partials/_agent_manager_view.html +0 -51
  424. flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
  425. flock/webapp/templates/partials/_chat_container.html +0 -15
  426. flock/webapp/templates/partials/_chat_messages.html +0 -57
  427. flock/webapp/templates/partials/_chat_settings_form.html +0 -85
  428. flock/webapp/templates/partials/_create_flock_form.html +0 -50
  429. flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
  430. flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
  431. flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
  432. flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
  433. flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
  434. flock/webapp/templates/partials/_env_vars_table.html +0 -23
  435. flock/webapp/templates/partials/_execution_form.html +0 -118
  436. flock/webapp/templates/partials/_execution_view_container.html +0 -28
  437. flock/webapp/templates/partials/_flock_file_list.html +0 -23
  438. flock/webapp/templates/partials/_flock_properties_form.html +0 -52
  439. flock/webapp/templates/partials/_flock_upload_form.html +0 -16
  440. flock/webapp/templates/partials/_header_flock_status.html +0 -5
  441. flock/webapp/templates/partials/_load_manager_view.html +0 -49
  442. flock/webapp/templates/partials/_registry_table.html +0 -25
  443. flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
  444. flock/webapp/templates/partials/_results_display.html +0 -78
  445. flock/webapp/templates/partials/_settings_env_content.html +0 -9
  446. flock/webapp/templates/partials/_settings_theme_content.html +0 -14
  447. flock/webapp/templates/partials/_settings_view.html +0 -36
  448. flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
  449. flock/webapp/templates/partials/_share_link_snippet.html +0 -35
  450. flock/webapp/templates/partials/_sidebar.html +0 -74
  451. flock/webapp/templates/partials/_streaming_results_container.html +0 -195
  452. flock/webapp/templates/partials/_structured_data_view.html +0 -40
  453. flock/webapp/templates/partials/_theme_preview.html +0 -36
  454. flock/webapp/templates/registry_viewer.html +0 -84
  455. flock/webapp/templates/shared_run_page.html +0 -140
  456. flock/workflow/__init__.py +0 -0
  457. flock/workflow/activities.py +0 -196
  458. flock/workflow/agent_activities.py +0 -24
  459. flock/workflow/agent_execution_activity.py +0 -202
  460. flock/workflow/flock_workflow.py +0 -214
  461. flock/workflow/temporal_config.py +0 -96
  462. flock/workflow/temporal_setup.py +0 -68
  463. flock_core-0.5.0b28.dist-info/METADATA +0 -274
  464. flock_core-0.5.0b28.dist-info/RECORD +0 -561
  465. flock_core-0.5.0b28.dist-info/entry_points.txt +0 -2
  466. /flock/{core/logging → logging}/formatters/themes.py +0 -0
  467. /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
  468. /flock/{core/mcp → mcp}/util/__init__.py +0 -0
  469. {flock_core-0.5.0b28.dist-info → flock_core-0.5.0b51.dist-info}/WHEEL +0 -0
@@ -0,0 +1,595 @@
1
+ /**
2
+ * Unit tests for WebSocket client service.
3
+ *
4
+ * Tests verify connection management, reconnection with exponential backoff,
5
+ * message buffering, event handling, and heartbeat/pong handling.
6
+ */
7
+
8
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
9
+
10
+ // Mock WebSocket
11
+ class MockWebSocket {
12
+ url: string;
13
+ readyState: number;
14
+ onopen: ((event: Event) => void) | null = null;
15
+ onclose: ((event: CloseEvent) => void) | null = null;
16
+ onerror: ((event: Event) => void) | null = null;
17
+ onmessage: ((event: MessageEvent) => void) | null = null;
18
+
19
+ static CONNECTING = 0;
20
+ static OPEN = 1;
21
+ static CLOSING = 2;
22
+ static CLOSED = 3;
23
+
24
+ sentMessages: string[] = [];
25
+ private autoConnectTimerId: any = null;
26
+ private errorSimulated = false;
27
+ static skipAutoConnect = false; // Global flag for controlling auto-connect
28
+
29
+ constructor(url: string) {
30
+ this.url = url;
31
+ this.readyState = MockWebSocket.CONNECTING;
32
+
33
+ // Simulate connection opening (can be prevented by simulateError or global flag)
34
+ if (!MockWebSocket.skipAutoConnect) {
35
+ this.autoConnectTimerId = setTimeout(() => {
36
+ if (!this.errorSimulated && this.readyState === MockWebSocket.CONNECTING) {
37
+ this.readyState = MockWebSocket.OPEN;
38
+ if (this.onopen) {
39
+ this.onopen(new Event('open'));
40
+ }
41
+ }
42
+ }, 0);
43
+ }
44
+ }
45
+
46
+ // Method to manually trigger connection success (for timeout tests)
47
+ manuallyConnect(): void {
48
+ if (this.readyState === MockWebSocket.CONNECTING) {
49
+ this.readyState = MockWebSocket.OPEN;
50
+ if (this.onopen) {
51
+ this.onopen(new Event('open'));
52
+ }
53
+ }
54
+ }
55
+
56
+ send(data: string): void {
57
+ if (this.readyState !== MockWebSocket.OPEN) {
58
+ throw new Error('WebSocket is not open');
59
+ }
60
+ this.sentMessages.push(data);
61
+ }
62
+
63
+ close(code?: number, reason?: string): void {
64
+ this.readyState = MockWebSocket.CLOSING;
65
+ setTimeout(() => {
66
+ this.readyState = MockWebSocket.CLOSED;
67
+ if (this.onclose) {
68
+ this.onclose(new CloseEvent('close', { code: code || 1000, reason }));
69
+ }
70
+ }, 0);
71
+ }
72
+
73
+ // Test helpers
74
+ simulateMessage(data: any): void {
75
+ if (this.onmessage) {
76
+ this.onmessage(new MessageEvent('message', { data: JSON.stringify(data) }));
77
+ }
78
+ }
79
+
80
+ simulateError(): void {
81
+ this.errorSimulated = true;
82
+ // Clear auto-connect timer if still pending
83
+ if (this.autoConnectTimerId) {
84
+ clearTimeout(this.autoConnectTimerId);
85
+ this.autoConnectTimerId = null;
86
+ }
87
+ // Transition to closed state
88
+ this.readyState = MockWebSocket.CLOSED;
89
+ if (this.onerror) {
90
+ this.onerror(new Event('error'));
91
+ }
92
+ // Also trigger onclose after error
93
+ setTimeout(() => {
94
+ if (this.onclose) {
95
+ this.onclose(new CloseEvent('close', { code: 1006, reason: 'Connection error' }));
96
+ }
97
+ }, 0);
98
+ }
99
+
100
+ simulateClose(code: number = 1000): void {
101
+ this.readyState = MockWebSocket.CLOSED;
102
+ if (this.onclose) {
103
+ this.onclose(new CloseEvent('close', { code }));
104
+ }
105
+ }
106
+ }
107
+
108
+ // Replace global WebSocket with mock
109
+ (globalThis as any).WebSocket = MockWebSocket;
110
+
111
+ describe('WebSocketClient', () => {
112
+ let WebSocketClient: any;
113
+ let client: any;
114
+ let mockStore: any;
115
+
116
+ beforeEach(async () => {
117
+ // Reset timers
118
+ vi.useFakeTimers();
119
+
120
+ // Mock store for event dispatching
121
+ mockStore = {
122
+ addAgent: vi.fn(),
123
+ updateAgent: vi.fn(),
124
+ addMessage: vi.fn(),
125
+ batchUpdate: vi.fn(),
126
+ };
127
+
128
+ // Dynamic import to avoid module-level errors before implementation
129
+ try {
130
+ const module = await import('./websocket');
131
+ WebSocketClient = module.WebSocketClient;
132
+ client = new WebSocketClient('ws://localhost:8080/ws', mockStore);
133
+ } catch (error) {
134
+ // Skip tests if WebSocketClient not implemented yet (TDD approach)
135
+ throw new Error('WebSocketClient not implemented yet - skipping tests');
136
+ }
137
+ });
138
+
139
+ afterEach(() => {
140
+ if (client) {
141
+ client.disconnect();
142
+ }
143
+ vi.clearAllTimers();
144
+ vi.useRealTimers();
145
+ });
146
+
147
+ it('should connect to WebSocket server', async () => {
148
+ // Connect
149
+ client.connect();
150
+
151
+ // Wait for connection
152
+ await vi.runAllTimersAsync();
153
+
154
+ // Verify connection status
155
+ expect(client.isConnected()).toBe(true);
156
+ expect(client.getConnectionStatus()).toBe('connected');
157
+ });
158
+
159
+ it('should handle connection failure', async () => {
160
+ // Prevent auto-reconnect for this test
161
+ MockWebSocket.skipAutoConnect = true;
162
+
163
+ // Connect
164
+ client.connect();
165
+
166
+ // Simulate connection error before connection succeeds
167
+ const ws = client.ws as MockWebSocket;
168
+ ws.simulateError();
169
+
170
+ // Wait for error handling (just the error, not reconnection)
171
+ await vi.advanceTimersByTimeAsync(100);
172
+
173
+ // Verify connection status
174
+ expect(client.isConnected()).toBe(false);
175
+ expect(client.getConnectionStatus()).toBe('error');
176
+
177
+ // Reset flag
178
+ MockWebSocket.skipAutoConnect = false;
179
+ });
180
+
181
+ it('should reconnect with exponential backoff (1s, 2s, 4s, 8s, max 30s)', async () => {
182
+ // Connect
183
+ client.connect();
184
+ await vi.runAllTimersAsync();
185
+
186
+ // Simulate disconnection
187
+ (client.ws as MockWebSocket).simulateClose(1006); // Abnormal closure
188
+
189
+ // Track reconnection attempts
190
+ const reconnectTimes: number[] = [];
191
+
192
+ // Override connect to track timing
193
+ const originalConnect = client.connect.bind(client);
194
+ client.connect = vi.fn(() => {
195
+ reconnectTimes.push(Date.now());
196
+ originalConnect();
197
+ });
198
+
199
+ // Wait for first reconnection attempt (1s)
200
+ await vi.advanceTimersByTimeAsync(1000);
201
+ expect(reconnectTimes.length).toBe(1);
202
+
203
+ // Simulate failure, wait for second attempt (2s)
204
+ (client.ws as MockWebSocket)?.simulateClose(1006);
205
+ await vi.advanceTimersByTimeAsync(2000);
206
+ expect(reconnectTimes.length).toBe(2);
207
+
208
+ // Simulate failure, wait for third attempt (4s)
209
+ (client.ws as MockWebSocket)?.simulateClose(1006);
210
+ await vi.advanceTimersByTimeAsync(4000);
211
+ expect(reconnectTimes.length).toBe(3);
212
+
213
+ // Simulate failure, wait for fourth attempt (8s)
214
+ (client.ws as MockWebSocket)?.simulateClose(1006);
215
+ await vi.advanceTimersByTimeAsync(8000);
216
+ expect(reconnectTimes.length).toBe(4);
217
+
218
+ // Verify max backoff (30s)
219
+ (client.ws as MockWebSocket)?.simulateClose(1006);
220
+ await vi.advanceTimersByTimeAsync(16000); // Would be 16s, but capped at 30s
221
+ await vi.advanceTimersByTimeAsync(30000);
222
+ expect(reconnectTimes.length).toBe(5);
223
+ });
224
+
225
+ it('should buffer messages during disconnection (max 100 messages)', async () => {
226
+ // Connect
227
+ client.connect();
228
+ await vi.runAllTimersAsync();
229
+
230
+ // Disconnect
231
+ client.disconnect();
232
+
233
+ // Try to send messages while disconnected
234
+ for (let i = 0; i < 120; i++) {
235
+ client.send({ type: 'test', index: i });
236
+ }
237
+
238
+ // Verify messages are buffered (max 100)
239
+ expect(client.getBufferedMessageCount()).toBe(100);
240
+
241
+ // Reconnect
242
+ client.connect();
243
+ await vi.runAllTimersAsync();
244
+
245
+ // Verify buffered messages are sent
246
+ const ws = client.ws as MockWebSocket;
247
+ expect(ws.sentMessages.length).toBe(100);
248
+
249
+ // Verify oldest messages were kept (FIFO)
250
+ const firstMessage = JSON.parse(ws.sentMessages[0]!);
251
+ expect(firstMessage.index).toBe(20); // 0-19 were dropped
252
+ });
253
+
254
+ it('should dispatch received messages to store handlers', async () => {
255
+ // Connect
256
+ client.connect();
257
+ await vi.runAllTimersAsync();
258
+
259
+ // Simulate receiving agent_activated event
260
+ const ws = client.ws as MockWebSocket;
261
+ ws.simulateMessage({
262
+ agent_name: 'test_agent',
263
+ agent_id: 'test_agent',
264
+ consumed_types: ['Input'],
265
+ consumed_artifacts: ['artifact-1'],
266
+ subscription_info: { from_agents: [], channels: [], mode: 'both' },
267
+ labels: ['test'],
268
+ tenant_id: null,
269
+ max_concurrency: 1,
270
+ correlation_id: 'corr-123',
271
+ timestamp: '2025-10-03T12:00:00Z',
272
+ });
273
+
274
+ // Verify store was updated
275
+ // Implementation will determine exact handler
276
+ // This could be addAgent, updateAgent, or custom event handler
277
+ expect(mockStore.addAgent.mock.calls.length + mockStore.updateAgent.mock.calls.length).toBeGreaterThan(0);
278
+ });
279
+
280
+ it('should update connection status state', async () => {
281
+ // Initial state
282
+ expect(client.getConnectionStatus()).toBe('disconnected');
283
+
284
+ // Connecting
285
+ client.connect();
286
+ expect(client.getConnectionStatus()).toBe('connecting');
287
+
288
+ // Connected
289
+ await vi.runAllTimersAsync();
290
+ expect(client.getConnectionStatus()).toBe('connected');
291
+
292
+ // Disconnecting
293
+ client.disconnect();
294
+ expect(client.getConnectionStatus()).toBe('disconnecting');
295
+
296
+ // Disconnected
297
+ await vi.runAllTimersAsync();
298
+ expect(client.getConnectionStatus()).toBe('disconnected');
299
+
300
+ // Error state
301
+ client.connect();
302
+ await vi.runAllTimersAsync();
303
+ (client.ws as MockWebSocket).simulateError();
304
+ expect(client.getConnectionStatus()).toBe('error');
305
+ });
306
+
307
+ it('should handle heartbeat/pong messages', async () => {
308
+ // Connect
309
+ client.connect();
310
+ await vi.runAllTimersAsync();
311
+
312
+ const ws = client.ws as MockWebSocket;
313
+
314
+ // Simulate receiving heartbeat/ping from server
315
+ ws.simulateMessage({ type: 'ping', timestamp: Date.now() });
316
+
317
+ // Verify pong response was sent
318
+ const pongMessages = ws.sentMessages.filter(msg => {
319
+ try {
320
+ const data = JSON.parse(msg);
321
+ return data.type === 'pong';
322
+ } catch {
323
+ return false;
324
+ }
325
+ });
326
+
327
+ expect(pongMessages.length).toBeGreaterThan(0);
328
+ });
329
+
330
+ it('should handle message_published events', async () => {
331
+ // Connect
332
+ client.connect();
333
+ await vi.runAllTimersAsync();
334
+
335
+ // Simulate receiving message_published event
336
+ const ws = client.ws as MockWebSocket;
337
+ ws.simulateMessage({
338
+ artifact_id: 'artifact-123',
339
+ artifact_type: 'Movie',
340
+ produced_by: 'movie_agent',
341
+ payload: { title: 'Inception', year: 2010 },
342
+ visibility: { kind: 'Public' },
343
+ tags: ['scifi'],
344
+ version: 1,
345
+ consumers: ['tagline_agent'],
346
+ correlation_id: 'corr-456',
347
+ timestamp: '2025-10-03T12:01:00Z',
348
+ });
349
+
350
+ // Verify message was added to store
351
+ expect(mockStore.addMessage).toHaveBeenCalledWith(
352
+ expect.objectContaining({
353
+ id: 'artifact-123',
354
+ type: 'Movie',
355
+ payload: expect.objectContaining({ title: 'Inception' }),
356
+ })
357
+ );
358
+ });
359
+
360
+ it('should handle agent_completed events', async () => {
361
+ // Connect
362
+ client.connect();
363
+ await vi.runAllTimersAsync();
364
+
365
+ // Simulate receiving agent_completed event
366
+ const ws = client.ws as MockWebSocket;
367
+ ws.simulateMessage({
368
+ agent_name: 'test_agent',
369
+ run_id: 'task-123',
370
+ duration_ms: 1234.56,
371
+ artifacts_produced: ['artifact-1', 'artifact-2'],
372
+ metrics: { tokens_used: 500, cost_usd: 0.01 },
373
+ final_state: {},
374
+ correlation_id: 'corr-789',
375
+ timestamp: '2025-10-03T12:02:00Z',
376
+ });
377
+
378
+ // Verify agent status was updated
379
+ expect(mockStore.updateAgent).toHaveBeenCalledWith(
380
+ 'test_agent',
381
+ expect.objectContaining({
382
+ status: 'idle', // or 'completed'
383
+ })
384
+ );
385
+ });
386
+
387
+ it('should handle agent_error events', async () => {
388
+ // Connect
389
+ client.connect();
390
+ await vi.runAllTimersAsync();
391
+
392
+ // Simulate receiving agent_error event
393
+ const ws = client.ws as MockWebSocket;
394
+ ws.simulateMessage({
395
+ agent_name: 'test_agent',
396
+ run_id: 'task-456',
397
+ error_type: 'ValueError',
398
+ error_message: 'Invalid input',
399
+ traceback: 'Traceback...',
400
+ failed_at: '2025-10-03T12:03:00Z',
401
+ correlation_id: 'corr-999',
402
+ timestamp: '2025-10-03T12:03:00Z',
403
+ });
404
+
405
+ // Verify agent status was updated to error
406
+ expect(mockStore.updateAgent).toHaveBeenCalledWith(
407
+ 'test_agent',
408
+ expect.objectContaining({
409
+ status: 'error',
410
+ })
411
+ );
412
+ });
413
+
414
+ it('should handle streaming_output events', async () => {
415
+ // Connect
416
+ client.connect();
417
+ await vi.runAllTimersAsync();
418
+
419
+ // Simulate receiving streaming_output event
420
+ const ws = client.ws as MockWebSocket;
421
+ ws.simulateMessage({
422
+ agent_name: 'llm_agent',
423
+ run_id: 'task-789',
424
+ output_type: 'llm_token',
425
+ content: 'Generated text...',
426
+ sequence: 1,
427
+ is_final: false,
428
+ correlation_id: 'corr-111',
429
+ timestamp: '2025-10-03T12:04:00Z',
430
+ });
431
+
432
+ // Verify streaming output was handled
433
+ // Implementation may use custom handler or update agent state
434
+ // This test verifies the event was processed without error
435
+ expect(mockStore.updateAgent).toHaveBeenCalled();
436
+ });
437
+
438
+ it('should clean up on disconnect', async () => {
439
+ // Connect
440
+ client.connect();
441
+ await vi.runAllTimersAsync();
442
+
443
+ const ws = client.ws as MockWebSocket;
444
+
445
+ // Disconnect
446
+ client.disconnect();
447
+ await vi.runAllTimersAsync();
448
+
449
+ // Verify WebSocket is closed
450
+ expect(ws.readyState).toBe(MockWebSocket.CLOSED);
451
+
452
+ // Verify no reconnection attempts
453
+ await vi.advanceTimersByTimeAsync(5000);
454
+ expect(client.isConnected()).toBe(false);
455
+ });
456
+
457
+ it('should handle malformed JSON messages gracefully', async () => {
458
+ // Connect
459
+ client.connect();
460
+ await vi.runAllTimersAsync();
461
+
462
+ const ws = client.ws as MockWebSocket;
463
+
464
+ // Simulate receiving malformed JSON
465
+ if (ws.onmessage) {
466
+ ws.onmessage(new MessageEvent('message', { data: 'invalid json {{{' }));
467
+ }
468
+
469
+ // Verify client is still connected (error was handled)
470
+ expect(client.isConnected()).toBe(true);
471
+ });
472
+
473
+ it('should support manual reconnection', async () => {
474
+ // Connect and disconnect
475
+ client.connect();
476
+ await vi.runAllTimersAsync();
477
+ client.disconnect();
478
+ await vi.runAllTimersAsync();
479
+
480
+ expect(client.isConnected()).toBe(false);
481
+
482
+ // Manual reconnect
483
+ client.connect();
484
+ await vi.runAllTimersAsync();
485
+
486
+ expect(client.isConnected()).toBe(true);
487
+ });
488
+
489
+ it('should prevent multiple simultaneous connections', async () => {
490
+ // Connect
491
+ client.connect();
492
+ await vi.runAllTimersAsync();
493
+
494
+ const firstWs = client.ws;
495
+
496
+ // Try to connect again (should use same WebSocket)
497
+ client.connect();
498
+ await vi.runAllTimersAsync();
499
+
500
+ expect(client.ws).toBe(firstWs); // Should reuse existing connection
501
+
502
+ // Verify same WebSocket instance (or old one was closed)
503
+ // Implementation should either reuse or close old connection
504
+ expect(client.ws).toBeDefined();
505
+ expect(client.isConnected()).toBe(true);
506
+ });
507
+ });
508
+
509
+ describe('WebSocketClient - Edge Cases', () => {
510
+ let WebSocketClient: any;
511
+ let client: any;
512
+ let mockStore: any;
513
+
514
+ beforeEach(async () => {
515
+ vi.useFakeTimers();
516
+
517
+ mockStore = {
518
+ addAgent: vi.fn(),
519
+ updateAgent: vi.fn(),
520
+ addMessage: vi.fn(),
521
+ batchUpdate: vi.fn(),
522
+ };
523
+
524
+ try {
525
+ const module = await import('./websocket');
526
+ WebSocketClient = module.WebSocketClient;
527
+ client = new WebSocketClient('ws://localhost:8080/ws', mockStore);
528
+ } catch (error) {
529
+ throw new Error('WebSocketClient not implemented yet - skipping tests');
530
+ }
531
+ });
532
+
533
+ afterEach(() => {
534
+ if (client) {
535
+ client.disconnect();
536
+ }
537
+ vi.clearAllTimers();
538
+ vi.useRealTimers();
539
+ });
540
+
541
+ it('should handle rapid connect/disconnect cycles', async () => {
542
+ for (let i = 0; i < 5; i++) {
543
+ client.connect();
544
+ await vi.runAllTimersAsync();
545
+ expect(client.isConnected()).toBe(true);
546
+
547
+ client.disconnect();
548
+ await vi.runAllTimersAsync();
549
+ expect(client.isConnected()).toBe(false);
550
+ }
551
+ });
552
+
553
+ it('should handle connection timeout', async () => {
554
+ // Prevent auto-connect in mock
555
+ MockWebSocket.skipAutoConnect = true;
556
+
557
+ // Connect
558
+ client.connect();
559
+
560
+ // Don't simulate open event - let connection timeout
561
+ await vi.advanceTimersByTimeAsync(10000); // 10 second timeout
562
+
563
+ // Verify timeout was handled
564
+ // Implementation should either retry or emit error
565
+ expect(client.getConnectionStatus()).toMatch(/error|connecting/);
566
+
567
+ // Reset flag
568
+ MockWebSocket.skipAutoConnect = false;
569
+ });
570
+
571
+ it('should reset backoff on successful connection', async () => {
572
+ // Initial connection
573
+ client.connect();
574
+ await vi.runAllTimersAsync();
575
+
576
+ // Disconnect and trigger exponential backoff
577
+ (client.ws as MockWebSocket).simulateClose(1006);
578
+ await vi.advanceTimersByTimeAsync(1000);
579
+ (client.ws as MockWebSocket)?.simulateClose(1006);
580
+ await vi.advanceTimersByTimeAsync(2000);
581
+
582
+ // Successful connection
583
+ client.connect();
584
+ await vi.runAllTimersAsync();
585
+ expect(client.isConnected()).toBe(true);
586
+
587
+ // Next disconnection should start at 1s again (backoff reset)
588
+ (client.ws as MockWebSocket).simulateClose(1006);
589
+ const startTime = Date.now();
590
+ await vi.advanceTimersByTimeAsync(1000);
591
+
592
+ // Verify reconnection happened at 1s (not continuing exponential backoff)
593
+ expect(Date.now() - startTime).toBeLessThan(1500);
594
+ });
595
+ });