flock-core 0.4.542__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.542.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.542.dist-info/METADATA +0 -676
  496. flock_core-0.4.542.dist-info/RECORD +0 -572
  497. flock_core-0.4.542.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.542.dist-info → flock_core-0.5.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,559 @@
1
+ """DashboardEventCollector - captures agent lifecycle events for real-time dashboard.
2
+
3
+ This component hooks into the agent execution lifecycle to emit WebSocket events.
4
+ Phase 1: Events stored in in-memory buffer (max 100 events).
5
+ Phase 3: Extended to emit via WebSocket using WebSocketManager.
6
+ """
7
+
8
+ import asyncio
9
+ import hashlib
10
+ import json
11
+ import traceback
12
+ from collections import defaultdict, deque
13
+ from dataclasses import dataclass, field
14
+ from datetime import datetime, timezone
15
+ from typing import TYPE_CHECKING, Any, Optional
16
+
17
+ from pydantic import PrivateAttr
18
+
19
+ from flock.components import AgentComponent
20
+ from flock.dashboard.events import (
21
+ AgentActivatedEvent,
22
+ AgentCompletedEvent,
23
+ AgentErrorEvent,
24
+ MessagePublishedEvent,
25
+ SubscriptionInfo,
26
+ VisibilitySpec,
27
+ )
28
+ from flock.dashboard.models.graph import GraphRun, GraphState
29
+ from flock.logging.logging import get_logger
30
+ from flock.runtime import Context
31
+ from flock.store import AgentSnapshotRecord, BlackboardStore
32
+
33
+
34
+ logger = get_logger("dashboard.collector")
35
+
36
+ if TYPE_CHECKING: # pragma: no cover - type hints only
37
+ from flock.agent import Agent
38
+ from flock.artifacts import Artifact
39
+ from flock.dashboard.websocket import WebSocketManager
40
+
41
+
42
+ @dataclass(slots=True)
43
+ class RunRecord:
44
+ run_id: str
45
+ agent_name: str
46
+ correlation_id: str = ""
47
+ status: str = "active"
48
+ consumed_artifacts: list[str] = field(default_factory=list)
49
+ produced_artifacts: list[str] = field(default_factory=list)
50
+ duration_ms: float | None = None
51
+ started_at: datetime | None = None
52
+ completed_at: datetime | None = None
53
+ metrics: dict[str, Any] = field(default_factory=dict)
54
+ error_message: str | None = None
55
+
56
+ def to_graph_run(self) -> GraphRun:
57
+ status = self.status if self.status in {"active", "completed", "error"} else "active"
58
+ return GraphRun(
59
+ run_id=self.run_id,
60
+ agent_name=self.agent_name,
61
+ correlation_id=self.correlation_id or None,
62
+ status=status, # type: ignore[arg-type]
63
+ consumed_artifacts=list(self.consumed_artifacts),
64
+ produced_artifacts=list(self.produced_artifacts),
65
+ duration_ms=self.duration_ms,
66
+ started_at=self.started_at,
67
+ completed_at=self.completed_at,
68
+ metrics=dict(self.metrics),
69
+ error_message=self.error_message,
70
+ )
71
+
72
+
73
+ @dataclass(slots=True)
74
+ class AgentSnapshot:
75
+ name: str
76
+ description: str
77
+ subscriptions: list[str]
78
+ output_types: list[str]
79
+ labels: list[str]
80
+ first_seen: datetime
81
+ last_seen: datetime
82
+ signature: str
83
+
84
+
85
+ class DashboardEventCollector(AgentComponent):
86
+ """Collects agent lifecycle events for dashboard visualization.
87
+
88
+ Implements AgentComponent interface to hook into agent execution:
89
+ - on_pre_consume: emits agent_activated
90
+ - on_post_publish: emits message_published
91
+ - on_terminate: emits agent_completed
92
+ - on_error: emits agent_error
93
+
94
+ Phase 1: Events stored in in-memory deque (max 100, LRU eviction).
95
+ Phase 3: Emits events via WebSocket using WebSocketManager.
96
+ """
97
+
98
+ # Use PrivateAttr for non-Pydantic fields (AgentComponent extends BaseModel)
99
+ _events: deque[
100
+ AgentActivatedEvent | MessagePublishedEvent | AgentCompletedEvent | AgentErrorEvent
101
+ ] = PrivateAttr(default=None)
102
+
103
+ # Track run start times for duration calculation
104
+ _run_start_times: dict[str, float] = PrivateAttr(default_factory=dict)
105
+
106
+ # WebSocketManager for broadcasting events
107
+ _websocket_manager: Optional["WebSocketManager"] = PrivateAttr(default=None)
108
+
109
+ # Graph assembly helpers
110
+ _graph_lock: asyncio.Lock = PrivateAttr(default_factory=asyncio.Lock)
111
+ _run_registry: dict[str, RunRecord] = PrivateAttr(default_factory=dict)
112
+ _artifact_consumers: dict[str, set[str]] = PrivateAttr(default_factory=lambda: defaultdict(set))
113
+ _agent_status: dict[str, str] = PrivateAttr(default_factory=dict)
114
+ _agent_snapshots: dict[str, AgentSnapshot] = PrivateAttr(default_factory=dict)
115
+
116
+ def __init__(self, *, store: BlackboardStore | None = None, **data):
117
+ super().__init__(**data)
118
+ # In-memory buffer with max 100 events (LRU eviction)
119
+ self._events = deque(maxlen=100)
120
+ self._run_start_times = {}
121
+ self._websocket_manager = None
122
+ self._graph_lock = asyncio.Lock()
123
+ self._run_registry = {}
124
+ self._artifact_consumers = defaultdict(set)
125
+ self._agent_status = {}
126
+ self._store: BlackboardStore | None = store
127
+ self._persistent_loaded = False
128
+ self._agent_snapshots = {}
129
+
130
+ def set_websocket_manager(self, manager: "WebSocketManager") -> None:
131
+ """Set WebSocketManager for broadcasting events.
132
+
133
+ Args:
134
+ manager: WebSocketManager instance to use for broadcasting
135
+ """
136
+ self._websocket_manager = manager
137
+
138
+ @property
139
+ def events(self) -> deque:
140
+ """Access events buffer."""
141
+ return self._events
142
+
143
+ async def on_pre_consume(
144
+ self, agent: "Agent", ctx: Context, inputs: list["Artifact"]
145
+ ) -> list["Artifact"]:
146
+ """Emit agent_activated event when agent begins consuming.
147
+
148
+ Args:
149
+ agent: The agent that is consuming
150
+ ctx: Execution context with correlation_id
151
+ inputs: Artifacts being consumed
152
+
153
+ Returns:
154
+ Unmodified inputs (pass-through)
155
+ """
156
+ # Record start time for duration calculation
157
+ self._run_start_times[ctx.task_id] = datetime.now(timezone.utc).timestamp()
158
+
159
+ # Extract consumed types and artifact IDs
160
+ consumed_types = list({artifact.type for artifact in inputs})
161
+ consumed_artifacts = [str(artifact.id) for artifact in inputs]
162
+
163
+ # Extract produced types from agent outputs
164
+ produced_types = [output.spec.type_name for output in agent.outputs]
165
+
166
+ correlation_id = str(ctx.correlation_id) if ctx.correlation_id else ""
167
+ async with self._graph_lock:
168
+ run = self._ensure_run_record(
169
+ run_id=ctx.task_id,
170
+ agent_name=agent.name,
171
+ correlation_id=correlation_id,
172
+ ensure_started=True,
173
+ )
174
+ run.status = "active"
175
+ for artifact_id in consumed_artifacts:
176
+ if artifact_id not in run.consumed_artifacts:
177
+ run.consumed_artifacts.append(artifact_id)
178
+ self._artifact_consumers[artifact_id].add(agent.name)
179
+ self._agent_status[agent.name] = "running"
180
+ await self._update_agent_snapshot_locked(agent)
181
+
182
+ # Build subscription info from agent's subscriptions
183
+ subscription_info = SubscriptionInfo(from_agents=[], channels=[], mode="both")
184
+
185
+ if agent.subscriptions:
186
+ # Get first subscription's config (agents typically have one)
187
+ sub = agent.subscriptions[0]
188
+ subscription_info.from_agents = list(sub.from_agents) if sub.from_agents else []
189
+ subscription_info.channels = list(sub.channels) if sub.channels else []
190
+ subscription_info.mode = sub.mode
191
+
192
+ # Create and store event
193
+ event = AgentActivatedEvent(
194
+ correlation_id=correlation_id,
195
+ agent_name=agent.name,
196
+ agent_id=agent.name,
197
+ run_id=ctx.task_id, # Unique ID for this agent run
198
+ consumed_types=consumed_types,
199
+ consumed_artifacts=consumed_artifacts,
200
+ produced_types=produced_types,
201
+ subscription_info=subscription_info,
202
+ labels=list(agent.labels),
203
+ tenant_id=agent.tenant_id,
204
+ max_concurrency=agent.max_concurrency,
205
+ )
206
+
207
+ self._events.append(event)
208
+ logger.info(f"Agent activated: {agent.name} (correlation_id={event.correlation_id})")
209
+
210
+ # Broadcast via WebSocket if manager is configured
211
+ if self._websocket_manager:
212
+ await self._websocket_manager.broadcast(event)
213
+ else:
214
+ logger.warning("WebSocket manager not configured, event not broadcast")
215
+
216
+ return inputs
217
+
218
+ async def on_post_publish(self, agent: "Agent", ctx: Context, artifact: "Artifact") -> None:
219
+ """Emit message_published event when artifact is published.
220
+
221
+ Args:
222
+ agent: The agent that published the artifact
223
+ ctx: Execution context with correlation_id
224
+ artifact: The published artifact
225
+ """
226
+ # Convert visibility to VisibilitySpec
227
+ visibility_spec = self._convert_visibility(artifact.visibility)
228
+ correlation_id = str(ctx.correlation_id) if ctx.correlation_id else ""
229
+ artifact_id = str(artifact.id)
230
+
231
+ async with self._graph_lock:
232
+ run = self._ensure_run_record(
233
+ run_id=ctx.task_id,
234
+ agent_name=agent.name,
235
+ correlation_id=correlation_id,
236
+ ensure_started=True,
237
+ )
238
+ run.status = "active"
239
+ if artifact_id not in run.produced_artifacts:
240
+ run.produced_artifacts.append(artifact_id)
241
+ await self._update_agent_snapshot_locked(agent)
242
+
243
+ # Create and store event
244
+ event = MessagePublishedEvent(
245
+ correlation_id=correlation_id,
246
+ artifact_id=str(artifact.id),
247
+ artifact_type=artifact.type,
248
+ produced_by=artifact.produced_by,
249
+ payload=artifact.payload,
250
+ visibility=visibility_spec,
251
+ tags=list(artifact.tags) if artifact.tags else [],
252
+ partition_key=artifact.partition_key,
253
+ version=artifact.version,
254
+ consumers=[], # Phase 1: empty, Phase 3: compute from subscription matching
255
+ )
256
+
257
+ self._events.append(event)
258
+ logger.info(
259
+ f"Message published: {artifact.type} by {artifact.produced_by} (correlation_id={event.correlation_id})"
260
+ )
261
+
262
+ # Broadcast via WebSocket if manager is configured
263
+ if self._websocket_manager:
264
+ await self._websocket_manager.broadcast(event)
265
+ else:
266
+ logger.warning("WebSocket manager not configured, event not broadcast")
267
+
268
+ async def on_terminate(self, agent: "Agent", ctx: Context) -> None:
269
+ """Emit agent_completed event when agent finishes successfully.
270
+
271
+ Args:
272
+ agent: The agent that completed
273
+ ctx: Execution context with final state
274
+ """
275
+ # Calculate duration
276
+ start_time = self._run_start_times.get(ctx.task_id)
277
+ if start_time:
278
+ duration_ms = (datetime.now(timezone.utc).timestamp() - start_time) * 1000
279
+ del self._run_start_times[ctx.task_id]
280
+ else:
281
+ duration_ms = 0.0
282
+
283
+ # Extract artifacts produced from context state (if tracked)
284
+ artifacts_produced = ctx.state.get("artifacts_produced", [])
285
+ if not isinstance(artifacts_produced, list):
286
+ artifacts_produced = []
287
+
288
+ # Extract metrics from context state (if tracked)
289
+ metrics = ctx.state.get("metrics", {})
290
+ if not isinstance(metrics, dict):
291
+ metrics = {}
292
+
293
+ # Create and store event
294
+ event = AgentCompletedEvent(
295
+ correlation_id=str(ctx.correlation_id) if ctx.correlation_id else "",
296
+ agent_name=agent.name,
297
+ run_id=ctx.task_id,
298
+ duration_ms=duration_ms,
299
+ artifacts_produced=artifacts_produced,
300
+ metrics=metrics,
301
+ final_state=dict(ctx.state),
302
+ )
303
+
304
+ self._events.append(event)
305
+
306
+ async with self._graph_lock:
307
+ correlation_id = str(ctx.correlation_id) if ctx.correlation_id else ""
308
+ run = self._ensure_run_record(
309
+ run_id=ctx.task_id,
310
+ agent_name=agent.name,
311
+ correlation_id=correlation_id,
312
+ ensure_started=True,
313
+ )
314
+ run.status = "completed"
315
+ run.duration_ms = duration_ms
316
+ run.metrics = dict(metrics)
317
+ run.completed_at = datetime.now(timezone.utc)
318
+ for artifact_id in artifacts_produced:
319
+ if artifact_id not in run.produced_artifacts:
320
+ run.produced_artifacts.append(artifact_id)
321
+ self._agent_status[agent.name] = "idle"
322
+ await self._update_agent_snapshot_locked(agent)
323
+
324
+ # Broadcast via WebSocket if manager is configured
325
+ if self._websocket_manager:
326
+ await self._websocket_manager.broadcast(event)
327
+
328
+ async def on_error(self, agent: "Agent", ctx: Context, error: Exception) -> None:
329
+ """Emit agent_error event when agent execution fails.
330
+
331
+ Args:
332
+ agent: The agent that failed
333
+ ctx: Execution context
334
+ error: The exception that was raised
335
+ """
336
+ # Get error details
337
+ error_type = type(error).__name__
338
+ error_message = str(error)
339
+ # Use traceback.format_exception to get traceback from exception object
340
+ error_traceback = "".join(
341
+ traceback.format_exception(type(error), error, error.__traceback__)
342
+ )
343
+ failed_at = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
344
+
345
+ # Clean up start time tracking
346
+ if ctx.task_id in self._run_start_times:
347
+ del self._run_start_times[ctx.task_id]
348
+
349
+ # Create and store event
350
+ event = AgentErrorEvent(
351
+ correlation_id=str(ctx.correlation_id) if ctx.correlation_id else "",
352
+ agent_name=agent.name,
353
+ run_id=ctx.task_id,
354
+ error_type=error_type,
355
+ error_message=error_message,
356
+ traceback=error_traceback,
357
+ failed_at=failed_at,
358
+ )
359
+
360
+ self._events.append(event)
361
+
362
+ async with self._graph_lock:
363
+ correlation_id = str(ctx.correlation_id) if ctx.correlation_id else ""
364
+ run = self._ensure_run_record(
365
+ run_id=ctx.task_id,
366
+ agent_name=agent.name,
367
+ correlation_id=correlation_id,
368
+ ensure_started=True,
369
+ )
370
+ run.status = "error"
371
+ run.error_message = error_message
372
+ run.completed_at = datetime.now(timezone.utc)
373
+ self._agent_status[agent.name] = "error"
374
+ await self._update_agent_snapshot_locked(agent)
375
+
376
+ # Broadcast via WebSocket if manager is configured
377
+ if self._websocket_manager:
378
+ await self._websocket_manager.broadcast(event)
379
+
380
+ async def snapshot_graph_state(self) -> GraphState:
381
+ """Return a thread-safe snapshot of runs, consumptions, and agent status."""
382
+ async with self._graph_lock:
383
+ consumptions = {
384
+ artifact_id: sorted(consumers)
385
+ for artifact_id, consumers in self._artifact_consumers.items()
386
+ }
387
+ runs = [record.to_graph_run() for record in self._run_registry.values()]
388
+ agent_status = dict(self._agent_status)
389
+ return GraphState(consumptions=consumptions, runs=runs, agent_status=agent_status)
390
+
391
+ async def snapshot_agent_registry(self) -> dict[str, AgentSnapshot]:
392
+ """Return a snapshot of all known agents (active and inactive)."""
393
+ await self.load_persistent_snapshots()
394
+ async with self._graph_lock:
395
+ return {
396
+ name: self._clone_snapshot(snapshot)
397
+ for name, snapshot in self._agent_snapshots.items()
398
+ }
399
+
400
+ async def load_persistent_snapshots(self) -> None:
401
+ if self._store is None or self._persistent_loaded:
402
+ return
403
+ records = await self._store.load_agent_snapshots()
404
+ async with self._graph_lock:
405
+ for record in records:
406
+ self._agent_snapshots[record.agent_name] = AgentSnapshot(
407
+ name=record.agent_name,
408
+ description=record.description,
409
+ subscriptions=list(record.subscriptions),
410
+ output_types=list(record.output_types),
411
+ labels=list(record.labels),
412
+ first_seen=record.first_seen,
413
+ last_seen=record.last_seen,
414
+ signature=record.signature,
415
+ )
416
+ self._persistent_loaded = True
417
+
418
+ async def clear_agent_registry(self) -> None:
419
+ """Clear cached agent metadata (for explicit resets)."""
420
+ async with self._graph_lock:
421
+ self._agent_snapshots.clear()
422
+ if self._store is not None:
423
+ await self._store.clear_agent_snapshots()
424
+
425
+ def _ensure_run_record(
426
+ self,
427
+ *,
428
+ run_id: str,
429
+ agent_name: str,
430
+ correlation_id: str,
431
+ ensure_started: bool = False,
432
+ ) -> RunRecord:
433
+ """Internal helper. Caller must hold _graph_lock."""
434
+ run = self._run_registry.get(run_id)
435
+ if not run:
436
+ run = RunRecord(
437
+ run_id=run_id,
438
+ agent_name=agent_name,
439
+ correlation_id=correlation_id,
440
+ started_at=datetime.now(timezone.utc) if ensure_started else None,
441
+ )
442
+ self._run_registry[run_id] = run
443
+ else:
444
+ run.agent_name = agent_name
445
+ if correlation_id:
446
+ run.correlation_id = correlation_id
447
+ if ensure_started and run.started_at is None:
448
+ run.started_at = datetime.now(timezone.utc)
449
+ return run
450
+
451
+ async def _update_agent_snapshot_locked(self, agent: "Agent") -> None:
452
+ now = datetime.now(timezone.utc)
453
+ description = agent.description or ""
454
+ subscriptions = sorted(
455
+ {
456
+ type_name
457
+ for subscription in getattr(agent, "subscriptions", [])
458
+ for type_name in getattr(subscription, "type_names", [])
459
+ }
460
+ )
461
+ output_types = sorted(
462
+ {
463
+ output.spec.type_name
464
+ for output in getattr(agent, "outputs", [])
465
+ if getattr(output, "spec", None) is not None
466
+ and getattr(output.spec, "type_name", "")
467
+ }
468
+ )
469
+ labels = sorted(agent.labels)
470
+
471
+ signature_payload = {
472
+ "description": description,
473
+ "subscriptions": subscriptions,
474
+ "output_types": output_types,
475
+ "labels": labels,
476
+ }
477
+ signature = hashlib.sha256(
478
+ json.dumps(signature_payload, sort_keys=True).encode("utf-8")
479
+ ).hexdigest()
480
+
481
+ snapshot = self._agent_snapshots.get(agent.name)
482
+ if snapshot is None:
483
+ snapshot = AgentSnapshot(
484
+ name=agent.name,
485
+ description=description,
486
+ subscriptions=subscriptions,
487
+ output_types=output_types,
488
+ labels=labels,
489
+ first_seen=now,
490
+ last_seen=now,
491
+ signature=signature,
492
+ )
493
+ self._agent_snapshots[agent.name] = snapshot
494
+ else:
495
+ snapshot.description = description
496
+ snapshot.subscriptions = subscriptions
497
+ snapshot.output_types = output_types
498
+ snapshot.labels = labels
499
+ snapshot.last_seen = now
500
+ snapshot.signature = signature
501
+
502
+ if self._store is not None:
503
+ record = self._snapshot_to_record(snapshot)
504
+ await self._store.upsert_agent_snapshot(record)
505
+
506
+ @staticmethod
507
+ def _clone_snapshot(snapshot: AgentSnapshot) -> AgentSnapshot:
508
+ return AgentSnapshot(
509
+ name=snapshot.name,
510
+ description=snapshot.description,
511
+ subscriptions=list(snapshot.subscriptions),
512
+ output_types=list(snapshot.output_types),
513
+ labels=list(snapshot.labels),
514
+ first_seen=snapshot.first_seen,
515
+ last_seen=snapshot.last_seen,
516
+ signature=snapshot.signature,
517
+ )
518
+
519
+ def _snapshot_to_record(self, snapshot: AgentSnapshot) -> AgentSnapshotRecord:
520
+ return AgentSnapshotRecord(
521
+ agent_name=snapshot.name,
522
+ description=snapshot.description,
523
+ subscriptions=list(snapshot.subscriptions),
524
+ output_types=list(snapshot.output_types),
525
+ labels=list(snapshot.labels),
526
+ first_seen=snapshot.first_seen,
527
+ last_seen=snapshot.last_seen,
528
+ signature=snapshot.signature,
529
+ )
530
+
531
+ def _convert_visibility(self, visibility) -> VisibilitySpec:
532
+ """Convert flock.visibility.Visibility to VisibilitySpec.
533
+
534
+ Args:
535
+ visibility: Visibility object from artifact
536
+
537
+ Returns:
538
+ VisibilitySpec for event serialization
539
+ """
540
+ # Get visibility kind from class name, stripping "Visibility" suffix
541
+ class_name = type(visibility).__name__
542
+ kind = class_name[: -len("Visibility")] if class_name.endswith("Visibility") else class_name
543
+
544
+ spec = VisibilitySpec(kind=kind)
545
+
546
+ # Extract type-specific fields
547
+ if kind == "Private":
548
+ spec.agents = list(visibility.agents) if hasattr(visibility, "agents") else []
549
+ elif kind == "Labelled":
550
+ spec.required_labels = (
551
+ list(visibility.required_labels) if hasattr(visibility, "required_labels") else []
552
+ )
553
+ elif kind == "Tenant":
554
+ spec.tenant_id = visibility.tenant_id if hasattr(visibility, "tenant_id") else None
555
+
556
+ return spec
557
+
558
+
559
+ __all__ = ["AgentSnapshot", "DashboardEventCollector"]