flock-core 0.4.543__py3-none-any.whl → 0.5.0__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 (501) hide show
  1. flock/__init__.py +12 -217
  2. flock/agent.py +1079 -0
  3. flock/api/themes.py +71 -0
  4. flock/artifacts.py +86 -0
  5. flock/cli.py +147 -0
  6. flock/components.py +189 -0
  7. flock/dashboard/__init__.py +30 -0
  8. flock/dashboard/collector.py +559 -0
  9. flock/dashboard/events.py +188 -0
  10. flock/dashboard/graph_builder.py +563 -0
  11. flock/dashboard/launcher.py +235 -0
  12. flock/dashboard/models/graph.py +156 -0
  13. flock/dashboard/service.py +991 -0
  14. flock/dashboard/static_v2/assets/index-DFRnI_mt.js +111 -0
  15. flock/dashboard/static_v2/assets/index-fPLNdmp1.css +1 -0
  16. flock/dashboard/static_v2/index.html +13 -0
  17. flock/dashboard/websocket.py +246 -0
  18. flock/engines/__init__.py +6 -0
  19. flock/engines/dspy_engine.py +932 -0
  20. flock/examples.py +131 -0
  21. flock/frontend/README.md +778 -0
  22. flock/frontend/docs/DESIGN_SYSTEM.md +1980 -0
  23. flock/frontend/index.html +12 -0
  24. flock/frontend/package-lock.json +4337 -0
  25. flock/frontend/package.json +48 -0
  26. flock/frontend/src/App.tsx +139 -0
  27. flock/frontend/src/__tests__/integration/graph-snapshot.test.tsx +647 -0
  28. flock/frontend/src/__tests__/integration/indexeddb-persistence.test.tsx +699 -0
  29. flock/frontend/src/components/common/BuildInfo.tsx +39 -0
  30. flock/frontend/src/components/common/EmptyState.module.css +115 -0
  31. flock/frontend/src/components/common/EmptyState.tsx +128 -0
  32. flock/frontend/src/components/common/ErrorBoundary.module.css +169 -0
  33. flock/frontend/src/components/common/ErrorBoundary.tsx +118 -0
  34. flock/frontend/src/components/common/KeyboardShortcutsDialog.css +251 -0
  35. flock/frontend/src/components/common/KeyboardShortcutsDialog.tsx +151 -0
  36. flock/frontend/src/components/common/LoadingSpinner.module.css +97 -0
  37. flock/frontend/src/components/common/LoadingSpinner.tsx +29 -0
  38. flock/frontend/src/components/controls/PublishControl.css +547 -0
  39. flock/frontend/src/components/controls/PublishControl.test.tsx +543 -0
  40. flock/frontend/src/components/controls/PublishControl.tsx +432 -0
  41. flock/frontend/src/components/details/DetailWindowContainer.tsx +58 -0
  42. flock/frontend/src/components/details/LiveOutputTab.test.tsx +792 -0
  43. flock/frontend/src/components/details/LiveOutputTab.tsx +220 -0
  44. flock/frontend/src/components/details/MessageDetailWindow.tsx +439 -0
  45. flock/frontend/src/components/details/MessageHistoryTab.tsx +374 -0
  46. flock/frontend/src/components/details/NodeDetailWindow.test.tsx +501 -0
  47. flock/frontend/src/components/details/NodeDetailWindow.tsx +218 -0
  48. flock/frontend/src/components/details/RunStatusTab.tsx +348 -0
  49. flock/frontend/src/components/details/tabs.test.tsx +1015 -0
  50. flock/frontend/src/components/filters/ArtifactTypeFilter.tsx +21 -0
  51. flock/frontend/src/components/filters/CorrelationIDFilter.module.css +102 -0
  52. flock/frontend/src/components/filters/CorrelationIDFilter.test.tsx +197 -0
  53. flock/frontend/src/components/filters/CorrelationIDFilter.tsx +121 -0
  54. flock/frontend/src/components/filters/FilterFlyout.module.css +104 -0
  55. flock/frontend/src/components/filters/FilterFlyout.tsx +80 -0
  56. flock/frontend/src/components/filters/FilterPills.module.css +220 -0
  57. flock/frontend/src/components/filters/FilterPills.test.tsx +189 -0
  58. flock/frontend/src/components/filters/FilterPills.tsx +143 -0
  59. flock/frontend/src/components/filters/ProducerFilter.tsx +21 -0
  60. flock/frontend/src/components/filters/SavedFiltersControl.module.css +60 -0
  61. flock/frontend/src/components/filters/SavedFiltersControl.test.tsx +158 -0
  62. flock/frontend/src/components/filters/SavedFiltersControl.tsx +159 -0
  63. flock/frontend/src/components/filters/TagFilter.tsx +21 -0
  64. flock/frontend/src/components/filters/TimeRangeFilter.module.css +115 -0
  65. flock/frontend/src/components/filters/TimeRangeFilter.test.tsx +154 -0
  66. flock/frontend/src/components/filters/TimeRangeFilter.tsx +110 -0
  67. flock/frontend/src/components/filters/VisibilityFilter.tsx +21 -0
  68. flock/frontend/src/components/graph/AgentNode.test.tsx +77 -0
  69. flock/frontend/src/components/graph/AgentNode.tsx +324 -0
  70. flock/frontend/src/components/graph/GraphCanvas.tsx +613 -0
  71. flock/frontend/src/components/graph/MessageFlowEdge.tsx +128 -0
  72. flock/frontend/src/components/graph/MessageNode.test.tsx +64 -0
  73. flock/frontend/src/components/graph/MessageNode.tsx +129 -0
  74. flock/frontend/src/components/graph/MiniMap.tsx +47 -0
  75. flock/frontend/src/components/graph/TransformEdge.tsx +123 -0
  76. flock/frontend/src/components/layout/DashboardLayout.css +420 -0
  77. flock/frontend/src/components/layout/DashboardLayout.tsx +287 -0
  78. flock/frontend/src/components/layout/Header.module.css +88 -0
  79. flock/frontend/src/components/layout/Header.tsx +52 -0
  80. flock/frontend/src/components/modules/HistoricalArtifactsModule.module.css +288 -0
  81. flock/frontend/src/components/modules/HistoricalArtifactsModule.tsx +450 -0
  82. flock/frontend/src/components/modules/HistoricalArtifactsModuleWrapper.tsx +13 -0
  83. flock/frontend/src/components/modules/JsonAttributeRenderer.tsx +140 -0
  84. flock/frontend/src/components/modules/ModuleRegistry.test.ts +333 -0
  85. flock/frontend/src/components/modules/ModuleRegistry.ts +93 -0
  86. flock/frontend/src/components/modules/ModuleWindow.tsx +223 -0
  87. flock/frontend/src/components/modules/TraceModuleJaeger.tsx +1971 -0
  88. flock/frontend/src/components/modules/TraceModuleJaegerWrapper.tsx +13 -0
  89. flock/frontend/src/components/modules/registerModules.ts +29 -0
  90. flock/frontend/src/components/settings/AdvancedSettings.tsx +175 -0
  91. flock/frontend/src/components/settings/AppearanceSettings.tsx +185 -0
  92. flock/frontend/src/components/settings/GraphSettings.tsx +110 -0
  93. flock/frontend/src/components/settings/MultiSelect.tsx +235 -0
  94. flock/frontend/src/components/settings/SettingsPanel.css +327 -0
  95. flock/frontend/src/components/settings/SettingsPanel.tsx +131 -0
  96. flock/frontend/src/components/settings/ThemeSelector.tsx +298 -0
  97. flock/frontend/src/components/settings/TracingSettings.tsx +404 -0
  98. flock/frontend/src/hooks/useKeyboardShortcuts.ts +148 -0
  99. flock/frontend/src/hooks/useModulePersistence.test.ts +442 -0
  100. flock/frontend/src/hooks/useModulePersistence.ts +154 -0
  101. flock/frontend/src/hooks/useModules.ts +157 -0
  102. flock/frontend/src/hooks/usePersistence.ts +141 -0
  103. flock/frontend/src/main.tsx +13 -0
  104. flock/frontend/src/services/api.ts +337 -0
  105. flock/frontend/src/services/graphService.test.ts +330 -0
  106. flock/frontend/src/services/graphService.ts +75 -0
  107. flock/frontend/src/services/indexeddb.test.ts +793 -0
  108. flock/frontend/src/services/indexeddb.ts +848 -0
  109. flock/frontend/src/services/layout.test.ts +437 -0
  110. flock/frontend/src/services/layout.ts +357 -0
  111. flock/frontend/src/services/themeApplicator.ts +140 -0
  112. flock/frontend/src/services/themeService.ts +77 -0
  113. flock/frontend/src/services/websocket.ts +650 -0
  114. flock/frontend/src/store/filterStore.test.ts +250 -0
  115. flock/frontend/src/store/filterStore.ts +272 -0
  116. flock/frontend/src/store/graphStore.test.ts +570 -0
  117. flock/frontend/src/store/graphStore.ts +462 -0
  118. flock/frontend/src/store/moduleStore.test.ts +253 -0
  119. flock/frontend/src/store/moduleStore.ts +75 -0
  120. flock/frontend/src/store/settingsStore.ts +188 -0
  121. flock/frontend/src/store/streamStore.ts +68 -0
  122. flock/frontend/src/store/uiStore.test.ts +54 -0
  123. flock/frontend/src/store/uiStore.ts +122 -0
  124. flock/frontend/src/store/wsStore.ts +34 -0
  125. flock/frontend/src/styles/index.css +15 -0
  126. flock/frontend/src/styles/scrollbar.css +47 -0
  127. flock/frontend/src/styles/variables.css +488 -0
  128. flock/frontend/src/test/setup.ts +1 -0
  129. flock/frontend/src/types/filters.ts +47 -0
  130. flock/frontend/src/types/graph.ts +95 -0
  131. flock/frontend/src/types/modules.ts +10 -0
  132. flock/frontend/src/types/theme.ts +55 -0
  133. flock/frontend/src/utils/artifacts.ts +24 -0
  134. flock/frontend/src/utils/mockData.ts +98 -0
  135. flock/frontend/src/utils/performance.ts +16 -0
  136. flock/frontend/src/vite-env.d.ts +17 -0
  137. flock/frontend/tsconfig.json +27 -0
  138. flock/frontend/tsconfig.node.json +11 -0
  139. flock/frontend/vite.config.ts +25 -0
  140. flock/frontend/vitest.config.ts +11 -0
  141. flock/{core/util → helper}/cli_helper.py +9 -5
  142. flock/{core/logging → logging}/__init__.py +2 -3
  143. flock/logging/auto_trace.py +159 -0
  144. flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
  145. flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
  146. flock/{core/logging → logging}/formatters/themed_formatter.py +69 -107
  147. flock/{core/logging → logging}/logging.py +78 -61
  148. flock/{core/logging → logging}/telemetry.py +66 -26
  149. flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
  150. flock/logging/telemetry_exporter/duckdb_exporter.py +216 -0
  151. flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +13 -10
  152. flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
  153. flock/logging/trace_and_logged.py +304 -0
  154. flock/mcp/__init__.py +91 -0
  155. flock/{core/mcp/mcp_client.py → mcp/client.py} +131 -158
  156. flock/{core/mcp/mcp_config.py → mcp/config.py} +86 -132
  157. flock/mcp/manager.py +286 -0
  158. flock/mcp/servers/sse/__init__.py +1 -1
  159. flock/mcp/servers/sse/flock_sse_server.py +16 -58
  160. flock/mcp/servers/stdio/__init__.py +1 -1
  161. flock/mcp/servers/stdio/flock_stdio_server.py +13 -53
  162. flock/mcp/servers/streamable_http/flock_streamable_http_server.py +22 -67
  163. flock/mcp/servers/websockets/flock_websocket_server.py +12 -45
  164. flock/{core/mcp/flock_mcp_tool_base.py → mcp/tool.py} +24 -78
  165. flock/mcp/types/__init__.py +42 -0
  166. flock/{core/mcp → mcp}/types/callbacks.py +12 -15
  167. flock/{core/mcp → mcp}/types/factories.py +7 -6
  168. flock/{core/mcp → mcp}/types/handlers.py +13 -18
  169. flock/{core/mcp → mcp}/types/types.py +70 -74
  170. flock/{core/mcp → mcp}/util/helpers.py +3 -3
  171. flock/orchestrator.py +970 -0
  172. flock/registry.py +148 -0
  173. flock/runtime.py +262 -0
  174. flock/service.py +277 -0
  175. flock/store.py +1214 -0
  176. flock/subscription.py +111 -0
  177. flock/themes/andromeda.toml +1 -1
  178. flock/themes/apple-system-colors.toml +1 -1
  179. flock/themes/arcoiris.toml +1 -1
  180. flock/themes/atomonelight.toml +1 -1
  181. flock/themes/ayu copy.toml +1 -1
  182. flock/themes/ayu-light.toml +1 -1
  183. flock/themes/belafonte-day.toml +1 -1
  184. flock/themes/belafonte-night.toml +1 -1
  185. flock/themes/blulocodark.toml +1 -1
  186. flock/themes/breeze.toml +1 -1
  187. flock/themes/broadcast.toml +1 -1
  188. flock/themes/brogrammer.toml +1 -1
  189. flock/themes/builtin-dark.toml +1 -1
  190. flock/themes/builtin-pastel-dark.toml +1 -1
  191. flock/themes/catppuccin-latte.toml +1 -1
  192. flock/themes/catppuccin-macchiato.toml +1 -1
  193. flock/themes/catppuccin-mocha.toml +1 -1
  194. flock/themes/cga.toml +1 -1
  195. flock/themes/chalk.toml +1 -1
  196. flock/themes/ciapre.toml +1 -1
  197. flock/themes/coffee-theme.toml +1 -1
  198. flock/themes/cyberpunkscarletprotocol.toml +1 -1
  199. flock/themes/dark+.toml +1 -1
  200. flock/themes/darkermatrix.toml +1 -1
  201. flock/themes/darkmatrix.toml +2 -2
  202. flock/themes/darkside.toml +1 -1
  203. flock/themes/deep.toml +2 -2
  204. flock/themes/desert.toml +1 -1
  205. flock/themes/django.toml +1 -1
  206. flock/themes/djangosmooth.toml +1 -1
  207. flock/themes/doomone.toml +1 -1
  208. flock/themes/dotgov.toml +1 -1
  209. flock/themes/dracula+.toml +1 -1
  210. flock/themes/duckbones.toml +1 -1
  211. flock/themes/encom.toml +1 -1
  212. flock/themes/espresso.toml +1 -1
  213. flock/themes/everblush.toml +1 -1
  214. flock/themes/fairyfloss.toml +1 -1
  215. flock/themes/fideloper.toml +1 -1
  216. flock/themes/fishtank.toml +1 -1
  217. flock/themes/flexoki-light.toml +1 -1
  218. flock/themes/floraverse.toml +1 -1
  219. flock/themes/framer.toml +1 -1
  220. flock/themes/galizur.toml +1 -1
  221. flock/themes/github.toml +1 -1
  222. flock/themes/grass.toml +1 -1
  223. flock/themes/grey-green.toml +1 -1
  224. flock/themes/gruvboxlight.toml +1 -1
  225. flock/themes/guezwhoz.toml +1 -1
  226. flock/themes/harper.toml +1 -1
  227. flock/themes/hax0r-blue.toml +1 -1
  228. flock/themes/hopscotch.256.toml +1 -1
  229. flock/themes/ic-green-ppl.toml +1 -1
  230. flock/themes/iceberg-dark.toml +1 -1
  231. flock/themes/japanesque.toml +1 -1
  232. flock/themes/jubi.toml +1 -1
  233. flock/themes/kibble.toml +1 -1
  234. flock/themes/kolorit.toml +1 -1
  235. flock/themes/kurokula.toml +1 -1
  236. flock/themes/materialdesigncolors.toml +1 -1
  237. flock/themes/matrix.toml +1 -1
  238. flock/themes/mellifluous.toml +1 -1
  239. flock/themes/midnight-in-mojave.toml +1 -1
  240. flock/themes/monokai-remastered.toml +1 -1
  241. flock/themes/monokai-soda.toml +1 -1
  242. flock/themes/neon.toml +1 -1
  243. flock/themes/neopolitan.toml +5 -5
  244. flock/themes/nord-light.toml +1 -1
  245. flock/themes/ocean.toml +1 -1
  246. flock/themes/onehalfdark.toml +1 -1
  247. flock/themes/onehalflight.toml +1 -1
  248. flock/themes/palenighthc.toml +1 -1
  249. flock/themes/paulmillr.toml +1 -1
  250. flock/themes/pencildark.toml +1 -1
  251. flock/themes/pnevma.toml +1 -1
  252. flock/themes/purple-rain.toml +1 -1
  253. flock/themes/purplepeter.toml +1 -1
  254. flock/themes/raycast-dark.toml +1 -1
  255. flock/themes/red-sands.toml +1 -1
  256. flock/themes/relaxed.toml +1 -1
  257. flock/themes/retro.toml +1 -1
  258. flock/themes/rose-pine.toml +1 -1
  259. flock/themes/royal.toml +1 -1
  260. flock/themes/ryuuko.toml +1 -1
  261. flock/themes/sakura.toml +1 -1
  262. flock/themes/scarlet-protocol.toml +1 -1
  263. flock/themes/seoulbones-dark.toml +1 -1
  264. flock/themes/shades-of-purple.toml +1 -1
  265. flock/themes/smyck.toml +1 -1
  266. flock/themes/softserver.toml +1 -1
  267. flock/themes/solarized-darcula.toml +1 -1
  268. flock/themes/square.toml +1 -1
  269. flock/themes/sugarplum.toml +1 -1
  270. flock/themes/thayer-bright.toml +1 -1
  271. flock/themes/tokyonight.toml +1 -1
  272. flock/themes/tomorrow.toml +1 -1
  273. flock/themes/ubuntu.toml +1 -1
  274. flock/themes/ultradark.toml +1 -1
  275. flock/themes/ultraviolent.toml +1 -1
  276. flock/themes/unikitty.toml +1 -1
  277. flock/themes/urple.toml +1 -1
  278. flock/themes/vesper.toml +1 -1
  279. flock/themes/vimbones.toml +1 -1
  280. flock/themes/wildcherry.toml +1 -1
  281. flock/themes/wilmersdorf.toml +1 -1
  282. flock/themes/wryan.toml +1 -1
  283. flock/themes/xcodedarkhc.toml +1 -1
  284. flock/themes/xcodelight.toml +1 -1
  285. flock/themes/zenbones-light.toml +1 -1
  286. flock/themes/zenwritten-dark.toml +1 -1
  287. flock/utilities.py +301 -0
  288. flock/utility/output_utility_component.py +226 -0
  289. flock/visibility.py +107 -0
  290. flock_core-0.5.0.dist-info/METADATA +964 -0
  291. flock_core-0.5.0.dist-info/RECORD +525 -0
  292. flock_core-0.5.0.dist-info/entry_points.txt +2 -0
  293. {flock_core-0.4.543.dist-info → flock_core-0.5.0.dist-info}/licenses/LICENSE +1 -1
  294. flock/adapter/__init__.py +0 -14
  295. flock/adapter/azure_adapter.py +0 -68
  296. flock/adapter/chroma_adapter.py +0 -73
  297. flock/adapter/faiss_adapter.py +0 -97
  298. flock/adapter/pinecone_adapter.py +0 -51
  299. flock/adapter/vector_base.py +0 -47
  300. flock/cli/assets/release_notes.md +0 -140
  301. flock/cli/config.py +0 -8
  302. flock/cli/constants.py +0 -36
  303. flock/cli/create_agent.py +0 -1
  304. flock/cli/create_flock.py +0 -280
  305. flock/cli/execute_flock.py +0 -620
  306. flock/cli/load_agent.py +0 -1
  307. flock/cli/load_examples.py +0 -1
  308. flock/cli/load_flock.py +0 -192
  309. flock/cli/load_release_notes.py +0 -20
  310. flock/cli/loaded_flock_cli.py +0 -254
  311. flock/cli/manage_agents.py +0 -459
  312. flock/cli/registry_management.py +0 -889
  313. flock/cli/runner.py +0 -41
  314. flock/cli/settings.py +0 -857
  315. flock/cli/utils.py +0 -135
  316. flock/cli/view_results.py +0 -29
  317. flock/cli/yaml_editor.py +0 -396
  318. flock/config.py +0 -56
  319. flock/core/__init__.py +0 -44
  320. flock/core/api/__init__.py +0 -10
  321. flock/core/api/custom_endpoint.py +0 -45
  322. flock/core/api/endpoints.py +0 -262
  323. flock/core/api/main.py +0 -162
  324. flock/core/api/models.py +0 -101
  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/config/flock_agent_config.py +0 -11
  329. flock/core/config/scheduled_agent_config.py +0 -40
  330. flock/core/context/context.py +0 -214
  331. flock/core/context/context_manager.py +0 -40
  332. flock/core/context/context_vars.py +0 -11
  333. flock/core/evaluation/utils.py +0 -395
  334. flock/core/execution/batch_executor.py +0 -369
  335. flock/core/execution/evaluation_executor.py +0 -438
  336. flock/core/execution/local_executor.py +0 -31
  337. flock/core/execution/opik_executor.py +0 -103
  338. flock/core/execution/temporal_executor.py +0 -166
  339. flock/core/flock.py +0 -1003
  340. flock/core/flock_agent.py +0 -1258
  341. flock/core/flock_evaluator.py +0 -60
  342. flock/core/flock_factory.py +0 -513
  343. flock/core/flock_module.py +0 -207
  344. flock/core/flock_registry.py +0 -702
  345. flock/core/flock_router.py +0 -83
  346. flock/core/flock_scheduler.py +0 -166
  347. flock/core/flock_server_manager.py +0 -136
  348. flock/core/interpreter/python_interpreter.py +0 -689
  349. flock/core/logging/live_capture.py +0 -137
  350. flock/core/logging/trace_and_logged.py +0 -59
  351. flock/core/mcp/__init__.py +0 -1
  352. flock/core/mcp/flock_mcp_server.py +0 -640
  353. flock/core/mcp/mcp_client_manager.py +0 -201
  354. flock/core/mcp/types/__init__.py +0 -1
  355. flock/core/mixin/dspy_integration.py +0 -445
  356. flock/core/mixin/prompt_parser.py +0 -125
  357. flock/core/serialization/__init__.py +0 -13
  358. flock/core/serialization/callable_registry.py +0 -52
  359. flock/core/serialization/flock_serializer.py +0 -854
  360. flock/core/serialization/json_encoder.py +0 -41
  361. flock/core/serialization/secure_serializer.py +0 -175
  362. flock/core/serialization/serializable.py +0 -342
  363. flock/core/serialization/serialization_utils.py +0 -409
  364. flock/core/util/file_path_utils.py +0 -223
  365. flock/core/util/hydrator.py +0 -309
  366. flock/core/util/input_resolver.py +0 -141
  367. flock/core/util/loader.py +0 -59
  368. flock/core/util/splitter.py +0 -219
  369. flock/di.py +0 -41
  370. flock/evaluators/__init__.py +0 -1
  371. flock/evaluators/declarative/__init__.py +0 -1
  372. flock/evaluators/declarative/declarative_evaluator.py +0 -217
  373. flock/evaluators/memory/memory_evaluator.py +0 -90
  374. flock/evaluators/test/test_case_evaluator.py +0 -38
  375. flock/evaluators/zep/zep_evaluator.py +0 -59
  376. flock/modules/__init__.py +0 -1
  377. flock/modules/assertion/__init__.py +0 -1
  378. flock/modules/assertion/assertion_module.py +0 -286
  379. flock/modules/callback/__init__.py +0 -1
  380. flock/modules/callback/callback_module.py +0 -91
  381. flock/modules/enterprise_memory/README.md +0 -99
  382. flock/modules/enterprise_memory/enterprise_memory_module.py +0 -526
  383. flock/modules/mem0/__init__.py +0 -1
  384. flock/modules/mem0/mem0_module.py +0 -126
  385. flock/modules/mem0_async/__init__.py +0 -1
  386. flock/modules/mem0_async/async_mem0_module.py +0 -126
  387. flock/modules/memory/__init__.py +0 -1
  388. flock/modules/memory/memory_module.py +0 -429
  389. flock/modules/memory/memory_parser.py +0 -125
  390. flock/modules/memory/memory_storage.py +0 -736
  391. flock/modules/output/__init__.py +0 -1
  392. flock/modules/output/output_module.py +0 -196
  393. flock/modules/performance/__init__.py +0 -1
  394. flock/modules/performance/metrics_module.py +0 -678
  395. flock/modules/zep/__init__.py +0 -1
  396. flock/modules/zep/zep_module.py +0 -192
  397. flock/platform/docker_tools.py +0 -49
  398. flock/platform/jaeger_install.py +0 -86
  399. flock/routers/__init__.py +0 -1
  400. flock/routers/agent/__init__.py +0 -1
  401. flock/routers/agent/agent_router.py +0 -236
  402. flock/routers/agent/handoff_agent.py +0 -58
  403. flock/routers/conditional/conditional_router.py +0 -486
  404. flock/routers/default/__init__.py +0 -1
  405. flock/routers/default/default_router.py +0 -80
  406. flock/routers/feedback/feedback_router.py +0 -114
  407. flock/routers/list_generator/list_generator_router.py +0 -166
  408. flock/routers/llm/__init__.py +0 -1
  409. flock/routers/llm/llm_router.py +0 -365
  410. flock/tools/__init__.py +0 -0
  411. flock/tools/azure_tools.py +0 -781
  412. flock/tools/code_tools.py +0 -167
  413. flock/tools/file_tools.py +0 -149
  414. flock/tools/github_tools.py +0 -157
  415. flock/tools/markdown_tools.py +0 -205
  416. flock/tools/system_tools.py +0 -9
  417. flock/tools/text_tools.py +0 -810
  418. flock/tools/web_tools.py +0 -92
  419. flock/tools/zendesk_tools.py +0 -501
  420. flock/webapp/__init__.py +0 -1
  421. flock/webapp/app/__init__.py +0 -0
  422. flock/webapp/app/api/__init__.py +0 -0
  423. flock/webapp/app/api/agent_management.py +0 -237
  424. flock/webapp/app/api/execution.py +0 -503
  425. flock/webapp/app/api/flock_management.py +0 -125
  426. flock/webapp/app/api/registry_viewer.py +0 -29
  427. flock/webapp/app/chat.py +0 -662
  428. flock/webapp/app/config.py +0 -104
  429. flock/webapp/app/dependencies.py +0 -117
  430. flock/webapp/app/main.py +0 -1086
  431. flock/webapp/app/middleware.py +0 -113
  432. flock/webapp/app/models_ui.py +0 -7
  433. flock/webapp/app/services/__init__.py +0 -0
  434. flock/webapp/app/services/feedback_file_service.py +0 -363
  435. flock/webapp/app/services/flock_service.py +0 -345
  436. flock/webapp/app/services/sharing_models.py +0 -81
  437. flock/webapp/app/services/sharing_store.py +0 -597
  438. flock/webapp/app/templates/theme_mapper.html +0 -326
  439. flock/webapp/app/theme_mapper.py +0 -811
  440. flock/webapp/app/utils.py +0 -85
  441. flock/webapp/run.py +0 -219
  442. flock/webapp/static/css/chat.css +0 -301
  443. flock/webapp/static/css/components.css +0 -167
  444. flock/webapp/static/css/header.css +0 -39
  445. flock/webapp/static/css/layout.css +0 -281
  446. flock/webapp/static/css/sidebar.css +0 -127
  447. flock/webapp/static/css/two-pane.css +0 -48
  448. flock/webapp/templates/base.html +0 -389
  449. flock/webapp/templates/chat.html +0 -152
  450. flock/webapp/templates/chat_settings.html +0 -19
  451. flock/webapp/templates/flock_editor.html +0 -16
  452. flock/webapp/templates/index.html +0 -12
  453. flock/webapp/templates/partials/_agent_detail_form.html +0 -93
  454. flock/webapp/templates/partials/_agent_list.html +0 -18
  455. flock/webapp/templates/partials/_agent_manager_view.html +0 -51
  456. flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
  457. flock/webapp/templates/partials/_chat_container.html +0 -15
  458. flock/webapp/templates/partials/_chat_messages.html +0 -57
  459. flock/webapp/templates/partials/_chat_settings_form.html +0 -85
  460. flock/webapp/templates/partials/_create_flock_form.html +0 -50
  461. flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
  462. flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
  463. flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
  464. flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
  465. flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
  466. flock/webapp/templates/partials/_env_vars_table.html +0 -23
  467. flock/webapp/templates/partials/_execution_form.html +0 -127
  468. flock/webapp/templates/partials/_execution_view_container.html +0 -28
  469. flock/webapp/templates/partials/_flock_file_list.html +0 -23
  470. flock/webapp/templates/partials/_flock_properties_form.html +0 -52
  471. flock/webapp/templates/partials/_flock_upload_form.html +0 -16
  472. flock/webapp/templates/partials/_header_flock_status.html +0 -5
  473. flock/webapp/templates/partials/_live_logs.html +0 -13
  474. flock/webapp/templates/partials/_load_manager_view.html +0 -49
  475. flock/webapp/templates/partials/_registry_table.html +0 -25
  476. flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
  477. flock/webapp/templates/partials/_results_display.html +0 -78
  478. flock/webapp/templates/partials/_settings_env_content.html +0 -9
  479. flock/webapp/templates/partials/_settings_theme_content.html +0 -14
  480. flock/webapp/templates/partials/_settings_view.html +0 -36
  481. flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
  482. flock/webapp/templates/partials/_share_link_snippet.html +0 -35
  483. flock/webapp/templates/partials/_sidebar.html +0 -74
  484. flock/webapp/templates/partials/_structured_data_view.html +0 -40
  485. flock/webapp/templates/partials/_theme_preview.html +0 -36
  486. flock/webapp/templates/registry_viewer.html +0 -84
  487. flock/webapp/templates/shared_run_page.html +0 -140
  488. flock/workflow/__init__.py +0 -0
  489. flock/workflow/activities.py +0 -237
  490. flock/workflow/agent_activities.py +0 -24
  491. flock/workflow/agent_execution_activity.py +0 -240
  492. flock/workflow/flock_workflow.py +0 -225
  493. flock/workflow/temporal_config.py +0 -96
  494. flock/workflow/temporal_setup.py +0 -60
  495. flock_core-0.4.543.dist-info/METADATA +0 -676
  496. flock_core-0.4.543.dist-info/RECORD +0 -572
  497. flock_core-0.4.543.dist-info/entry_points.txt +0 -2
  498. /flock/{core/logging → logging}/formatters/themes.py +0 -0
  499. /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
  500. /flock/{core/mcp → mcp}/util/__init__.py +0 -0
  501. {flock_core-0.4.543.dist-info → flock_core-0.5.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,792 @@
1
+ /**
2
+ * Unit tests for LiveOutputTab component.
3
+ *
4
+ * Tests verify streaming output accumulation, ordering, auto-scroll,
5
+ * virtualization performance, output type rendering, and final marker handling.
6
+ */
7
+
8
+ import { describe, it, expect } from 'vitest';
9
+ import { render, screen } from '@testing-library/react';
10
+ import React from 'react';
11
+
12
+ // Streaming output data type from PRD
13
+ interface StreamingOutputData {
14
+ agent_name: string;
15
+ run_id: string;
16
+ output_type: 'llm_token' | 'log' | 'stdout' | 'stderr';
17
+ content: string;
18
+ sequence: number;
19
+ is_final: boolean;
20
+ }
21
+
22
+ // Mock component - will be replaced by actual implementation
23
+ const MockLiveOutputTab = ({
24
+ nodeId,
25
+ outputs,
26
+ autoScroll = true,
27
+ }: {
28
+ nodeId: string;
29
+ outputs: StreamingOutputData[];
30
+ autoScroll?: boolean;
31
+ }) => {
32
+ const containerRef = React.useRef<HTMLDivElement>(null);
33
+
34
+ // Sort outputs by sequence number
35
+ const sortedOutputs = [...outputs].sort((a, b) => a.sequence - b.sequence);
36
+
37
+ // Auto-scroll to bottom when new output arrives
38
+ React.useEffect(() => {
39
+ if (autoScroll && containerRef.current) {
40
+ containerRef.current.scrollTop = containerRef.current.scrollHeight;
41
+ }
42
+ }, [outputs.length, autoScroll]);
43
+
44
+ const getOutputColor = (type: StreamingOutputData['output_type']) => {
45
+ switch (type) {
46
+ case 'llm_token':
47
+ return '#8be9fd';
48
+ case 'log':
49
+ return '#f1fa8c';
50
+ case 'stdout':
51
+ return '#50fa7b';
52
+ case 'stderr':
53
+ return '#ff5555';
54
+ default:
55
+ return '#f8f8f2';
56
+ }
57
+ };
58
+
59
+ return (
60
+ <div
61
+ data-testid={`live-output-${nodeId}`}
62
+ ref={containerRef}
63
+ style={{
64
+ height: '400px',
65
+ overflow: 'auto',
66
+ background: '#282a36',
67
+ padding: '8px',
68
+ }}
69
+ >
70
+ {sortedOutputs.length === 0 ? (
71
+ <div data-testid="empty-output">No output yet...</div>
72
+ ) : (
73
+ sortedOutputs.map((output, index) => (
74
+ <div
75
+ key={`${output.run_id}-${output.sequence}`}
76
+ data-testid={`output-line-${index}`}
77
+ data-output-type={output.output_type}
78
+ data-sequence={output.sequence}
79
+ style={{
80
+ color: getOutputColor(output.output_type),
81
+ fontFamily: 'monospace',
82
+ fontSize: '12px',
83
+ whiteSpace: 'pre-wrap',
84
+ minHeight: '20px', // Ensure each line has height for scrolling
85
+ }}
86
+ >
87
+ {output.content}
88
+ </div>
89
+ ))
90
+ )}
91
+ {sortedOutputs.some((o) => o.is_final) && (
92
+ <div data-testid="final-marker" style={{ color: '#6272a4', marginTop: '8px' }}>
93
+ --- End of output ---
94
+ </div>
95
+ )}
96
+ </div>
97
+ );
98
+ };
99
+
100
+ // Mock virtualized component for performance testing
101
+ const MockVirtualizedOutputTab = ({
102
+ nodeId,
103
+ outputs,
104
+ }: {
105
+ nodeId: string;
106
+ outputs: StreamingOutputData[];
107
+ }) => {
108
+ const [visibleRange, setVisibleRange] = React.useState({ start: 0, end: 50 });
109
+
110
+ const sortedOutputs = [...outputs].sort((a, b) => a.sequence - b.sequence);
111
+ const visibleOutputs = sortedOutputs.slice(visibleRange.start, visibleRange.end);
112
+
113
+ const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
114
+ const target = e.target as HTMLDivElement;
115
+ const scrollTop = target.scrollTop;
116
+ const itemHeight = 20;
117
+ const start = Math.floor(scrollTop / itemHeight);
118
+ const end = start + 50;
119
+ setVisibleRange({ start, end });
120
+ };
121
+
122
+ return (
123
+ <div
124
+ data-testid={`virtualized-output-${nodeId}`}
125
+ onScroll={handleScroll}
126
+ style={{ height: '400px', overflow: 'auto' }}
127
+ >
128
+ <div style={{ height: `${sortedOutputs.length * 20}px`, position: 'relative' }}>
129
+ {visibleOutputs.map((output, index) => (
130
+ <div
131
+ key={`${output.run_id}-${output.sequence}`}
132
+ data-testid={`virtual-line-${visibleRange.start + index}`}
133
+ style={{
134
+ position: 'absolute',
135
+ top: `${(visibleRange.start + index) * 20}px`,
136
+ height: '20px',
137
+ }}
138
+ >
139
+ {output.content}
140
+ </div>
141
+ ))}
142
+ </div>
143
+ <div data-testid="total-lines">{sortedOutputs.length}</div>
144
+ </div>
145
+ );
146
+ };
147
+
148
+ describe('LiveOutputTab', () => {
149
+ describe('Output Display', () => {
150
+ it('should render empty state when no output', () => {
151
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={[]} />);
152
+
153
+ expect(screen.getByTestId('empty-output')).toBeInTheDocument();
154
+ expect(screen.getByText('No output yet...')).toBeInTheDocument();
155
+ });
156
+
157
+ it('should display streaming output content', () => {
158
+ const outputs: StreamingOutputData[] = [
159
+ {
160
+ agent_name: 'test_agent',
161
+ run_id: 'run-1',
162
+ output_type: 'llm_token',
163
+ content: 'Hello',
164
+ sequence: 0,
165
+ is_final: false,
166
+ },
167
+ {
168
+ agent_name: 'test_agent',
169
+ run_id: 'run-1',
170
+ output_type: 'llm_token',
171
+ content: ' World',
172
+ sequence: 1,
173
+ is_final: false,
174
+ },
175
+ ];
176
+
177
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
178
+
179
+ expect(screen.getByText('Hello')).toBeInTheDocument();
180
+ // Use data-testid to check content with leading whitespace (disable normalization)
181
+ const line1 = screen.getByTestId('output-line-1');
182
+ expect(line1.textContent).toBe(' World');
183
+ });
184
+
185
+ it('should append new content as it arrives', () => {
186
+ const { rerender } = render(<MockLiveOutputTab nodeId="agent-1" outputs={[]} />);
187
+
188
+ expect(screen.getByTestId('empty-output')).toBeInTheDocument();
189
+
190
+ // First token
191
+ const outputs1: StreamingOutputData[] = [
192
+ {
193
+ agent_name: 'test_agent',
194
+ run_id: 'run-1',
195
+ output_type: 'llm_token',
196
+ content: 'First',
197
+ sequence: 0,
198
+ is_final: false,
199
+ },
200
+ ];
201
+ rerender(<MockLiveOutputTab nodeId="agent-1" outputs={outputs1} />);
202
+ expect(screen.getByText('First')).toBeInTheDocument();
203
+
204
+ // Second token
205
+ const outputs2: StreamingOutputData[] = [
206
+ ...outputs1,
207
+ {
208
+ agent_name: 'test_agent',
209
+ run_id: 'run-1',
210
+ output_type: 'llm_token',
211
+ content: ' Second',
212
+ sequence: 1,
213
+ is_final: false,
214
+ },
215
+ ];
216
+ rerender(<MockLiveOutputTab nodeId="agent-1" outputs={outputs2} />);
217
+ expect(screen.getByText('First')).toBeInTheDocument();
218
+ // Use data-testid to check content with leading whitespace (disable normalization)
219
+ const line1 = screen.getByTestId('output-line-1');
220
+ expect(line1.textContent).toBe(' Second');
221
+ });
222
+
223
+ it('should simulate token-by-token streaming', async () => {
224
+ const tokens = ['The', ' quick', ' brown', ' fox', ' jumps'];
225
+ const outputs: StreamingOutputData[] = [];
226
+
227
+ const { rerender } = render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
228
+
229
+ for (let i = 0; i < tokens.length; i++) {
230
+ outputs.push({
231
+ agent_name: 'test_agent',
232
+ run_id: 'run-1',
233
+ output_type: 'llm_token',
234
+ content: tokens[i]!,
235
+ sequence: i,
236
+ is_final: false,
237
+ });
238
+
239
+ rerender(<MockLiveOutputTab nodeId="agent-1" outputs={[...outputs]} />);
240
+
241
+ // Verify each token is present (check textContent directly to preserve whitespace)
242
+ const token = tokens[i]!;
243
+ const line = screen.getByTestId(`output-line-${i}`);
244
+ expect(line.textContent).toBe(token);
245
+ }
246
+
247
+ // Verify all tokens are displayed
248
+ expect(screen.getAllByTestId(/output-line-/).length).toBe(tokens.length);
249
+ });
250
+ });
251
+
252
+ describe('Output Ordering', () => {
253
+ it('should order outputs by sequence number', () => {
254
+ const outputs: StreamingOutputData[] = [
255
+ {
256
+ agent_name: 'test_agent',
257
+ run_id: 'run-1',
258
+ output_type: 'llm_token',
259
+ content: 'Third',
260
+ sequence: 2,
261
+ is_final: false,
262
+ },
263
+ {
264
+ agent_name: 'test_agent',
265
+ run_id: 'run-1',
266
+ output_type: 'llm_token',
267
+ content: 'First',
268
+ sequence: 0,
269
+ is_final: false,
270
+ },
271
+ {
272
+ agent_name: 'test_agent',
273
+ run_id: 'run-1',
274
+ output_type: 'llm_token',
275
+ content: 'Second',
276
+ sequence: 1,
277
+ is_final: false,
278
+ },
279
+ ];
280
+
281
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
282
+
283
+ const lines = screen.getAllByTestId(/output-line-/);
284
+ expect(lines[0]).toHaveAttribute('data-sequence', '0');
285
+ expect(lines[1]).toHaveAttribute('data-sequence', '1');
286
+ expect(lines[2]).toHaveAttribute('data-sequence', '2');
287
+ expect(lines[0]).toHaveTextContent('First');
288
+ expect(lines[1]).toHaveTextContent('Second');
289
+ expect(lines[2]).toHaveTextContent('Third');
290
+ });
291
+
292
+ it('should handle out-of-order arrival', () => {
293
+ const { rerender } = render(<MockLiveOutputTab nodeId="agent-1" outputs={[]} />);
294
+
295
+ // Receive sequence 2 first
296
+ const outputs1: StreamingOutputData[] = [
297
+ {
298
+ agent_name: 'test_agent',
299
+ run_id: 'run-1',
300
+ output_type: 'llm_token',
301
+ content: 'Third',
302
+ sequence: 2,
303
+ is_final: false,
304
+ },
305
+ ];
306
+ rerender(<MockLiveOutputTab nodeId="agent-1" outputs={outputs1} />);
307
+
308
+ // Then receive sequence 0
309
+ const outputs2: StreamingOutputData[] = [
310
+ ...outputs1,
311
+ {
312
+ agent_name: 'test_agent',
313
+ run_id: 'run-1',
314
+ output_type: 'llm_token',
315
+ content: 'First',
316
+ sequence: 0,
317
+ is_final: false,
318
+ },
319
+ ];
320
+ rerender(<MockLiveOutputTab nodeId="agent-1" outputs={outputs2} />);
321
+
322
+ // Verify correct ordering
323
+ const lines = screen.getAllByTestId(/output-line-/);
324
+ expect(lines[0]).toHaveTextContent('First');
325
+ expect(lines[1]).toHaveTextContent('Third');
326
+ });
327
+
328
+ it('should maintain sequence order with mixed output types', () => {
329
+ const outputs: StreamingOutputData[] = [
330
+ {
331
+ agent_name: 'test_agent',
332
+ run_id: 'run-1',
333
+ output_type: 'stdout',
334
+ content: 'Second',
335
+ sequence: 1,
336
+ is_final: false,
337
+ },
338
+ {
339
+ agent_name: 'test_agent',
340
+ run_id: 'run-1',
341
+ output_type: 'llm_token',
342
+ content: 'First',
343
+ sequence: 0,
344
+ is_final: false,
345
+ },
346
+ {
347
+ agent_name: 'test_agent',
348
+ run_id: 'run-1',
349
+ output_type: 'log',
350
+ content: 'Third',
351
+ sequence: 2,
352
+ is_final: false,
353
+ },
354
+ ];
355
+
356
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
357
+
358
+ const lines = screen.getAllByTestId(/output-line-/);
359
+ expect(lines[0]).toHaveTextContent('First');
360
+ expect(lines[1]).toHaveTextContent('Second');
361
+ expect(lines[2]).toHaveTextContent('Third');
362
+ });
363
+ });
364
+
365
+ describe('Auto-Scroll', () => {
366
+ it('should auto-scroll to bottom when new output arrives', () => {
367
+ const outputs: StreamingOutputData[] = Array.from({ length: 100 }, (_, i) => ({
368
+ agent_name: 'test_agent',
369
+ run_id: 'run-1',
370
+ output_type: 'llm_token' as const,
371
+ content: `Line ${i}`,
372
+ sequence: i,
373
+ is_final: false,
374
+ }));
375
+
376
+ const { rerender } = render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
377
+
378
+ const container = screen.getByTestId('live-output-agent-1');
379
+
380
+ // In test environment, verify scrollTop was set (may not be > 0 in JSDOM)
381
+ // The important thing is that the auto-scroll logic runs
382
+ expect(container.scrollTop).toBeGreaterThanOrEqual(0);
383
+
384
+ // Add more output
385
+ const newOutputs = [
386
+ ...outputs,
387
+ {
388
+ agent_name: 'test_agent',
389
+ run_id: 'run-1',
390
+ output_type: 'llm_token' as const,
391
+ content: 'New line',
392
+ sequence: 100,
393
+ is_final: false,
394
+ },
395
+ ];
396
+
397
+ rerender(<MockLiveOutputTab nodeId="agent-1" outputs={newOutputs} />);
398
+
399
+ // Verify the new line was added (scroll behavior tested implicitly)
400
+ expect(screen.getByText('New line')).toBeInTheDocument();
401
+ });
402
+
403
+ it('should allow disabling auto-scroll', () => {
404
+ const outputs: StreamingOutputData[] = [
405
+ {
406
+ agent_name: 'test_agent',
407
+ run_id: 'run-1',
408
+ output_type: 'llm_token',
409
+ content: 'Test',
410
+ sequence: 0,
411
+ is_final: false,
412
+ },
413
+ ];
414
+
415
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} autoScroll={false} />);
416
+
417
+ const container = screen.getByTestId('live-output-agent-1');
418
+ expect(container.scrollTop).toBe(0);
419
+ });
420
+ });
421
+
422
+ describe('Output Type Rendering', () => {
423
+ it('should render llm_token type correctly', () => {
424
+ const outputs: StreamingOutputData[] = [
425
+ {
426
+ agent_name: 'test_agent',
427
+ run_id: 'run-1',
428
+ output_type: 'llm_token',
429
+ content: 'LLM output',
430
+ sequence: 0,
431
+ is_final: false,
432
+ },
433
+ ];
434
+
435
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
436
+
437
+ const line = screen.getByTestId('output-line-0');
438
+ expect(line).toHaveAttribute('data-output-type', 'llm_token');
439
+ expect(line).toHaveStyle({ color: '#8be9fd' });
440
+ });
441
+
442
+ it('should render log type correctly', () => {
443
+ const outputs: StreamingOutputData[] = [
444
+ {
445
+ agent_name: 'test_agent',
446
+ run_id: 'run-1',
447
+ output_type: 'log',
448
+ content: 'Log message',
449
+ sequence: 0,
450
+ is_final: false,
451
+ },
452
+ ];
453
+
454
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
455
+
456
+ const line = screen.getByTestId('output-line-0');
457
+ expect(line).toHaveAttribute('data-output-type', 'log');
458
+ expect(line).toHaveStyle({ color: '#f1fa8c' });
459
+ });
460
+
461
+ it('should render stdout type correctly', () => {
462
+ const outputs: StreamingOutputData[] = [
463
+ {
464
+ agent_name: 'test_agent',
465
+ run_id: 'run-1',
466
+ output_type: 'stdout',
467
+ content: 'Standard output',
468
+ sequence: 0,
469
+ is_final: false,
470
+ },
471
+ ];
472
+
473
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
474
+
475
+ const line = screen.getByTestId('output-line-0');
476
+ expect(line).toHaveAttribute('data-output-type', 'stdout');
477
+ expect(line).toHaveStyle({ color: '#50fa7b' });
478
+ });
479
+
480
+ it('should render stderr type correctly', () => {
481
+ const outputs: StreamingOutputData[] = [
482
+ {
483
+ agent_name: 'test_agent',
484
+ run_id: 'run-1',
485
+ output_type: 'stderr',
486
+ content: 'Error output',
487
+ sequence: 0,
488
+ is_final: false,
489
+ },
490
+ ];
491
+
492
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
493
+
494
+ const line = screen.getByTestId('output-line-0');
495
+ expect(line).toHaveAttribute('data-output-type', 'stderr');
496
+ expect(line).toHaveStyle({ color: '#ff5555' });
497
+ });
498
+
499
+ it('should display mixed output types with correct styling', () => {
500
+ const outputs: StreamingOutputData[] = [
501
+ {
502
+ agent_name: 'test_agent',
503
+ run_id: 'run-1',
504
+ output_type: 'llm_token',
505
+ content: 'LLM',
506
+ sequence: 0,
507
+ is_final: false,
508
+ },
509
+ {
510
+ agent_name: 'test_agent',
511
+ run_id: 'run-1',
512
+ output_type: 'log',
513
+ content: 'LOG',
514
+ sequence: 1,
515
+ is_final: false,
516
+ },
517
+ {
518
+ agent_name: 'test_agent',
519
+ run_id: 'run-1',
520
+ output_type: 'stdout',
521
+ content: 'STDOUT',
522
+ sequence: 2,
523
+ is_final: false,
524
+ },
525
+ {
526
+ agent_name: 'test_agent',
527
+ run_id: 'run-1',
528
+ output_type: 'stderr',
529
+ content: 'STDERR',
530
+ sequence: 3,
531
+ is_final: false,
532
+ },
533
+ ];
534
+
535
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
536
+
537
+ expect(screen.getByTestId('output-line-0')).toHaveStyle({ color: '#8be9fd' });
538
+ expect(screen.getByTestId('output-line-1')).toHaveStyle({ color: '#f1fa8c' });
539
+ expect(screen.getByTestId('output-line-2')).toHaveStyle({ color: '#50fa7b' });
540
+ expect(screen.getByTestId('output-line-3')).toHaveStyle({ color: '#ff5555' });
541
+ });
542
+ });
543
+
544
+ describe('Final Marker', () => {
545
+ it('should display final marker when is_final is true', () => {
546
+ const outputs: StreamingOutputData[] = [
547
+ {
548
+ agent_name: 'test_agent',
549
+ run_id: 'run-1',
550
+ output_type: 'llm_token',
551
+ content: 'Done',
552
+ sequence: 0,
553
+ is_final: true,
554
+ },
555
+ ];
556
+
557
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
558
+
559
+ expect(screen.getByTestId('final-marker')).toBeInTheDocument();
560
+ expect(screen.getByText('--- End of output ---')).toBeInTheDocument();
561
+ });
562
+
563
+ it('should not display final marker when is_final is false', () => {
564
+ const outputs: StreamingOutputData[] = [
565
+ {
566
+ agent_name: 'test_agent',
567
+ run_id: 'run-1',
568
+ output_type: 'llm_token',
569
+ content: 'In progress',
570
+ sequence: 0,
571
+ is_final: false,
572
+ },
573
+ ];
574
+
575
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
576
+
577
+ expect(screen.queryByTestId('final-marker')).not.toBeInTheDocument();
578
+ });
579
+
580
+ it('should stop accumulation after final marker', () => {
581
+ const outputs: StreamingOutputData[] = [
582
+ {
583
+ agent_name: 'test_agent',
584
+ run_id: 'run-1',
585
+ output_type: 'llm_token',
586
+ content: 'Last',
587
+ sequence: 0,
588
+ is_final: true,
589
+ },
590
+ ];
591
+
592
+ const { rerender } = render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
593
+
594
+ expect(screen.getByTestId('final-marker')).toBeInTheDocument();
595
+
596
+ // Try to add more output (should be ignored by actual implementation)
597
+ const newOutputs: StreamingOutputData[] = [
598
+ ...outputs,
599
+ {
600
+ agent_name: 'test_agent',
601
+ run_id: 'run-1',
602
+ output_type: 'llm_token' as const,
603
+ content: 'Should not appear',
604
+ sequence: 1,
605
+ is_final: false,
606
+ },
607
+ ];
608
+
609
+ rerender(<MockLiveOutputTab nodeId="agent-1" outputs={newOutputs} />);
610
+
611
+ // Note: Mock shows it, but actual implementation should filter it out
612
+ // This test documents expected behavior
613
+ expect(screen.getByTestId('final-marker')).toBeInTheDocument();
614
+ });
615
+ });
616
+
617
+ describe('Virtualization Performance', () => {
618
+ it('should handle 1000+ lines without performance degradation', () => {
619
+ const startTime = performance.now();
620
+
621
+ const outputs: StreamingOutputData[] = Array.from({ length: 1000 }, (_, i) => ({
622
+ agent_name: 'test_agent',
623
+ run_id: 'run-1',
624
+ output_type: 'llm_token' as const,
625
+ content: `Line ${i}: ${'x'.repeat(100)}`,
626
+ sequence: i,
627
+ is_final: false,
628
+ }));
629
+
630
+ render(<MockVirtualizedOutputTab nodeId="agent-1" outputs={outputs} />);
631
+
632
+ const endTime = performance.now();
633
+ const renderTime = endTime - startTime;
634
+
635
+ // Virtualized rendering should be fast (< 100ms for 1000 lines)
636
+ expect(renderTime).toBeLessThan(100);
637
+
638
+ expect(screen.getByTestId('total-lines')).toHaveTextContent('1000');
639
+ });
640
+
641
+ it('should only render visible lines in virtualized mode', () => {
642
+ const outputs: StreamingOutputData[] = Array.from({ length: 1000 }, (_, i) => ({
643
+ agent_name: 'test_agent',
644
+ run_id: 'run-1',
645
+ output_type: 'llm_token' as const,
646
+ content: `Line ${i}`,
647
+ sequence: i,
648
+ is_final: false,
649
+ }));
650
+
651
+ render(<MockVirtualizedOutputTab nodeId="agent-1" outputs={outputs} />);
652
+
653
+ // Should only render ~50 visible lines (not all 1000)
654
+ const visibleLines = screen.getAllByTestId(/virtual-line-/);
655
+ expect(visibleLines.length).toBeLessThan(100);
656
+ expect(visibleLines.length).toBeGreaterThan(0);
657
+ });
658
+
659
+ it('should handle rapid updates with many lines', () => {
660
+ const { rerender } = render(
661
+ <MockVirtualizedOutputTab nodeId="agent-1" outputs={[]} />
662
+ );
663
+
664
+ // Add 100 lines at a time
665
+ for (let batch = 0; batch < 10; batch++) {
666
+ const outputs: StreamingOutputData[] = Array.from(
667
+ { length: (batch + 1) * 100 },
668
+ (_, i) => ({
669
+ agent_name: 'test_agent',
670
+ run_id: 'run-1',
671
+ output_type: 'llm_token' as const,
672
+ content: `Line ${i}`,
673
+ sequence: i,
674
+ is_final: false,
675
+ })
676
+ );
677
+
678
+ const startTime = performance.now();
679
+ rerender(<MockVirtualizedOutputTab nodeId="agent-1" outputs={outputs} />);
680
+ const endTime = performance.now();
681
+
682
+ // Each update should be fast
683
+ expect(endTime - startTime).toBeLessThan(50);
684
+ }
685
+
686
+ expect(screen.getByTestId('total-lines')).toHaveTextContent('1000');
687
+ });
688
+ });
689
+
690
+ describe('Edge Cases', () => {
691
+ it('should handle empty content', () => {
692
+ const outputs: StreamingOutputData[] = [
693
+ {
694
+ agent_name: 'test_agent',
695
+ run_id: 'run-1',
696
+ output_type: 'llm_token',
697
+ content: '',
698
+ sequence: 0,
699
+ is_final: false,
700
+ },
701
+ ];
702
+
703
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
704
+
705
+ expect(screen.getByTestId('output-line-0')).toBeInTheDocument();
706
+ });
707
+
708
+ it('should handle multiline content', () => {
709
+ const outputs: StreamingOutputData[] = [
710
+ {
711
+ agent_name: 'test_agent',
712
+ run_id: 'run-1',
713
+ output_type: 'stdout',
714
+ content: 'Line 1\nLine 2\nLine 3',
715
+ sequence: 0,
716
+ is_final: false,
717
+ },
718
+ ];
719
+
720
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
721
+
722
+ const line = screen.getByTestId('output-line-0');
723
+ expect(line).toHaveTextContent('Line 1');
724
+ expect(line).toHaveTextContent('Line 2');
725
+ expect(line).toHaveTextContent('Line 3');
726
+ expect(line).toHaveStyle({ whiteSpace: 'pre-wrap' });
727
+ });
728
+
729
+ it('should handle special characters', () => {
730
+ const outputs: StreamingOutputData[] = [
731
+ {
732
+ agent_name: 'test_agent',
733
+ run_id: 'run-1',
734
+ output_type: 'llm_token',
735
+ content: '<script>alert("xss")</script>',
736
+ sequence: 0,
737
+ is_final: false,
738
+ },
739
+ ];
740
+
741
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
742
+
743
+ // Content should be escaped (React does this by default)
744
+ const line = screen.getByTestId('output-line-0');
745
+ expect(line.textContent).toBe('<script>alert("xss")</script>');
746
+ });
747
+
748
+ it('should handle duplicate sequence numbers', () => {
749
+ const outputs: StreamingOutputData[] = [
750
+ {
751
+ agent_name: 'test_agent',
752
+ run_id: 'run-1',
753
+ output_type: 'llm_token',
754
+ content: 'First',
755
+ sequence: 0,
756
+ is_final: false,
757
+ },
758
+ {
759
+ agent_name: 'test_agent',
760
+ run_id: 'run-1',
761
+ output_type: 'llm_token',
762
+ content: 'Duplicate',
763
+ sequence: 0,
764
+ is_final: false,
765
+ },
766
+ ];
767
+
768
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
769
+
770
+ // Both should be displayed (implementation detail)
771
+ expect(screen.getAllByTestId(/output-line-/).length).toBe(2);
772
+ });
773
+
774
+ it('should handle very long content lines', () => {
775
+ const outputs: StreamingOutputData[] = [
776
+ {
777
+ agent_name: 'test_agent',
778
+ run_id: 'run-1',
779
+ output_type: 'llm_token',
780
+ content: 'x'.repeat(10000),
781
+ sequence: 0,
782
+ is_final: false,
783
+ },
784
+ ];
785
+
786
+ render(<MockLiveOutputTab nodeId="agent-1" outputs={outputs} />);
787
+
788
+ const line = screen.getByTestId('output-line-0');
789
+ expect(line.textContent?.length).toBe(10000);
790
+ });
791
+ });
792
+ });