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,848 @@
1
+ /**
2
+ * IndexedDB Persistence Service for Flock Flow Dashboard
3
+ *
4
+ * Provides persistent storage for dashboard data with LRU eviction strategy.
5
+ * Implements separate layout storage for Agent View and Blackboard View.
6
+ *
7
+ * SPECIFICATION: docs/specs/003-real-time-dashboard/DATA_MODEL.md Section 3 & 6
8
+ *
9
+ * Database: flock_dashboard_v1 (version 1)
10
+ * Object Stores:
11
+ * - agents: Agent metadata and history (key: agent_id)
12
+ * - artifacts: Message data and lineage (key: artifact_id)
13
+ * - runs: Agent execution records (key: run_id)
14
+ * - layout_agent_view: Node positions for Agent View (key: node_id)
15
+ * - layout_blackboard_view: Node positions for Blackboard View (key: node_id)
16
+ * - module_instances: Module window positions and state (key: instance_id)
17
+ * - sessions: Session metadata for LRU eviction (key: session_id)
18
+ * - filters: Saved filter presets (key: filter_id)
19
+ *
20
+ * LRU Strategy:
21
+ * - Trigger eviction at 80% quota (EVICTION_THRESHOLD)
22
+ * - Evict until 60% quota (EVICTION_TARGET)
23
+ * - Evict oldest sessions first (based on created_at)
24
+ * - Typical session: ~154 KB, ~324 sessions before eviction
25
+ */
26
+
27
+ // Note: idb library is available for production use, but we use raw IndexedDB API
28
+ // for better compatibility with test mocks
29
+
30
+ import type { FilterSnapshot } from '../types/filters';
31
+
32
+ // Database constants
33
+ const DB_NAME = 'flock_dashboard_v1';
34
+ const DB_VERSION = 1;
35
+ const EVICTION_THRESHOLD = 0.8; // Trigger eviction at 80% quota
36
+ const EVICTION_TARGET = 0.6; // Evict until 60% quota
37
+ const MAX_RECORDS_PER_STORE = 500; // LRU record limit per store
38
+
39
+ // Type definitions matching DATA_MODEL.md Section 3.2
40
+
41
+ interface AgentRecord {
42
+ agent_id: string; // PRIMARY KEY
43
+ agent_name: string;
44
+ labels: string[];
45
+ tenant_id: string | null; // INDEXED
46
+ max_concurrency: number;
47
+ consumes_types: string[];
48
+ from_agents: string[];
49
+ channels: string[];
50
+ run_history: string[]; // [run_id] - last 100 runs
51
+ total_runs: number;
52
+ total_errors: number;
53
+ first_seen: string; // ISO timestamp
54
+ last_active: string; // INDEXED - for LRU eviction
55
+ }
56
+
57
+ interface ArtifactRecord {
58
+ artifact_id: string; // PRIMARY KEY (UUID)
59
+ artifact_type: string; // INDEXED
60
+ produced_by: string; // INDEXED
61
+ correlation_id: string; // INDEXED - critical for filtering
62
+ payload: Record<string, any>;
63
+ payload_preview: string;
64
+ visibility: VisibilitySpec;
65
+ tags: string[];
66
+ partition_key: string | null;
67
+ version: number;
68
+ consumed_by: string[];
69
+ derived_from: string[];
70
+ published_at: string; // INDEXED - for time range filtering
71
+ }
72
+
73
+ interface VisibilitySpec {
74
+ kind: string;
75
+ [key: string]: any;
76
+ }
77
+
78
+ interface RunRecord {
79
+ run_id: string; // PRIMARY KEY (Context.task_id)
80
+ agent_name: string; // INDEXED
81
+ correlation_id: string; // INDEXED
82
+ status: 'active' | 'completed' | 'error';
83
+ started_at: string; // INDEXED - for time range filtering
84
+ completed_at: string | null;
85
+ duration_ms: number | null;
86
+ consumed_artifacts: string[];
87
+ produced_artifacts: string[];
88
+ output_stream: OutputChunk[]; // Stored as JSON array
89
+ metrics: Record<string, number>;
90
+ final_state: Record<string, any>;
91
+ error_type: string | null;
92
+ error_message: string | null;
93
+ traceback: string | null;
94
+ }
95
+
96
+ interface OutputChunk {
97
+ sequence: number;
98
+ output_type: string;
99
+ content: string;
100
+ timestamp: string;
101
+ }
102
+
103
+ interface ModuleInstanceRecord {
104
+ instance_id: string; // PRIMARY KEY
105
+ type: string; // Module type (e.g., 'eventLog')
106
+ position: { x: number; y: number };
107
+ size: { width: number; height: number };
108
+ visible: boolean;
109
+ created_at: string; // ISO timestamp
110
+ updated_at: string; // ISO timestamp
111
+ }
112
+
113
+ interface LayoutRecord {
114
+ node_id: string; // PRIMARY KEY (agent_name or artifact_id)
115
+ x: number;
116
+ y: number;
117
+ last_updated: string; // ISO timestamp
118
+ }
119
+
120
+ interface SessionRecord {
121
+ session_id: string; // PRIMARY KEY
122
+ created_at: string; // INDEXED - for LRU eviction
123
+ last_activity: string; // ISO timestamp
124
+ artifact_count: number;
125
+ run_count: number;
126
+ size_estimate_bytes: number; // Approximate storage size
127
+ }
128
+
129
+ // Future use: Saved filter presets
130
+ export interface FilterRecord {
131
+ filter_id: string; // PRIMARY KEY
132
+ name: string;
133
+ filters: FilterSnapshot;
134
+ created_at: number;
135
+ }
136
+
137
+ // Helper to wrap IDBRequest in Promise
138
+ function promisifyRequest<T>(request: IDBRequest<T>): Promise<T> {
139
+ return new Promise((resolve, reject) => {
140
+ request.onsuccess = () => resolve(request.result);
141
+ request.onerror = () => reject(request.error);
142
+ });
143
+ }
144
+
145
+ /**
146
+ * IndexedDB Service for persistent dashboard storage
147
+ * Implements LRU eviction and graceful degradation
148
+ */
149
+ export class IndexedDBService {
150
+ db: IDBDatabase | null = null;
151
+ private inMemoryStore: Map<string, Map<string, any>> = new Map();
152
+ private available = false;
153
+
154
+ /**
155
+ * Initialize database with schema and indexes
156
+ */
157
+ async initialize(): Promise<void> {
158
+ // Check if IndexedDB is available
159
+ if (typeof indexedDB === 'undefined') {
160
+ console.warn('[IndexedDB] IndexedDB not available, using in-memory fallback');
161
+ this.available = false;
162
+ this.initializeInMemoryStores();
163
+ return;
164
+ }
165
+
166
+ try {
167
+ // Use indexedDB.open directly to work with test mocks
168
+ const request = indexedDB.open(DB_NAME, DB_VERSION);
169
+
170
+ this.db = await new Promise<any>(async (resolve, reject) => {
171
+ request.onerror = () => {
172
+ reject(request.error);
173
+ };
174
+ request.onsuccess = () => {
175
+ resolve(request.result);
176
+ };
177
+ request.onupgradeneeded = (event: IDBVersionChangeEvent) => {
178
+ const db = (event.target as IDBOpenDBRequest).result;
179
+ const storeNames: string[] = [];
180
+
181
+ // Helper to add index name to store's indexNames (mock workaround)
182
+ const addIndexName = (store: any, indexName: string) => {
183
+ if (Array.isArray(store.indexNames) && !store.indexNames.includes(indexName)) {
184
+ store.indexNames.push(indexName);
185
+ }
186
+ };
187
+
188
+ // Create agents store with indexes
189
+ if (!db.objectStoreNames.contains('agents')) {
190
+ const agentsStore = db.createObjectStore('agents', { keyPath: 'agent_id' });
191
+ agentsStore.createIndex('last_active', 'last_active', { unique: false });
192
+ addIndexName(agentsStore, 'last_active');
193
+ agentsStore.createIndex('tenant_id', 'tenant_id', { unique: false });
194
+ addIndexName(agentsStore, 'tenant_id');
195
+ storeNames.push('agents');
196
+ }
197
+
198
+ // Create artifacts store with indexes
199
+ if (!db.objectStoreNames.contains('artifacts')) {
200
+ const artifactsStore = db.createObjectStore('artifacts', { keyPath: 'artifact_id' });
201
+ artifactsStore.createIndex('correlation_id', 'correlation_id', { unique: false });
202
+ addIndexName(artifactsStore, 'correlation_id');
203
+ artifactsStore.createIndex('published_at', 'published_at', { unique: false });
204
+ addIndexName(artifactsStore, 'published_at');
205
+ artifactsStore.createIndex('artifact_type', 'artifact_type', { unique: false });
206
+ addIndexName(artifactsStore, 'artifact_type');
207
+ artifactsStore.createIndex('produced_by', 'produced_by', { unique: false });
208
+ addIndexName(artifactsStore, 'produced_by');
209
+ storeNames.push('artifacts');
210
+ }
211
+
212
+ // Create runs store with indexes
213
+ if (!db.objectStoreNames.contains('runs')) {
214
+ const runsStore = db.createObjectStore('runs', { keyPath: 'run_id' });
215
+ runsStore.createIndex('agent_name', 'agent_name', { unique: false });
216
+ addIndexName(runsStore, 'agent_name');
217
+ runsStore.createIndex('correlation_id', 'correlation_id', { unique: false });
218
+ addIndexName(runsStore, 'correlation_id');
219
+ runsStore.createIndex('started_at', 'started_at', { unique: false });
220
+ addIndexName(runsStore, 'started_at');
221
+ storeNames.push('runs');
222
+ }
223
+
224
+ // Create layout stores (no indexes needed)
225
+ if (!db.objectStoreNames.contains('layout_agent_view')) {
226
+ db.createObjectStore('layout_agent_view', { keyPath: 'node_id' });
227
+ storeNames.push('layout_agent_view');
228
+ }
229
+
230
+ if (!db.objectStoreNames.contains('layout_blackboard_view')) {
231
+ db.createObjectStore('layout_blackboard_view', { keyPath: 'node_id' });
232
+ storeNames.push('layout_blackboard_view');
233
+ }
234
+
235
+ // Create sessions store with index
236
+ if (!db.objectStoreNames.contains('sessions')) {
237
+ const sessionsStore = db.createObjectStore('sessions', { keyPath: 'session_id' });
238
+ sessionsStore.createIndex('created_at', 'created_at', { unique: false });
239
+ addIndexName(sessionsStore, 'created_at');
240
+ storeNames.push('sessions');
241
+ }
242
+
243
+ // Create module_instances store
244
+ if (!db.objectStoreNames.contains('module_instances')) {
245
+ db.createObjectStore('module_instances', { keyPath: 'instance_id' });
246
+ storeNames.push('module_instances');
247
+ }
248
+
249
+ // Create filters store
250
+ if (!db.objectStoreNames.contains('filters')) {
251
+ db.createObjectStore('filters', { keyPath: 'filter_id' });
252
+ storeNames.push('filters');
253
+ }
254
+
255
+ // No mock workaround needed - fake-indexeddb properly implements objectStoreNames
256
+ };
257
+
258
+ // For test mocks using setTimeout with fake timers, we need to advance time
259
+ // Check if we're in a test environment with fake timers (vi global exists)
260
+ if (typeof (globalThis as any).vi !== 'undefined') {
261
+ try {
262
+ // Synchronously run all pending timers to allow mock IndexedDB to execute
263
+ (globalThis as any).vi.runAllTimers();
264
+ } catch (e) {
265
+ // Not using fake timers, ignore
266
+ }
267
+ }
268
+ });
269
+
270
+ this.available = true;
271
+ console.log('[IndexedDB] Database initialized:', DB_NAME, 'version', DB_VERSION);
272
+ } catch (error) {
273
+ console.error('[IndexedDB] Initialization failed, using in-memory fallback:', error);
274
+ this.available = false;
275
+ this.initializeInMemoryStores();
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Initialize in-memory stores as fallback
281
+ */
282
+ private initializeInMemoryStores(): void {
283
+ const storeNames = [
284
+ 'agents',
285
+ 'artifacts',
286
+ 'runs',
287
+ 'layout_agent_view',
288
+ 'layout_blackboard_view',
289
+ 'module_instances',
290
+ 'sessions',
291
+ 'filters',
292
+ ];
293
+ storeNames.forEach((name) => {
294
+ this.inMemoryStore.set(name, new Map());
295
+ });
296
+ }
297
+
298
+ /**
299
+ * Check if IndexedDB is available
300
+ */
301
+ isAvailable(): boolean {
302
+ return this.available;
303
+ }
304
+
305
+ // ============================================================================
306
+ // CRUD Operations - Agents Store
307
+ // ============================================================================
308
+
309
+ async saveAgent(agent: AgentRecord): Promise<void> {
310
+ if (!this.db) {
311
+ this.inMemoryStore.get('agents')?.set(agent.agent_id, agent);
312
+ return;
313
+ }
314
+
315
+ try {
316
+ const tx = this.db.transaction('agents', 'readwrite');
317
+ const store = tx.objectStore('agents');
318
+ await promisifyRequest(store.put(agent));
319
+ await this.checkAndEvictByRecordLimit('agents', 'last_active');
320
+ } catch (error) {
321
+ console.error('[IndexedDB] Failed to save agent:', error);
322
+ }
323
+ }
324
+
325
+ async getAgent(agentId: string): Promise<AgentRecord | undefined> {
326
+ if (!this.db) {
327
+ return this.inMemoryStore.get('agents')?.get(agentId);
328
+ }
329
+
330
+ try {
331
+ const tx = this.db.transaction('agents', 'readonly');
332
+ const store = tx.objectStore('agents');
333
+ return await promisifyRequest(store.get(agentId));
334
+ } catch (error) {
335
+ console.error('[IndexedDB] Failed to get agent:', error);
336
+ return undefined;
337
+ }
338
+ }
339
+
340
+ async deleteAgent(agentId: string): Promise<void> {
341
+ if (!this.db) {
342
+ this.inMemoryStore.get('agents')?.delete(agentId);
343
+ return;
344
+ }
345
+
346
+ try {
347
+ const tx = this.db.transaction('agents', 'readwrite');
348
+ const store = tx.objectStore('agents');
349
+ await promisifyRequest(store.delete(agentId));
350
+ } catch (error) {
351
+ console.error('[IndexedDB] Failed to delete agent:', error);
352
+ }
353
+ }
354
+
355
+ // ============================================================================
356
+ // CRUD Operations - Artifacts Store
357
+ // ============================================================================
358
+
359
+ async saveArtifact(artifact: ArtifactRecord): Promise<void> {
360
+ if (!this.db) {
361
+ this.inMemoryStore.get('artifacts')?.set(artifact.artifact_id, artifact);
362
+ return;
363
+ }
364
+
365
+ try {
366
+ const tx = this.db.transaction('artifacts', 'readwrite');
367
+ const store = tx.objectStore('artifacts');
368
+ await promisifyRequest(store.put(artifact));
369
+ await this.checkAndEvictByRecordLimit('artifacts', 'published_at');
370
+ } catch (error) {
371
+ console.error('[IndexedDB] Failed to save artifact:', error);
372
+ }
373
+ }
374
+
375
+ async getArtifact(artifactId: string): Promise<ArtifactRecord | undefined> {
376
+ if (!this.db) {
377
+ return this.inMemoryStore.get('artifacts')?.get(artifactId);
378
+ }
379
+
380
+ try {
381
+ const tx = this.db.transaction('artifacts', 'readonly');
382
+ const store = tx.objectStore('artifacts');
383
+ return await promisifyRequest(store.get(artifactId));
384
+ } catch (error) {
385
+ console.error('[IndexedDB] Failed to get artifact:', error);
386
+ return undefined;
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Query artifacts by correlation_id using index (O(log n))
392
+ */
393
+ async getArtifactsByCorrelationId(correlationId: string): Promise<ArtifactRecord[]> {
394
+ if (!this.db) {
395
+ const artifacts = this.inMemoryStore.get('artifacts');
396
+ if (!artifacts) return [];
397
+ return Array.from(artifacts.values()).filter((a) => a.correlation_id === correlationId);
398
+ }
399
+
400
+ try {
401
+ const tx = this.db.transaction('artifacts', 'readonly');
402
+ const store = tx.objectStore('artifacts');
403
+ const index = store.index('correlation_id');
404
+ return await promisifyRequest(index.getAll(correlationId));
405
+ } catch (error) {
406
+ console.error('[IndexedDB] Failed to query artifacts by correlation_id:', error);
407
+ return [];
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Query artifacts by time range using published_at index (O(log n))
413
+ */
414
+ async getArtifactsByTimeRange(startTime: string, endTime: string): Promise<ArtifactRecord[]> {
415
+ if (!this.db) {
416
+ const artifacts = this.inMemoryStore.get('artifacts');
417
+ if (!artifacts) return [];
418
+ return Array.from(artifacts.values()).filter(
419
+ (a) => a.published_at >= startTime && a.published_at <= endTime
420
+ );
421
+ }
422
+
423
+ try {
424
+ const range = IDBKeyRange.bound(startTime, endTime);
425
+ const tx = this.db.transaction('artifacts', 'readonly');
426
+ const store = tx.objectStore('artifacts');
427
+ const index = store.index('published_at');
428
+ return await promisifyRequest(index.getAll(range));
429
+ } catch (error) {
430
+ console.error('[IndexedDB] Failed to query artifacts by time range:', error);
431
+ return [];
432
+ }
433
+ }
434
+
435
+ // ============================================================================
436
+ // CRUD Operations - Filters Store
437
+ // ============================================================================
438
+
439
+ async saveFilterPreset(record: FilterRecord): Promise<void> {
440
+ if (!this.db) {
441
+ this.inMemoryStore.get('filters')?.set(record.filter_id, record);
442
+ return;
443
+ }
444
+
445
+ try {
446
+ const tx = this.db.transaction('filters', 'readwrite');
447
+ const store = tx.objectStore('filters');
448
+ await promisifyRequest(store.put(record));
449
+ } catch (error) {
450
+ console.error('[IndexedDB] Failed to save filter preset:', error);
451
+ }
452
+ }
453
+
454
+ async getAllFilterPresets(): Promise<FilterRecord[]> {
455
+ if (!this.db) {
456
+ const filters = this.inMemoryStore.get('filters');
457
+ if (!filters) return [];
458
+ return Array.from(filters.values()).sort((a, b) => b.created_at - a.created_at);
459
+ }
460
+
461
+ try {
462
+ const tx = this.db.transaction('filters', 'readonly');
463
+ const store = tx.objectStore('filters');
464
+ const records: FilterRecord[] = await promisifyRequest(store.getAll());
465
+ return records.sort((a, b) => b.created_at - a.created_at);
466
+ } catch (error) {
467
+ console.error('[IndexedDB] Failed to load filter presets:', error);
468
+ return [];
469
+ }
470
+ }
471
+
472
+ async deleteFilterPreset(filterId: string): Promise<void> {
473
+ if (!this.db) {
474
+ this.inMemoryStore.get('filters')?.delete(filterId);
475
+ return;
476
+ }
477
+
478
+ try {
479
+ const tx = this.db.transaction('filters', 'readwrite');
480
+ const store = tx.objectStore('filters');
481
+ await promisifyRequest(store.delete(filterId));
482
+ } catch (error) {
483
+ console.error('[IndexedDB] Failed to delete filter preset:', error);
484
+ }
485
+ }
486
+
487
+ // ============================================================================
488
+ // CRUD Operations - Runs Store
489
+ // ============================================================================
490
+
491
+ async saveRun(run: RunRecord): Promise<void> {
492
+ if (!this.db) {
493
+ this.inMemoryStore.get('runs')?.set(run.run_id, run);
494
+ return;
495
+ }
496
+
497
+ try {
498
+ const tx = this.db.transaction('runs', 'readwrite');
499
+ const store = tx.objectStore('runs');
500
+ await promisifyRequest(store.put(run));
501
+ await this.checkAndEvictByRecordLimit('runs', 'started_at');
502
+ } catch (error) {
503
+ console.error('[IndexedDB] Failed to save run:', error);
504
+ }
505
+ }
506
+
507
+ async getRun(runId: string): Promise<RunRecord | undefined> {
508
+ if (!this.db) {
509
+ return this.inMemoryStore.get('runs')?.get(runId);
510
+ }
511
+
512
+ try {
513
+ const tx = this.db.transaction('runs', 'readonly');
514
+ const store = tx.objectStore('runs');
515
+ return await promisifyRequest(store.get(runId));
516
+ } catch (error) {
517
+ console.error('[IndexedDB] Failed to get run:', error);
518
+ return undefined;
519
+ }
520
+ }
521
+
522
+ // ============================================================================
523
+ // Layout Persistence - Agent View
524
+ // ============================================================================
525
+
526
+ async saveAgentViewLayout(layout: LayoutRecord): Promise<void> {
527
+ if (!this.db) {
528
+ this.inMemoryStore.get('layout_agent_view')?.set(layout.node_id, layout);
529
+ return;
530
+ }
531
+
532
+ try {
533
+ const tx = this.db.transaction('layout_agent_view', 'readwrite');
534
+ const store = tx.objectStore('layout_agent_view');
535
+ await promisifyRequest(store.put(layout));
536
+ } catch (error) {
537
+ console.error('[IndexedDB] Failed to save agent view layout:', error);
538
+ }
539
+ }
540
+
541
+ async getAgentViewLayout(nodeId: string): Promise<LayoutRecord | undefined> {
542
+ if (!this.db) {
543
+ return this.inMemoryStore.get('layout_agent_view')?.get(nodeId);
544
+ }
545
+
546
+ try {
547
+ const tx = this.db.transaction('layout_agent_view', 'readonly');
548
+ const store = tx.objectStore('layout_agent_view');
549
+ return await promisifyRequest(store.get(nodeId));
550
+ } catch (error) {
551
+ console.error('[IndexedDB] Failed to get agent view layout:', error);
552
+ return undefined;
553
+ }
554
+ }
555
+
556
+ async getAllAgentViewLayouts(): Promise<LayoutRecord[]> {
557
+ if (!this.db) {
558
+ const layouts = this.inMemoryStore.get('layout_agent_view');
559
+ return layouts ? Array.from(layouts.values()) : [];
560
+ }
561
+
562
+ try {
563
+ const tx = this.db.transaction('layout_agent_view', 'readonly');
564
+ const store = tx.objectStore('layout_agent_view');
565
+ return await promisifyRequest(store.getAll());
566
+ } catch (error) {
567
+ console.error('[IndexedDB] Failed to get all agent view layouts:', error);
568
+ return [];
569
+ }
570
+ }
571
+
572
+ // ============================================================================
573
+ // Layout Persistence - Blackboard View
574
+ // ============================================================================
575
+
576
+ async saveBlackboardViewLayout(layout: LayoutRecord): Promise<void> {
577
+ if (!this.db) {
578
+ this.inMemoryStore.get('layout_blackboard_view')?.set(layout.node_id, layout);
579
+ return;
580
+ }
581
+
582
+ try {
583
+ const tx = this.db.transaction('layout_blackboard_view', 'readwrite');
584
+ const store = tx.objectStore('layout_blackboard_view');
585
+ await promisifyRequest(store.put(layout));
586
+ } catch (error) {
587
+ console.error('[IndexedDB] Failed to save blackboard view layout:', error);
588
+ }
589
+ }
590
+
591
+ async getBlackboardViewLayout(nodeId: string): Promise<LayoutRecord | undefined> {
592
+ if (!this.db) {
593
+ return this.inMemoryStore.get('layout_blackboard_view')?.get(nodeId);
594
+ }
595
+
596
+ try {
597
+ const tx = this.db.transaction('layout_blackboard_view', 'readonly');
598
+ const store = tx.objectStore('layout_blackboard_view');
599
+ return await promisifyRequest(store.get(nodeId));
600
+ } catch (error) {
601
+ console.error('[IndexedDB] Failed to get blackboard view layout:', error);
602
+ return undefined;
603
+ }
604
+ }
605
+
606
+ async getAllBlackboardViewLayouts(): Promise<LayoutRecord[]> {
607
+ if (!this.db) {
608
+ const layouts = this.inMemoryStore.get('layout_blackboard_view');
609
+ return layouts ? Array.from(layouts.values()) : [];
610
+ }
611
+
612
+ try {
613
+ const tx = this.db.transaction('layout_blackboard_view', 'readonly');
614
+ const store = tx.objectStore('layout_blackboard_view');
615
+ return await promisifyRequest(store.getAll());
616
+ } catch (error) {
617
+ console.error('[IndexedDB] Failed to get all blackboard view layouts:', error);
618
+ return [];
619
+ }
620
+ }
621
+
622
+ // ============================================================================
623
+ // Module Instance Persistence
624
+ // ============================================================================
625
+
626
+ async saveModuleInstance(instance: ModuleInstanceRecord): Promise<void> {
627
+ if (!this.db) {
628
+ this.inMemoryStore.get('module_instances')?.set(instance.instance_id, instance);
629
+ return;
630
+ }
631
+
632
+ try {
633
+ const tx = this.db.transaction('module_instances', 'readwrite');
634
+ const store = tx.objectStore('module_instances');
635
+ await promisifyRequest(store.put(instance));
636
+ } catch (error) {
637
+ console.error('[IndexedDB] Failed to save module instance:', error);
638
+ }
639
+ }
640
+
641
+ async getModuleInstance(instanceId: string): Promise<ModuleInstanceRecord | undefined> {
642
+ if (!this.db) {
643
+ return this.inMemoryStore.get('module_instances')?.get(instanceId);
644
+ }
645
+
646
+ try {
647
+ const tx = this.db.transaction('module_instances', 'readonly');
648
+ const store = tx.objectStore('module_instances');
649
+ return await promisifyRequest(store.get(instanceId));
650
+ } catch (error) {
651
+ console.error('[IndexedDB] Failed to get module instance:', error);
652
+ return undefined;
653
+ }
654
+ }
655
+
656
+ async getAllModuleInstances(): Promise<ModuleInstanceRecord[]> {
657
+ if (!this.db) {
658
+ const instances = this.inMemoryStore.get('module_instances');
659
+ return instances ? Array.from(instances.values()) : [];
660
+ }
661
+
662
+ try {
663
+ const tx = this.db.transaction('module_instances', 'readonly');
664
+ const store = tx.objectStore('module_instances');
665
+ return await promisifyRequest(store.getAll());
666
+ } catch (error) {
667
+ console.error('[IndexedDB] Failed to get all module instances:', error);
668
+ return [];
669
+ }
670
+ }
671
+
672
+ async deleteModuleInstance(instanceId: string): Promise<void> {
673
+ if (!this.db) {
674
+ this.inMemoryStore.get('module_instances')?.delete(instanceId);
675
+ return;
676
+ }
677
+
678
+ try {
679
+ const tx = this.db.transaction('module_instances', 'readwrite');
680
+ const store = tx.objectStore('module_instances');
681
+ await promisifyRequest(store.delete(instanceId));
682
+ } catch (error) {
683
+ console.error('[IndexedDB] Failed to delete module instance:', error);
684
+ }
685
+ }
686
+
687
+ // ============================================================================
688
+ // Session Management
689
+ // ============================================================================
690
+
691
+ async saveSession(session: SessionRecord): Promise<void> {
692
+ if (!this.db) {
693
+ this.inMemoryStore.get('sessions')?.set(session.session_id, session);
694
+ return;
695
+ }
696
+
697
+ try {
698
+ const tx = this.db.transaction('sessions', 'readwrite');
699
+ const store = tx.objectStore('sessions');
700
+ await promisifyRequest(store.put(session));
701
+ } catch (error) {
702
+ console.error('[IndexedDB] Failed to save session:', error);
703
+ }
704
+ }
705
+
706
+ async getAllSessions(): Promise<SessionRecord[]> {
707
+ if (!this.db) {
708
+ const sessions = this.inMemoryStore.get('sessions');
709
+ return sessions ? Array.from(sessions.values()) : [];
710
+ }
711
+
712
+ try {
713
+ const tx = this.db.transaction('sessions', 'readonly');
714
+ const store = tx.objectStore('sessions');
715
+ return await promisifyRequest(store.getAll());
716
+ } catch (error) {
717
+ console.error('[IndexedDB] Failed to get all sessions:', error);
718
+ return [];
719
+ }
720
+ }
721
+
722
+ // ============================================================================
723
+ // LRU Eviction Strategy
724
+ // ============================================================================
725
+
726
+ /**
727
+ * Check if eviction should be triggered (80% quota threshold)
728
+ */
729
+ async checkShouldEvict(): Promise<boolean> {
730
+ if (!this.db) {
731
+ return false;
732
+ }
733
+
734
+ try {
735
+ if (!navigator.storage?.estimate) {
736
+ return false;
737
+ }
738
+
739
+ const estimate = await navigator.storage.estimate();
740
+ const usage = estimate.usage || 0;
741
+ const quota = estimate.quota || 0;
742
+
743
+ if (quota === 0) {
744
+ return false;
745
+ }
746
+
747
+ const percentage = usage / quota;
748
+
749
+ if (percentage > EVICTION_THRESHOLD) {
750
+ console.warn(
751
+ `[IndexedDB] Storage quota exceeded threshold: ${(percentage * 100).toFixed(1)}% (${usage}/${quota} bytes)`
752
+ );
753
+ return true;
754
+ }
755
+
756
+ return false;
757
+ } catch (error) {
758
+ console.error('[IndexedDB] Failed to check storage quota:', error);
759
+ return false;
760
+ }
761
+ }
762
+
763
+ /**
764
+ * Evict oldest sessions until usage reaches 60% target
765
+ */
766
+ async evictOldSessions(): Promise<void> {
767
+ if (!this.db) {
768
+ return;
769
+ }
770
+
771
+ try {
772
+ // Get all sessions sorted by created_at (oldest first)
773
+ const tx = this.db.transaction('sessions', 'readwrite');
774
+ const store = tx.objectStore('sessions');
775
+ const index = store.index('created_at');
776
+ const sessions = await promisifyRequest(index.getAll());
777
+
778
+ if (sessions.length === 0) {
779
+ return;
780
+ }
781
+
782
+ // Evict sessions one by one until target reached
783
+ for (const session of sessions) {
784
+ const estimate = await navigator.storage.estimate();
785
+ const usage = estimate.usage || 0;
786
+ const quota = estimate.quota || 0;
787
+
788
+ if (quota === 0) break;
789
+
790
+ const percentage = usage / quota;
791
+
792
+ // Stop if we've reached the target
793
+ if (percentage <= EVICTION_TARGET) {
794
+ break;
795
+ }
796
+
797
+ // Delete session
798
+ const deleteTx = this.db.transaction('sessions', 'readwrite');
799
+ const deleteStore = deleteTx.objectStore('sessions');
800
+ await promisifyRequest(deleteStore.delete(session.session_id));
801
+ console.log(`[IndexedDB] Evicted session: ${session.session_id}`);
802
+ }
803
+ } catch (error) {
804
+ console.error('[IndexedDB] Failed to evict old sessions:', error);
805
+ }
806
+ }
807
+
808
+ /**
809
+ * Check and evict by record limit (MAX_RECORDS_PER_STORE)
810
+ * Evicts oldest records based on specified index
811
+ */
812
+ private async checkAndEvictByRecordLimit(
813
+ storeName: string,
814
+ sortIndex: string
815
+ ): Promise<void> {
816
+ if (!this.db) {
817
+ return;
818
+ }
819
+
820
+ try {
821
+ const tx = this.db.transaction(storeName, 'readonly');
822
+ const store = tx.objectStore(storeName);
823
+ const count = await promisifyRequest(store.count());
824
+
825
+ if (count > MAX_RECORDS_PER_STORE) {
826
+ // Get oldest records using the sort index
827
+ const readTx = this.db.transaction(storeName, 'readonly');
828
+ const readStore = readTx.objectStore(storeName);
829
+ const index = readStore.index(sortIndex);
830
+ const keys = await promisifyRequest(index.getAllKeys());
831
+
832
+ // Delete oldest records until we're under the limit
833
+ const numToDelete = count - MAX_RECORDS_PER_STORE;
834
+ const writeTx = this.db.transaction(storeName, 'readwrite');
835
+ const writeStore = writeTx.objectStore(storeName);
836
+
837
+ for (let i = 0; i < numToDelete && i < keys.length; i++) {
838
+ await promisifyRequest(writeStore.delete(keys[i]!));
839
+ }
840
+ }
841
+ } catch (error) {
842
+ console.error(`[IndexedDB] Failed to evict by record limit for ${storeName}:`, error);
843
+ }
844
+ }
845
+ }
846
+
847
+ // Singleton instance
848
+ export const indexedDBService = new IndexedDBService();