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.
- flock/__init__.py +12 -217
- flock/agent.py +1079 -0
- flock/api/themes.py +71 -0
- flock/artifacts.py +86 -0
- flock/cli.py +147 -0
- flock/components.py +189 -0
- flock/dashboard/__init__.py +30 -0
- flock/dashboard/collector.py +559 -0
- flock/dashboard/events.py +188 -0
- flock/dashboard/graph_builder.py +563 -0
- flock/dashboard/launcher.py +235 -0
- flock/dashboard/models/graph.py +156 -0
- flock/dashboard/service.py +991 -0
- flock/dashboard/static_v2/assets/index-DFRnI_mt.js +111 -0
- flock/dashboard/static_v2/assets/index-fPLNdmp1.css +1 -0
- flock/dashboard/static_v2/index.html +13 -0
- flock/dashboard/websocket.py +246 -0
- flock/engines/__init__.py +6 -0
- flock/engines/dspy_engine.py +932 -0
- flock/examples.py +131 -0
- flock/frontend/README.md +778 -0
- flock/frontend/docs/DESIGN_SYSTEM.md +1980 -0
- flock/frontend/index.html +12 -0
- flock/frontend/package-lock.json +4337 -0
- flock/frontend/package.json +48 -0
- flock/frontend/src/App.tsx +139 -0
- flock/frontend/src/__tests__/integration/graph-snapshot.test.tsx +647 -0
- flock/frontend/src/__tests__/integration/indexeddb-persistence.test.tsx +699 -0
- flock/frontend/src/components/common/BuildInfo.tsx +39 -0
- flock/frontend/src/components/common/EmptyState.module.css +115 -0
- flock/frontend/src/components/common/EmptyState.tsx +128 -0
- flock/frontend/src/components/common/ErrorBoundary.module.css +169 -0
- flock/frontend/src/components/common/ErrorBoundary.tsx +118 -0
- flock/frontend/src/components/common/KeyboardShortcutsDialog.css +251 -0
- flock/frontend/src/components/common/KeyboardShortcutsDialog.tsx +151 -0
- flock/frontend/src/components/common/LoadingSpinner.module.css +97 -0
- flock/frontend/src/components/common/LoadingSpinner.tsx +29 -0
- flock/frontend/src/components/controls/PublishControl.css +547 -0
- flock/frontend/src/components/controls/PublishControl.test.tsx +543 -0
- flock/frontend/src/components/controls/PublishControl.tsx +432 -0
- flock/frontend/src/components/details/DetailWindowContainer.tsx +58 -0
- flock/frontend/src/components/details/LiveOutputTab.test.tsx +792 -0
- flock/frontend/src/components/details/LiveOutputTab.tsx +220 -0
- flock/frontend/src/components/details/MessageDetailWindow.tsx +439 -0
- flock/frontend/src/components/details/MessageHistoryTab.tsx +374 -0
- flock/frontend/src/components/details/NodeDetailWindow.test.tsx +501 -0
- flock/frontend/src/components/details/NodeDetailWindow.tsx +218 -0
- flock/frontend/src/components/details/RunStatusTab.tsx +348 -0
- flock/frontend/src/components/details/tabs.test.tsx +1015 -0
- flock/frontend/src/components/filters/ArtifactTypeFilter.tsx +21 -0
- flock/frontend/src/components/filters/CorrelationIDFilter.module.css +102 -0
- flock/frontend/src/components/filters/CorrelationIDFilter.test.tsx +197 -0
- flock/frontend/src/components/filters/CorrelationIDFilter.tsx +121 -0
- flock/frontend/src/components/filters/FilterFlyout.module.css +104 -0
- flock/frontend/src/components/filters/FilterFlyout.tsx +80 -0
- flock/frontend/src/components/filters/FilterPills.module.css +220 -0
- flock/frontend/src/components/filters/FilterPills.test.tsx +189 -0
- flock/frontend/src/components/filters/FilterPills.tsx +143 -0
- flock/frontend/src/components/filters/ProducerFilter.tsx +21 -0
- flock/frontend/src/components/filters/SavedFiltersControl.module.css +60 -0
- flock/frontend/src/components/filters/SavedFiltersControl.test.tsx +158 -0
- flock/frontend/src/components/filters/SavedFiltersControl.tsx +159 -0
- flock/frontend/src/components/filters/TagFilter.tsx +21 -0
- flock/frontend/src/components/filters/TimeRangeFilter.module.css +115 -0
- flock/frontend/src/components/filters/TimeRangeFilter.test.tsx +154 -0
- flock/frontend/src/components/filters/TimeRangeFilter.tsx +110 -0
- flock/frontend/src/components/filters/VisibilityFilter.tsx +21 -0
- flock/frontend/src/components/graph/AgentNode.test.tsx +77 -0
- flock/frontend/src/components/graph/AgentNode.tsx +324 -0
- flock/frontend/src/components/graph/GraphCanvas.tsx +613 -0
- flock/frontend/src/components/graph/MessageFlowEdge.tsx +128 -0
- flock/frontend/src/components/graph/MessageNode.test.tsx +64 -0
- flock/frontend/src/components/graph/MessageNode.tsx +129 -0
- flock/frontend/src/components/graph/MiniMap.tsx +47 -0
- flock/frontend/src/components/graph/TransformEdge.tsx +123 -0
- flock/frontend/src/components/layout/DashboardLayout.css +420 -0
- flock/frontend/src/components/layout/DashboardLayout.tsx +287 -0
- flock/frontend/src/components/layout/Header.module.css +88 -0
- flock/frontend/src/components/layout/Header.tsx +52 -0
- flock/frontend/src/components/modules/HistoricalArtifactsModule.module.css +288 -0
- flock/frontend/src/components/modules/HistoricalArtifactsModule.tsx +450 -0
- flock/frontend/src/components/modules/HistoricalArtifactsModuleWrapper.tsx +13 -0
- flock/frontend/src/components/modules/JsonAttributeRenderer.tsx +140 -0
- flock/frontend/src/components/modules/ModuleRegistry.test.ts +333 -0
- flock/frontend/src/components/modules/ModuleRegistry.ts +93 -0
- flock/frontend/src/components/modules/ModuleWindow.tsx +223 -0
- flock/frontend/src/components/modules/TraceModuleJaeger.tsx +1971 -0
- flock/frontend/src/components/modules/TraceModuleJaegerWrapper.tsx +13 -0
- flock/frontend/src/components/modules/registerModules.ts +29 -0
- flock/frontend/src/components/settings/AdvancedSettings.tsx +175 -0
- flock/frontend/src/components/settings/AppearanceSettings.tsx +185 -0
- flock/frontend/src/components/settings/GraphSettings.tsx +110 -0
- flock/frontend/src/components/settings/MultiSelect.tsx +235 -0
- flock/frontend/src/components/settings/SettingsPanel.css +327 -0
- flock/frontend/src/components/settings/SettingsPanel.tsx +131 -0
- flock/frontend/src/components/settings/ThemeSelector.tsx +298 -0
- flock/frontend/src/components/settings/TracingSettings.tsx +404 -0
- flock/frontend/src/hooks/useKeyboardShortcuts.ts +148 -0
- flock/frontend/src/hooks/useModulePersistence.test.ts +442 -0
- flock/frontend/src/hooks/useModulePersistence.ts +154 -0
- flock/frontend/src/hooks/useModules.ts +157 -0
- flock/frontend/src/hooks/usePersistence.ts +141 -0
- flock/frontend/src/main.tsx +13 -0
- flock/frontend/src/services/api.ts +337 -0
- flock/frontend/src/services/graphService.test.ts +330 -0
- flock/frontend/src/services/graphService.ts +75 -0
- flock/frontend/src/services/indexeddb.test.ts +793 -0
- flock/frontend/src/services/indexeddb.ts +848 -0
- flock/frontend/src/services/layout.test.ts +437 -0
- flock/frontend/src/services/layout.ts +357 -0
- flock/frontend/src/services/themeApplicator.ts +140 -0
- flock/frontend/src/services/themeService.ts +77 -0
- flock/frontend/src/services/websocket.ts +650 -0
- flock/frontend/src/store/filterStore.test.ts +250 -0
- flock/frontend/src/store/filterStore.ts +272 -0
- flock/frontend/src/store/graphStore.test.ts +570 -0
- flock/frontend/src/store/graphStore.ts +462 -0
- flock/frontend/src/store/moduleStore.test.ts +253 -0
- flock/frontend/src/store/moduleStore.ts +75 -0
- flock/frontend/src/store/settingsStore.ts +188 -0
- flock/frontend/src/store/streamStore.ts +68 -0
- flock/frontend/src/store/uiStore.test.ts +54 -0
- flock/frontend/src/store/uiStore.ts +122 -0
- flock/frontend/src/store/wsStore.ts +34 -0
- flock/frontend/src/styles/index.css +15 -0
- flock/frontend/src/styles/scrollbar.css +47 -0
- flock/frontend/src/styles/variables.css +488 -0
- flock/frontend/src/test/setup.ts +1 -0
- flock/frontend/src/types/filters.ts +47 -0
- flock/frontend/src/types/graph.ts +95 -0
- flock/frontend/src/types/modules.ts +10 -0
- flock/frontend/src/types/theme.ts +55 -0
- flock/frontend/src/utils/artifacts.ts +24 -0
- flock/frontend/src/utils/mockData.ts +98 -0
- flock/frontend/src/utils/performance.ts +16 -0
- flock/frontend/src/vite-env.d.ts +17 -0
- flock/frontend/tsconfig.json +27 -0
- flock/frontend/tsconfig.node.json +11 -0
- flock/frontend/vite.config.ts +25 -0
- flock/frontend/vitest.config.ts +11 -0
- flock/{core/util → helper}/cli_helper.py +9 -5
- flock/{core/logging → logging}/__init__.py +2 -3
- flock/logging/auto_trace.py +159 -0
- flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
- flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
- flock/{core/logging → logging}/formatters/themed_formatter.py +69 -107
- flock/{core/logging → logging}/logging.py +78 -61
- flock/{core/logging → logging}/telemetry.py +66 -26
- flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
- flock/logging/telemetry_exporter/duckdb_exporter.py +216 -0
- flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +13 -10
- flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
- flock/logging/trace_and_logged.py +304 -0
- flock/mcp/__init__.py +91 -0
- flock/{core/mcp/mcp_client.py → mcp/client.py} +131 -158
- flock/{core/mcp/mcp_config.py → mcp/config.py} +86 -132
- flock/mcp/manager.py +286 -0
- flock/mcp/servers/sse/__init__.py +1 -1
- flock/mcp/servers/sse/flock_sse_server.py +16 -58
- flock/mcp/servers/stdio/__init__.py +1 -1
- flock/mcp/servers/stdio/flock_stdio_server.py +13 -53
- flock/mcp/servers/streamable_http/flock_streamable_http_server.py +22 -67
- flock/mcp/servers/websockets/flock_websocket_server.py +12 -45
- flock/{core/mcp/flock_mcp_tool_base.py → mcp/tool.py} +24 -78
- flock/mcp/types/__init__.py +42 -0
- flock/{core/mcp → mcp}/types/callbacks.py +12 -15
- flock/{core/mcp → mcp}/types/factories.py +7 -6
- flock/{core/mcp → mcp}/types/handlers.py +13 -18
- flock/{core/mcp → mcp}/types/types.py +70 -74
- flock/{core/mcp → mcp}/util/helpers.py +3 -3
- flock/orchestrator.py +970 -0
- flock/registry.py +148 -0
- flock/runtime.py +262 -0
- flock/service.py +277 -0
- flock/store.py +1214 -0
- flock/subscription.py +111 -0
- flock/themes/andromeda.toml +1 -1
- flock/themes/apple-system-colors.toml +1 -1
- flock/themes/arcoiris.toml +1 -1
- flock/themes/atomonelight.toml +1 -1
- flock/themes/ayu copy.toml +1 -1
- flock/themes/ayu-light.toml +1 -1
- flock/themes/belafonte-day.toml +1 -1
- flock/themes/belafonte-night.toml +1 -1
- flock/themes/blulocodark.toml +1 -1
- flock/themes/breeze.toml +1 -1
- flock/themes/broadcast.toml +1 -1
- flock/themes/brogrammer.toml +1 -1
- flock/themes/builtin-dark.toml +1 -1
- flock/themes/builtin-pastel-dark.toml +1 -1
- flock/themes/catppuccin-latte.toml +1 -1
- flock/themes/catppuccin-macchiato.toml +1 -1
- flock/themes/catppuccin-mocha.toml +1 -1
- flock/themes/cga.toml +1 -1
- flock/themes/chalk.toml +1 -1
- flock/themes/ciapre.toml +1 -1
- flock/themes/coffee-theme.toml +1 -1
- flock/themes/cyberpunkscarletprotocol.toml +1 -1
- flock/themes/dark+.toml +1 -1
- flock/themes/darkermatrix.toml +1 -1
- flock/themes/darkmatrix.toml +2 -2
- flock/themes/darkside.toml +1 -1
- flock/themes/deep.toml +2 -2
- flock/themes/desert.toml +1 -1
- flock/themes/django.toml +1 -1
- flock/themes/djangosmooth.toml +1 -1
- flock/themes/doomone.toml +1 -1
- flock/themes/dotgov.toml +1 -1
- flock/themes/dracula+.toml +1 -1
- flock/themes/duckbones.toml +1 -1
- flock/themes/encom.toml +1 -1
- flock/themes/espresso.toml +1 -1
- flock/themes/everblush.toml +1 -1
- flock/themes/fairyfloss.toml +1 -1
- flock/themes/fideloper.toml +1 -1
- flock/themes/fishtank.toml +1 -1
- flock/themes/flexoki-light.toml +1 -1
- flock/themes/floraverse.toml +1 -1
- flock/themes/framer.toml +1 -1
- flock/themes/galizur.toml +1 -1
- flock/themes/github.toml +1 -1
- flock/themes/grass.toml +1 -1
- flock/themes/grey-green.toml +1 -1
- flock/themes/gruvboxlight.toml +1 -1
- flock/themes/guezwhoz.toml +1 -1
- flock/themes/harper.toml +1 -1
- flock/themes/hax0r-blue.toml +1 -1
- flock/themes/hopscotch.256.toml +1 -1
- flock/themes/ic-green-ppl.toml +1 -1
- flock/themes/iceberg-dark.toml +1 -1
- flock/themes/japanesque.toml +1 -1
- flock/themes/jubi.toml +1 -1
- flock/themes/kibble.toml +1 -1
- flock/themes/kolorit.toml +1 -1
- flock/themes/kurokula.toml +1 -1
- flock/themes/materialdesigncolors.toml +1 -1
- flock/themes/matrix.toml +1 -1
- flock/themes/mellifluous.toml +1 -1
- flock/themes/midnight-in-mojave.toml +1 -1
- flock/themes/monokai-remastered.toml +1 -1
- flock/themes/monokai-soda.toml +1 -1
- flock/themes/neon.toml +1 -1
- flock/themes/neopolitan.toml +5 -5
- flock/themes/nord-light.toml +1 -1
- flock/themes/ocean.toml +1 -1
- flock/themes/onehalfdark.toml +1 -1
- flock/themes/onehalflight.toml +1 -1
- flock/themes/palenighthc.toml +1 -1
- flock/themes/paulmillr.toml +1 -1
- flock/themes/pencildark.toml +1 -1
- flock/themes/pnevma.toml +1 -1
- flock/themes/purple-rain.toml +1 -1
- flock/themes/purplepeter.toml +1 -1
- flock/themes/raycast-dark.toml +1 -1
- flock/themes/red-sands.toml +1 -1
- flock/themes/relaxed.toml +1 -1
- flock/themes/retro.toml +1 -1
- flock/themes/rose-pine.toml +1 -1
- flock/themes/royal.toml +1 -1
- flock/themes/ryuuko.toml +1 -1
- flock/themes/sakura.toml +1 -1
- flock/themes/scarlet-protocol.toml +1 -1
- flock/themes/seoulbones-dark.toml +1 -1
- flock/themes/shades-of-purple.toml +1 -1
- flock/themes/smyck.toml +1 -1
- flock/themes/softserver.toml +1 -1
- flock/themes/solarized-darcula.toml +1 -1
- flock/themes/square.toml +1 -1
- flock/themes/sugarplum.toml +1 -1
- flock/themes/thayer-bright.toml +1 -1
- flock/themes/tokyonight.toml +1 -1
- flock/themes/tomorrow.toml +1 -1
- flock/themes/ubuntu.toml +1 -1
- flock/themes/ultradark.toml +1 -1
- flock/themes/ultraviolent.toml +1 -1
- flock/themes/unikitty.toml +1 -1
- flock/themes/urple.toml +1 -1
- flock/themes/vesper.toml +1 -1
- flock/themes/vimbones.toml +1 -1
- flock/themes/wildcherry.toml +1 -1
- flock/themes/wilmersdorf.toml +1 -1
- flock/themes/wryan.toml +1 -1
- flock/themes/xcodedarkhc.toml +1 -1
- flock/themes/xcodelight.toml +1 -1
- flock/themes/zenbones-light.toml +1 -1
- flock/themes/zenwritten-dark.toml +1 -1
- flock/utilities.py +301 -0
- flock/utility/output_utility_component.py +226 -0
- flock/visibility.py +107 -0
- flock_core-0.5.0.dist-info/METADATA +964 -0
- flock_core-0.5.0.dist-info/RECORD +525 -0
- flock_core-0.5.0.dist-info/entry_points.txt +2 -0
- {flock_core-0.4.542.dist-info → flock_core-0.5.0.dist-info}/licenses/LICENSE +1 -1
- flock/adapter/__init__.py +0 -14
- flock/adapter/azure_adapter.py +0 -68
- flock/adapter/chroma_adapter.py +0 -73
- flock/adapter/faiss_adapter.py +0 -97
- flock/adapter/pinecone_adapter.py +0 -51
- flock/adapter/vector_base.py +0 -47
- flock/cli/assets/release_notes.md +0 -140
- flock/cli/config.py +0 -8
- flock/cli/constants.py +0 -36
- flock/cli/create_agent.py +0 -1
- flock/cli/create_flock.py +0 -280
- flock/cli/execute_flock.py +0 -620
- flock/cli/load_agent.py +0 -1
- flock/cli/load_examples.py +0 -1
- flock/cli/load_flock.py +0 -192
- flock/cli/load_release_notes.py +0 -20
- flock/cli/loaded_flock_cli.py +0 -254
- flock/cli/manage_agents.py +0 -459
- flock/cli/registry_management.py +0 -889
- flock/cli/runner.py +0 -41
- flock/cli/settings.py +0 -857
- flock/cli/utils.py +0 -135
- flock/cli/view_results.py +0 -29
- flock/cli/yaml_editor.py +0 -396
- flock/config.py +0 -56
- flock/core/__init__.py +0 -44
- flock/core/api/__init__.py +0 -10
- flock/core/api/custom_endpoint.py +0 -45
- flock/core/api/endpoints.py +0 -262
- flock/core/api/main.py +0 -162
- flock/core/api/models.py +0 -101
- flock/core/api/run_store.py +0 -224
- flock/core/api/runner.py +0 -44
- flock/core/api/service.py +0 -214
- flock/core/config/flock_agent_config.py +0 -11
- flock/core/config/scheduled_agent_config.py +0 -40
- flock/core/context/context.py +0 -214
- flock/core/context/context_manager.py +0 -40
- flock/core/context/context_vars.py +0 -11
- flock/core/evaluation/utils.py +0 -395
- flock/core/execution/batch_executor.py +0 -369
- flock/core/execution/evaluation_executor.py +0 -438
- flock/core/execution/local_executor.py +0 -31
- flock/core/execution/opik_executor.py +0 -103
- flock/core/execution/temporal_executor.py +0 -166
- flock/core/flock.py +0 -1003
- flock/core/flock_agent.py +0 -1258
- flock/core/flock_evaluator.py +0 -60
- flock/core/flock_factory.py +0 -513
- flock/core/flock_module.py +0 -207
- flock/core/flock_registry.py +0 -702
- flock/core/flock_router.py +0 -83
- flock/core/flock_scheduler.py +0 -166
- flock/core/flock_server_manager.py +0 -136
- flock/core/interpreter/python_interpreter.py +0 -689
- flock/core/logging/live_capture.py +0 -137
- flock/core/logging/trace_and_logged.py +0 -59
- flock/core/mcp/__init__.py +0 -1
- flock/core/mcp/flock_mcp_server.py +0 -640
- flock/core/mcp/mcp_client_manager.py +0 -201
- flock/core/mcp/types/__init__.py +0 -1
- flock/core/mixin/dspy_integration.py +0 -445
- flock/core/mixin/prompt_parser.py +0 -125
- flock/core/serialization/__init__.py +0 -13
- flock/core/serialization/callable_registry.py +0 -52
- flock/core/serialization/flock_serializer.py +0 -854
- flock/core/serialization/json_encoder.py +0 -41
- flock/core/serialization/secure_serializer.py +0 -175
- flock/core/serialization/serializable.py +0 -342
- flock/core/serialization/serialization_utils.py +0 -409
- flock/core/util/file_path_utils.py +0 -223
- flock/core/util/hydrator.py +0 -309
- flock/core/util/input_resolver.py +0 -141
- flock/core/util/loader.py +0 -59
- flock/core/util/splitter.py +0 -219
- flock/di.py +0 -41
- flock/evaluators/__init__.py +0 -1
- flock/evaluators/declarative/__init__.py +0 -1
- flock/evaluators/declarative/declarative_evaluator.py +0 -217
- flock/evaluators/memory/memory_evaluator.py +0 -90
- flock/evaluators/test/test_case_evaluator.py +0 -38
- flock/evaluators/zep/zep_evaluator.py +0 -59
- flock/modules/__init__.py +0 -1
- flock/modules/assertion/__init__.py +0 -1
- flock/modules/assertion/assertion_module.py +0 -286
- flock/modules/callback/__init__.py +0 -1
- flock/modules/callback/callback_module.py +0 -91
- flock/modules/enterprise_memory/README.md +0 -99
- flock/modules/enterprise_memory/enterprise_memory_module.py +0 -526
- flock/modules/mem0/__init__.py +0 -1
- flock/modules/mem0/mem0_module.py +0 -126
- flock/modules/mem0_async/__init__.py +0 -1
- flock/modules/mem0_async/async_mem0_module.py +0 -126
- flock/modules/memory/__init__.py +0 -1
- flock/modules/memory/memory_module.py +0 -429
- flock/modules/memory/memory_parser.py +0 -125
- flock/modules/memory/memory_storage.py +0 -736
- flock/modules/output/__init__.py +0 -1
- flock/modules/output/output_module.py +0 -196
- flock/modules/performance/__init__.py +0 -1
- flock/modules/performance/metrics_module.py +0 -678
- flock/modules/zep/__init__.py +0 -1
- flock/modules/zep/zep_module.py +0 -192
- flock/platform/docker_tools.py +0 -49
- flock/platform/jaeger_install.py +0 -86
- flock/routers/__init__.py +0 -1
- flock/routers/agent/__init__.py +0 -1
- flock/routers/agent/agent_router.py +0 -236
- flock/routers/agent/handoff_agent.py +0 -58
- flock/routers/conditional/conditional_router.py +0 -486
- flock/routers/default/__init__.py +0 -1
- flock/routers/default/default_router.py +0 -80
- flock/routers/feedback/feedback_router.py +0 -114
- flock/routers/list_generator/list_generator_router.py +0 -166
- flock/routers/llm/__init__.py +0 -1
- flock/routers/llm/llm_router.py +0 -365
- flock/tools/__init__.py +0 -0
- flock/tools/azure_tools.py +0 -781
- flock/tools/code_tools.py +0 -167
- flock/tools/file_tools.py +0 -149
- flock/tools/github_tools.py +0 -157
- flock/tools/markdown_tools.py +0 -205
- flock/tools/system_tools.py +0 -9
- flock/tools/text_tools.py +0 -810
- flock/tools/web_tools.py +0 -92
- flock/tools/zendesk_tools.py +0 -501
- flock/webapp/__init__.py +0 -1
- flock/webapp/app/__init__.py +0 -0
- flock/webapp/app/api/__init__.py +0 -0
- flock/webapp/app/api/agent_management.py +0 -237
- flock/webapp/app/api/execution.py +0 -503
- flock/webapp/app/api/flock_management.py +0 -125
- flock/webapp/app/api/registry_viewer.py +0 -29
- flock/webapp/app/chat.py +0 -662
- flock/webapp/app/config.py +0 -104
- flock/webapp/app/dependencies.py +0 -117
- flock/webapp/app/main.py +0 -1086
- flock/webapp/app/middleware.py +0 -113
- flock/webapp/app/models_ui.py +0 -7
- flock/webapp/app/services/__init__.py +0 -0
- flock/webapp/app/services/feedback_file_service.py +0 -363
- flock/webapp/app/services/flock_service.py +0 -345
- flock/webapp/app/services/sharing_models.py +0 -81
- flock/webapp/app/services/sharing_store.py +0 -597
- flock/webapp/app/templates/theme_mapper.html +0 -326
- flock/webapp/app/theme_mapper.py +0 -811
- flock/webapp/app/utils.py +0 -85
- flock/webapp/run.py +0 -219
- flock/webapp/static/css/chat.css +0 -301
- flock/webapp/static/css/components.css +0 -167
- flock/webapp/static/css/header.css +0 -39
- flock/webapp/static/css/layout.css +0 -281
- flock/webapp/static/css/sidebar.css +0 -127
- flock/webapp/static/css/two-pane.css +0 -48
- flock/webapp/templates/base.html +0 -389
- flock/webapp/templates/chat.html +0 -152
- flock/webapp/templates/chat_settings.html +0 -19
- flock/webapp/templates/flock_editor.html +0 -16
- flock/webapp/templates/index.html +0 -12
- flock/webapp/templates/partials/_agent_detail_form.html +0 -93
- flock/webapp/templates/partials/_agent_list.html +0 -18
- flock/webapp/templates/partials/_agent_manager_view.html +0 -51
- flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
- flock/webapp/templates/partials/_chat_container.html +0 -15
- flock/webapp/templates/partials/_chat_messages.html +0 -57
- flock/webapp/templates/partials/_chat_settings_form.html +0 -85
- flock/webapp/templates/partials/_create_flock_form.html +0 -50
- flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
- flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
- flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
- flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
- flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
- flock/webapp/templates/partials/_env_vars_table.html +0 -23
- flock/webapp/templates/partials/_execution_form.html +0 -127
- flock/webapp/templates/partials/_execution_view_container.html +0 -28
- flock/webapp/templates/partials/_flock_file_list.html +0 -23
- flock/webapp/templates/partials/_flock_properties_form.html +0 -52
- flock/webapp/templates/partials/_flock_upload_form.html +0 -16
- flock/webapp/templates/partials/_header_flock_status.html +0 -5
- flock/webapp/templates/partials/_live_logs.html +0 -13
- flock/webapp/templates/partials/_load_manager_view.html +0 -49
- flock/webapp/templates/partials/_registry_table.html +0 -25
- flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
- flock/webapp/templates/partials/_results_display.html +0 -78
- flock/webapp/templates/partials/_settings_env_content.html +0 -9
- flock/webapp/templates/partials/_settings_theme_content.html +0 -14
- flock/webapp/templates/partials/_settings_view.html +0 -36
- flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
- flock/webapp/templates/partials/_share_link_snippet.html +0 -35
- flock/webapp/templates/partials/_sidebar.html +0 -74
- flock/webapp/templates/partials/_structured_data_view.html +0 -40
- flock/webapp/templates/partials/_theme_preview.html +0 -36
- flock/webapp/templates/registry_viewer.html +0 -84
- flock/webapp/templates/shared_run_page.html +0 -140
- flock/workflow/__init__.py +0 -0
- flock/workflow/activities.py +0 -237
- flock/workflow/agent_activities.py +0 -24
- flock/workflow/agent_execution_activity.py +0 -240
- flock/workflow/flock_workflow.py +0 -225
- flock/workflow/temporal_config.py +0 -96
- flock/workflow/temporal_setup.py +0 -60
- flock_core-0.4.542.dist-info/METADATA +0 -676
- flock_core-0.4.542.dist-info/RECORD +0 -572
- flock_core-0.4.542.dist-info/entry_points.txt +0 -2
- /flock/{core/logging → logging}/formatters/themes.py +0 -0
- /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
- /flock/{core/mcp → mcp}/util/__init__.py +0 -0
- {flock_core-0.4.542.dist-info → flock_core-0.5.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}._container_18lbh_1{position:relative;width:300px}._inputWrapper_18lbh_11{position:relative}._input_18lbh_11{width:100%;padding:var(--spacing-2) var(--spacing-8) var(--spacing-2) var(--spacing-3);background-color:var(--color-bg-surface);border:1px solid var(--color-border-default);border-radius:var(--radius-md);font-size:var(--font-size-body-sm);font-family:var(--font-family-sans);color:var(--color-text-primary);outline:none;box-sizing:border-box;transition:var(--transition-colors),box-shadow var(--duration-normal) var(--ease-smooth)}._input_18lbh_11::placeholder{color:var(--color-text-muted);font-style:italic}._input_18lbh_11:focus{border-color:var(--color-border-focus);box-shadow:var(--shadow-glow-primary)}._clearButton_18lbh_67{position:absolute;right:var(--spacing-2);top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;font-size:18px;color:var(--color-text-muted);padding:0 var(--spacing-1);line-height:1;transition:var(--transition-colors)}._clearButton_18lbh_67:hover{color:var(--color-text-secondary)}._dropdown_18lbh_105{position:absolute;top:100%;left:0;right:0;margin-top:var(--spacing-1);background-color:var(--color-bg-surface);border:1px solid var(--color-border-default);border-radius:var(--radius-md);box-shadow:var(--shadow-lg);max-height:300px;overflow-y:auto;z-index:1000}._dropdownEmpty_18lbh_135{padding:var(--spacing-3);color:var(--color-text-muted);font-size:var(--font-size-body-sm);text-align:center}._dropdownItem_18lbh_149{padding:var(--spacing-2-5) var(--spacing-3);cursor:pointer;border-bottom:1px solid var(--color-border-subtle);transition:var(--transition-colors)}._dropdownItem_18lbh_149:last-child{border-bottom:none}._dropdownItem_18lbh_149:hover{background-color:var(--color-bg-elevated)}._correlationId_18lbh_179{font-family:var(--font-family-mono);font-size:var(--font-size-body-xs);font-weight:var(--font-weight-medium);color:var(--color-text-primary);margin-bottom:var(--spacing-1);word-break:break-all}._metadata_18lbh_197{font-size:var(--font-size-caption);color:var(--color-text-tertiary)}._container_kluuc_1{display:flex;flex-direction:column;gap:var(--spacing-2)}._presetButtons_kluuc_13{display:flex;flex-wrap:wrap;gap:var(--spacing-2);align-items:center}._presetButton_kluuc_13{padding:var(--spacing-2) var(--spacing-4);background-color:var(--color-bg-surface);color:var(--color-text-secondary);border:1px solid var(--color-border-default);border-radius:var(--radius-md);cursor:pointer;font-size:var(--font-size-body-sm);font-weight:var(--font-weight-medium);font-family:var(--font-family-sans);transition:var(--transition-colors);white-space:nowrap}._presetButton_kluuc_13:hover{background-color:var(--color-bg-overlay);border-color:var(--color-border-strong)}._presetButton_kluuc_13._active_kluuc_65{background-color:var(--color-primary-600);color:var(--color-text-on-primary);border-color:var(--color-primary-600)}._presetButton_kluuc_13._active_kluuc_65:hover{background-color:var(--color-primary-700);border-color:var(--color-primary-700)}._presetButtonAll_kluuc_87{background:#c241f426;color:var(--color-secondary-200);border-color:var(--color-secondary-500)}._presetButtonAll_kluuc_87:hover{background:#c241f440;border-color:var(--color-secondary-400);color:var(--color-secondary-100)}._presetButtonAll_kluuc_87._active_kluuc_65{background:var(--color-secondary-500);border-color:var(--color-secondary-300);color:var(--color-text-on-primary)}._presetButtonAll_kluuc_87._active_kluuc_65:hover{background:var(--color-secondary-400);border-color:var(--color-secondary-200)}._customRange_kluuc_133{display:flex;gap:var(--spacing-3);align-items:center;padding:var(--spacing-2);background-color:var(--color-bg-surface);border-radius:var(--radius-md);border:1px solid var(--color-border-subtle)}._dateInputGroup_kluuc_153{display:flex;flex-direction:column;gap:var(--spacing-1)}._dateLabel_kluuc_165{font-size:var(--font-size-caption);color:var(--color-text-tertiary);font-weight:var(--font-weight-medium)}._dateInput_kluuc_153{padding:var(--spacing-1-5) var(--spacing-2);background-color:var(--color-bg-elevated);border:1px solid var(--color-border-default);border-radius:var(--radius-md);font-size:var(--font-size-body-sm);font-family:var(--font-family-sans);color:var(--color-text-primary);outline:none;transition:var(--transition-colors),box-shadow var(--duration-normal) var(--ease-smooth)}._dateInput_kluuc_153:focus{border-color:var(--color-border-focus);box-shadow:var(--shadow-glow-primary)}._dateInput_kluuc_153::-webkit-calendar-picker-indicator{cursor:pointer;filter:invert(.7);transition:var(--transition-opacity)}._dateInput_kluuc_153::-webkit-calendar-picker-indicator:hover{filter:invert(1)}._container_9ted7_1{display:flex;flex-direction:column;gap:var(--gap-xs)}._controlsRow_9ted7_13{display:flex;align-items:center;gap:var(--gap-sm);flex-wrap:wrap}._select_9ted7_27{min-width:180px;padding:var(--spacing-1) var(--spacing-2);border-radius:var(--radius-sm);border:1px solid var(--color-border-subtle);background:#00000059;color:var(--color-text-primary)}._saveButton_9ted7_45,._applyButton_9ted7_47,._deleteButton_9ted7_49{padding:var(--spacing-1) var(--spacing-3);border-radius:var(--radius-sm);border:none;font-size:var(--font-size-body-xs);cursor:pointer;transition:background-color var(--duration-fast) ease}._saveButton_9ted7_45{background:var(--color-info);color:var(--color-text-on-primary)}._applyButton_9ted7_47{background:var(--color-success);color:var(--color-text-on-primary)}._deleteButton_9ted7_49{background:var(--color-error);color:var(--color-text-on-primary)}._saveButton_9ted7_45:disabled,._applyButton_9ted7_47:disabled,._deleteButton_9ted7_49:disabled,._select_9ted7_27:disabled{opacity:.5;cursor:not-allowed}._error_9ted7_113{font-size:var(--font-size-body-xs);color:var(--color-error)}._panel_knkln_1{position:fixed;top:0;left:0;bottom:0;width:420px;background:var(--color-glass-bg);-webkit-backdrop-filter:blur(var(--blur-lg));backdrop-filter:blur(var(--blur-lg));border-right:var(--border-default);box-shadow:var(--shadow-2xl);z-index:1100;animation:_slideInLeft_knkln_1 var(--duration-slow) var(--ease-smooth);display:flex;flex-direction:column}._header_knkln_33{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--space-component-sm);padding:var(--space-layout-md);border-bottom:var(--border-subtle)}._title_knkln_51{margin:0;font-size:var(--font-size-h3);color:var(--color-text-primary)}._subtitle_knkln_63{margin:0;font-size:var(--font-size-body-xs);color:var(--color-text-tertiary)}._closeButton_knkln_75{width:32px;height:32px;border-radius:var(--radius-md);border:none;background:transparent;color:var(--color-text-secondary);font-size:22px;cursor:pointer;transition:var(--transition-all)}._closeButton_knkln_75:hover{background:var(--color-bg-overlay);color:var(--color-text-primary)}._content_knkln_109{flex:1;overflow-y:auto;padding:var(--space-layout-md);display:flex;flex-direction:column;gap:var(--space-layout-md)}._section_knkln_127{display:flex;flex-direction:column;gap:var(--space-component-sm)}._sectionLabel_knkln_139{margin:0;font-size:var(--font-size-body-xs);letter-spacing:var(--letter-spacing-wide);color:var(--color-text-tertiary);text-transform:uppercase}._separator_knkln_155{width:100%;height:1px;background:linear-gradient(90deg,transparent,rgba(148,163,184,.25),transparent);margin:var(--space-component-md) 0}@keyframes _slideInLeft_knkln_1{0%{transform:translate(-40px);opacity:0}to{transform:translate(0);opacity:1}}@media (max-width: 960px){._panel_knkln_1{width:100%}}._wrapper_1qq57_1{display:flex;align-items:center;width:100%;gap:var(--gap-md)}._container_1qq57_15{display:inline-flex;align-items:center;flex-wrap:wrap;gap:var(--gap-md);pointer-events:auto;margin-left:0;padding:0;max-width:100%}._toggleButton_1qq57_37{display:inline-flex;align-items:center;gap:var(--gap-sm);padding:var(--spacing-1) var(--spacing-3);border-radius:var(--radius-full);border:var(--border-subtle);background:color-mix(in srgb,var(--color-bg-surface) 80%,transparent);color:var(--color-text-secondary);font-size:var(--font-size-body-sm);font-weight:var(--font-weight-medium);letter-spacing:var(--letter-spacing-wide);text-transform:uppercase;cursor:pointer;transition:var(--transition-colors),var(--transition-transform),var(--transition-shadow)}._toggleButton_1qq57_37:hover{color:var(--color-text-primary);border-color:color-mix(in srgb,var(--color-border-focus) 60%,transparent);background:color-mix(in srgb,var(--color-bg-overlay) 75%,transparent)}._toggleButton_1qq57_37:focus-visible{outline:none;box-shadow:var(--shadow-glow-primary);color:var(--color-text-primary)}._toggleButton_1qq57_37:active{transform:scale(.98)}._toggleButtonActive_1qq57_103{color:var(--color-text-on-primary);background:linear-gradient(135deg,var(--color-primary-500) 0%,var(--color-primary-600) 100%);border-color:color-mix(in srgb,var(--color-primary-400) 70%,transparent);box-shadow:var(--shadow-sm)}._toggleButtonActive_1qq57_103:hover{box-shadow:var(--shadow-md)}._toggleIcon_1qq57_125{display:inline-flex;width:1rem;height:1rem}._toggleIcon_1qq57_125 svg{width:100%;height:100%}._toggleButtonActive_1qq57_103 ._toggleIcon_1qq57_125{color:var(--color-text-on-primary)}._toggleLabel_1qq57_155{letter-spacing:var(--letter-spacing-wide)}._pill_1qq57_163{--pill-accent: var(--color-primary-500);--pill-accent-soft: color-mix(in srgb, var(--color-bg-overlay) 88%, var(--pill-accent) 12%);--pill-border: color-mix(in srgb, var(--pill-accent) 45%, transparent);--pill-shadow: 0 12px 28px -18px color-mix(in srgb, var(--pill-accent) 40%, transparent);display:inline-flex;align-items:center;gap:var(--gap-sm);padding:var(--spacing-1-5) var(--spacing-3);border-radius:var(--radius-full);background:var(--color-bg-overlay);background:linear-gradient(135deg,var(--pill-accent-soft) 0%,color-mix(in srgb,var(--color-bg-surface) 92%,transparent) 100%);border:1px solid var(--pill-border);color:var(--color-text-primary);box-shadow:var(--shadow-xs),var(--pill-shadow);transition:var(--transition-transform),var(--transition-shadow),var(--transition-colors);cursor:default;animation:_fadeIn_1qq57_1 var(--duration-normal) var(--ease-out)}._pill_1qq57_163:hover{transform:translateY(-2px);box-shadow:0 16px 32px -18px color-mix(in srgb,var(--pill-accent) 55%,transparent)}._pill_1qq57_163:focus-within{box-shadow:0 16px 32px -18px color-mix(in srgb,var(--pill-accent) 55%,transparent),0 0 0 2px color-mix(in srgb,var(--pill-accent) 35%,transparent)}._pillAccent_1qq57_227{width:.75rem;height:.75rem;border-radius:var(--radius-full);flex-shrink:0;background:var(--pill-accent);box-shadow:0 0 0 2px color-mix(in srgb,var(--pill-accent) 25%,transparent)}._textGroup_1qq57_245{display:flex;flex-direction:column;gap:2px;line-height:1.1}._pillTitle_1qq57_259{font-size:var(--font-size-tiny);font-weight:var(--font-weight-medium);letter-spacing:var(--letter-spacing-wide);text-transform:uppercase;color:var(--color-text-tertiary)}._pillValue_1qq57_275{font-size:var(--font-size-body-sm);font-weight:var(--font-weight-semibold);color:var(--color-text-primary);line-height:1.3}._removeButton_1qq57_289{background:none;border:none;margin-left:var(--gap-sm);display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border-radius:var(--radius-full);color:var(--color-text-tertiary);transition:var(--transition-transform),var(--transition-colors),var(--transition-shadow);cursor:pointer}._removeButton_1qq57_289:hover,._removeButton_1qq57_289:focus-visible{color:var(--color-text-primary);background:color-mix(in srgb,var(--pill-accent) 18%,transparent)}._removeButton_1qq57_289:focus-visible{outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--pill-accent) 35%,transparent)}._removeButton_1qq57_289:active{transform:scale(.92)}._removeIcon_1qq57_349{font-size:var(--font-size-body);line-height:1}._pill_1qq57_163[data-filter-type=timeRange]{--pill-accent: var(--color-tertiary-500)}._pill_1qq57_163[data-filter-type=artifactTypes]{--pill-accent: var(--color-secondary-500)}._pill_1qq57_163[data-filter-type=producers]{--pill-accent: var(--color-success)}._pill_1qq57_163[data-filter-type=tags]{--pill-accent: var(--color-secondary-400)}._pill_1qq57_163[data-filter-type=visibility]{--pill-accent: var(--color-info)}@media (max-width: 768px){._wrapper_1qq57_1{width:100%}._container_1qq57_15{border-radius:var(--radius-xl);width:100%}}@keyframes _fadeIn_1qq57_1{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.publish-control-panel{position:fixed;top:0;right:0;bottom:0;width:400px;z-index:1000;background:var(--color-glass-bg);-webkit-backdrop-filter:blur(var(--blur-lg));backdrop-filter:blur(var(--blur-lg));border-left:var(--border-default);box-shadow:var(--shadow-xl);animation:slideInRight var(--duration-slow) var(--ease-smooth)}.publish-control-panel-inner{display:flex;flex-direction:column;height:100%;overflow:hidden}.publish-control-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-layout-sm);border-bottom:var(--border-subtle);flex-shrink:0}.publish-control-title{font-size:var(--font-size-h3);font-weight:var(--font-weight-semibold);color:var(--color-text-primary);margin:0}.publish-control-close-button{display:flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;background:transparent;border:none;border-radius:var(--radius-md);color:var(--color-text-secondary);cursor:pointer;transition:var(--transition-all)}.publish-control-close-button:hover{background:var(--color-bg-overlay);color:var(--color-text-primary)}.publish-control-close-button:active{background:var(--color-bg-surface);transform:scale(.95)}.publish-control-content{flex:1;overflow-y:auto;padding:var(--space-layout-sm)}.publish-control-content::-webkit-scrollbar{width:8px}.publish-control-content::-webkit-scrollbar-track{background:var(--color-bg-base)}.publish-control-content::-webkit-scrollbar-thumb{background:var(--color-bg-overlay);border-radius:var(--radius-full)}.publish-control-content::-webkit-scrollbar-thumb:hover{background:var(--color-border-default)}.publish-control__form{display:flex;flex-direction:column;gap:var(--space-component-lg)}.publish-control__field{display:flex;flex-direction:column;gap:var(--gap-sm)}.publish-control__label{font-size:var(--font-size-body-sm);font-weight:var(--font-weight-medium);color:var(--color-text-secondary);line-height:var(--line-height-normal);margin:0}.publish-control__select{width:100%;padding:var(--space-component-sm) var(--space-component-md);padding-right:var(--spacing-8);border-radius:var(--radius-md);font-family:var(--font-family-sans);font-size:var(--font-size-body-sm);line-height:var(--line-height-normal);background:var(--color-bg-elevated);color:var(--color-text-primary);border:var(--border-default);appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3b8' d='M6 9L1 4h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right var(--spacing-3) center;cursor:pointer;transition:var(--transition-colors),var(--transition-shadow)}.publish-control__select:hover:not(:disabled){border-color:var(--color-border-strong)}.publish-control__select:focus{outline:none;border-color:var(--color-border-focus);box-shadow:var(--shadow-glow-primary)}.publish-control__select:disabled{background:var(--color-bg-base);color:var(--color-text-disabled);cursor:not-allowed;opacity:.6}.publish-control__select--error{border-color:var(--color-border-error)}.publish-control__select--error:focus{box-shadow:var(--shadow-glow-error)}.publish-control__input{width:100%;padding:var(--space-component-sm) var(--space-component-md);border-radius:var(--radius-md);font-family:var(--font-family-sans);font-size:var(--font-size-body-sm);line-height:var(--line-height-normal);background:var(--color-bg-elevated);color:var(--color-text-primary);border:var(--border-default);transition:var(--transition-colors),var(--transition-shadow)}.publish-control__input::placeholder{color:var(--color-text-muted)}.publish-control__input:hover:not(:disabled){border-color:var(--color-border-strong)}.publish-control__input:focus{outline:none;border-color:var(--color-border-focus);box-shadow:var(--shadow-glow-primary)}.publish-control__input:disabled{background:var(--color-bg-base);color:var(--color-text-disabled);cursor:not-allowed;opacity:.6}.publish-control__input--error{border-color:var(--color-border-error)}.publish-control__input--error:focus{box-shadow:var(--shadow-glow-error)}.publish-control__textarea{width:100%;padding:var(--space-component-sm) var(--space-component-md);border-radius:var(--radius-md);min-height:180px;font-family:var(--font-family-mono);font-size:var(--font-size-body-sm);line-height:var(--line-height-relaxed);background:var(--color-bg-elevated);color:var(--color-text-primary);border:var(--border-default);resize:vertical;transition:var(--transition-colors),var(--transition-shadow)}.publish-control__textarea::placeholder{color:var(--color-text-muted);font-family:var(--font-family-mono)}.publish-control__textarea:hover:not(:disabled){border-color:var(--color-border-strong)}.publish-control__textarea:focus{outline:none;border-color:var(--color-border-focus);box-shadow:var(--shadow-glow-primary)}.publish-control__textarea:disabled{background:var(--color-bg-base);color:var(--color-text-disabled);cursor:not-allowed;opacity:.6}.publish-control__textarea--error{border-color:var(--color-border-error)}.publish-control__textarea--error:focus{box-shadow:var(--shadow-glow-error)}.publish-control__checkbox-wrapper{display:flex;align-items:center;gap:var(--gap-sm);padding:var(--space-component-sm) 0}.publish-control__checkbox{width:18px;height:18px;accent-color:var(--color-primary-500);cursor:pointer;transition:var(--transition-colors)}.publish-control__checkbox:disabled{cursor:not-allowed;opacity:.5}.publish-control__checkbox:focus-visible{outline:2px solid var(--color-border-focus);outline-offset:2px;border-radius:var(--radius-sm)}.publish-control__checkbox-label{font-size:var(--font-size-body-sm);font-weight:var(--font-weight-regular);color:var(--color-text-secondary);line-height:var(--line-height-normal);cursor:pointer;-webkit-user-select:none;user-select:none;margin:0}.publish-control__checkbox:disabled+.publish-control__checkbox-label{cursor:not-allowed;opacity:.5}.publish-control__submit{width:100%;padding:var(--space-component-sm) var(--space-component-lg);border-radius:var(--radius-md);border:none;font-family:var(--font-family-sans);font-size:var(--font-size-body-sm);font-weight:var(--font-weight-semibold);line-height:var(--line-height-normal);background:var(--color-primary-500);color:var(--color-text-on-primary);box-shadow:var(--shadow-sm);cursor:pointer;transition:var(--transition-colors),var(--transition-shadow);-webkit-user-select:none;user-select:none}.publish-control__submit:hover:not(:disabled){background:var(--color-primary-600);box-shadow:var(--shadow-md);transform:translateY(-1px)}.publish-control__submit:active:not(:disabled){background:var(--color-primary-700);box-shadow:var(--shadow-xs);transform:translateY(0)}.publish-control__submit:focus-visible{outline:none;box-shadow:var(--shadow-glow-primary)}.publish-control__submit:disabled{background:var(--color-bg-overlay);color:var(--color-text-disabled);cursor:not-allowed;box-shadow:none;transform:none;opacity:.6}.publish-control__error-text{font-size:var(--font-size-caption);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-error-light);margin:0;animation:slideDown var(--duration-fast) var(--ease-out)}.publish-control__message{padding:var(--space-component-md);border-radius:var(--radius-md);font-size:var(--font-size-body-sm);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);border:var(--border-width-1) solid;animation:slideDown var(--duration-normal) var(--ease-out)}.publish-control__message--success{background:var(--color-success-bg);color:var(--color-success-light);border-color:var(--color-success-border)}.publish-control__message--error{background:var(--color-error-bg);color:var(--color-error-light);border-color:var(--color-error-border)}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.publish-control__field-hint{font-size:var(--font-size-caption);font-weight:var(--font-weight-regular);color:var(--color-text-tertiary);font-style:italic}.publish-control__hint{font-size:var(--font-size-body-sm);font-weight:var(--font-weight-regular);color:var(--color-text-secondary);line-height:var(--line-height-normal);margin:0;padding:var(--space-component-md);text-align:center;opacity:.7}@media (prefers-reduced-motion: reduce){.publish-control-panel{animation-duration:.01ms!important}.publish-control__submit,.publish-control__select,.publish-control__input,.publish-control__textarea,.publish-control__checkbox,.publish-control__message,.publish-control__error-text,.publish-control-close-button{animation-duration:.01ms!important;transition-duration:.01ms!important}.publish-control__submit:hover:not(:disabled){transform:none}}@media (max-width: 768px){.publish-control-panel{width:100%;left:0}}.settings-panel{position:fixed;top:0;right:0;bottom:0;width:550px;z-index:1000;background:var(--color-glass-bg);-webkit-backdrop-filter:blur(var(--blur-lg));backdrop-filter:blur(var(--blur-lg));border-left:var(--border-default);box-shadow:var(--shadow-xl);animation:slideInRight var(--duration-slow) var(--ease-smooth)}.settings-panel-inner{display:flex;flex-direction:column;height:100%;overflow:hidden}.settings-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-layout-sm);border-bottom:var(--border-subtle);flex-shrink:0}.settings-title{font-size:var(--font-size-h3);font-weight:var(--font-weight-semibold);color:var(--color-text-primary);margin:0}.settings-close-button{display:flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;background:transparent;border:none;border-radius:var(--radius-md);color:var(--color-text-secondary);cursor:pointer;transition:var(--transition-all)}.settings-close-button:hover{background:var(--color-bg-overlay);color:var(--color-text-primary)}.settings-close-button:active{background:var(--color-bg-surface);transform:scale(.95)}.settings-tabs{display:flex;gap:var(--gap-xs);padding:var(--space-component-md);border-bottom:var(--border-subtle);flex-shrink:0}.settings-tab{display:flex;align-items:center;gap:var(--gap-xs);padding:var(--space-component-sm) var(--space-component-md);background:transparent;border:none;border-radius:var(--radius-md);color:var(--color-text-secondary);font-size:var(--font-size-body-sm);font-weight:var(--font-weight-medium);cursor:pointer;transition:var(--transition-all);position:relative}.settings-tab svg{width:16px;height:16px}.settings-tab:hover{background:var(--color-bg-overlay);color:var(--color-text-primary)}.settings-tab.active{background:var(--color-bg-surface);color:var(--color-primary-500);box-shadow:var(--shadow-sm)}.settings-tab.active:after{content:"";position:absolute;bottom:0;left:50%;transform:translate(-50%);width:40%;height:2px;background:var(--color-primary-500);border-radius:var(--radius-full)}.settings-content{flex:1;overflow-y:auto;padding:var(--space-layout-sm)}.settings-content::-webkit-scrollbar{width:8px}.settings-content::-webkit-scrollbar-track{background:var(--color-bg-base)}.settings-content::-webkit-scrollbar-thumb{background:var(--color-bg-overlay);border-radius:var(--radius-full)}.settings-content::-webkit-scrollbar-thumb:hover{background:var(--color-border-default)}.settings-footer{display:flex;align-items:center;justify-content:space-between;padding:var(--space-component-md);border-top:var(--border-subtle);flex-shrink:0}.settings-footer-left{display:flex;align-items:center}.settings-reset-button{padding:var(--space-component-sm) var(--space-component-md);background:transparent;border:var(--border-default);border-radius:var(--radius-md);color:var(--color-text-secondary);font-size:var(--font-size-body-sm);font-weight:var(--font-weight-medium);cursor:pointer;transition:var(--transition-all)}.settings-reset-button:hover{background:var(--color-bg-overlay);border-color:var(--color-border-strong);color:var(--color-text-primary)}.settings-reset-button:active{background:var(--color-bg-surface);transform:scale(.98)}.settings-section{margin-bottom:var(--space-layout-md)}.settings-section:last-child{margin-bottom:0}.settings-section-title{font-size:var(--font-size-body-md);font-weight:var(--font-weight-semibold);color:var(--color-text-primary);margin:0 0 var(--space-component-md) 0;padding-bottom:var(--space-component-sm);border-bottom:var(--border-subtle)}.settings-field{display:flex;flex-direction:column;gap:var(--gap-xs);margin-bottom:var(--space-component-lg)}.settings-label{font-size:var(--font-size-body-sm);font-weight:var(--font-weight-medium);color:var(--color-text-secondary)}.settings-description{font-size:var(--font-size-caption);color:var(--color-text-tertiary);margin:0}.settings-select,.settings-input{width:100%;padding:var(--space-component-sm) var(--space-component-md);background:var(--color-bg-elevated);border:var(--border-default);border-radius:var(--radius-md);color:var(--color-text-primary);font-size:var(--font-size-body-sm);font-family:var(--font-family-sans);transition:var(--transition-all)}.settings-select:hover,.settings-input:hover{border-color:var(--color-border-strong)}.settings-select:focus,.settings-input:focus{outline:none;border-color:var(--color-border-focus);box-shadow:var(--shadow-glow-primary)}.settings-checkbox-wrapper{display:flex;align-items:center;gap:var(--gap-sm);padding:var(--space-component-sm) 0}.settings-checkbox{width:18px;height:18px;accent-color:var(--color-primary-500);cursor:pointer}.settings-checkbox-label{font-size:var(--font-size-body-sm);color:var(--color-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none}@keyframes slideInRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@media (prefers-reduced-motion: reduce){.settings-panel{animation-duration:.01ms!important}.settings-tab,.settings-close-button,.settings-reset-button{transition-duration:.01ms!important}}@media (max-width: 768px){.settings-panel{width:100%;left:0}}.keyboard-shortcuts-overlay{position:fixed;inset:0;background:var(--color-overlay);backdrop-filter:blur(var(--blur-sm));-webkit-backdrop-filter:blur(var(--blur-sm));display:flex;align-items:center;justify-content:center;z-index:1000;animation:fadeIn var(--duration-normal) var(--ease-smooth)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.keyboard-shortcuts-dialog{background:var(--color-bg-surface);border:var(--border-default);border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);max-width:600px;width:90%;max-height:80vh;display:flex;flex-direction:column;animation:slideInUp var(--duration-normal) var(--ease-smooth)}@keyframes slideInUp{0%{opacity:0;transform:translateY(20px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}.keyboard-shortcuts-header{display:flex;justify-content:space-between;align-items:center;padding:var(--space-layout-sm) var(--space-layout-md);border-bottom:var(--border-subtle)}.keyboard-shortcuts-title{margin:0;font-size:var(--font-size-h4);font-weight:var(--font-weight-semibold);color:var(--color-text-primary);letter-spacing:var(--letter-spacing-tight)}.keyboard-shortcuts-close{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;background:transparent;color:var(--color-text-tertiary);border:none;border-radius:var(--radius-md);cursor:pointer;transition:var(--transition-colors),var(--transition-transform)}.keyboard-shortcuts-close:hover{background:var(--color-bg-overlay);color:var(--color-text-secondary)}.keyboard-shortcuts-close:active{transform:scale(.95)}.keyboard-shortcuts-close:focus-visible{outline:none;box-shadow:var(--shadow-glow-primary)}.keyboard-shortcuts-content{flex:1;overflow-y:auto;padding:var(--space-layout-md)}.keyboard-shortcuts-category{margin-bottom:var(--space-layout-lg)}.keyboard-shortcuts-category:last-child{margin-bottom:0}.keyboard-shortcuts-category-title{margin:0 0 var(--space-component-md) 0;font-size:var(--font-size-body-sm);font-weight:var(--font-weight-semibold);color:var(--color-text-secondary);text-transform:uppercase;letter-spacing:var(--letter-spacing-wide)}.keyboard-shortcuts-list{display:flex;flex-direction:column;gap:var(--gap-sm)}.keyboard-shortcut-item{display:flex;justify-content:space-between;align-items:center;padding:var(--space-component-sm) var(--space-component-md);background:var(--color-bg-elevated);border:var(--border-subtle);border-radius:var(--radius-md);transition:var(--transition-colors)}.keyboard-shortcut-item:hover{background:var(--color-bg-overlay);border-color:var(--color-border-strong)}.keyboard-shortcut-keys{display:flex;align-items:center;gap:var(--gap-xs)}.keyboard-key{display:inline-flex;align-items:center;justify-content:center;min-width:28px;padding:var(--spacing-1) var(--spacing-2);background:var(--color-bg-base);color:var(--color-text-primary);border:var(--border-default);border-radius:var(--radius-sm);font-family:var(--font-family-mono);font-size:var(--font-size-body-sm);font-weight:var(--font-weight-medium);box-shadow:var(--shadow-xs),inset 0 -2px 0 0 var(--color-border-default);line-height:1;white-space:nowrap}.keyboard-key-separator{color:var(--color-text-tertiary);font-size:var(--font-size-caption);font-weight:var(--font-weight-medium)}.keyboard-shortcut-description{color:var(--color-text-secondary);font-size:var(--font-size-body-sm);text-align:right}.keyboard-shortcuts-footer{padding:var(--space-component-md) var(--space-layout-md);border-top:var(--border-subtle);background:var(--color-bg-elevated)}.keyboard-shortcuts-hint{margin:0;text-align:center;color:var(--color-text-tertiary);font-size:var(--font-size-caption)}.keyboard-shortcuts-content::-webkit-scrollbar{width:8px}.keyboard-shortcuts-content::-webkit-scrollbar-track{background:var(--color-bg-elevated)}.keyboard-shortcuts-content::-webkit-scrollbar-thumb{background:var(--color-border-default);border-radius:var(--radius-full);transition:var(--transition-colors)}.keyboard-shortcuts-content::-webkit-scrollbar-thumb:hover{background:var(--color-border-strong)}@media (max-width: 768px){.keyboard-shortcuts-dialog{max-width:95%;max-height:90vh}.keyboard-shortcut-item{flex-direction:column;align-items:flex-start;gap:var(--gap-sm)}.keyboard-shortcut-description{text-align:left}}@media (prefers-reduced-motion: reduce){.keyboard-shortcuts-overlay,.keyboard-shortcuts-dialog{animation:none}.keyboard-shortcuts-close{transition:none}}._connectionBadge_kl6ey_1{display:flex;align-items:center;gap:var(--gap-sm);padding:var(--spacing-1-5) var(--spacing-3);border-radius:var(--radius-full);font-size:var(--font-size-caption);font-weight:var(--font-weight-medium);box-shadow:var(--shadow-xs);transition:var(--transition-all);cursor:default}._connectionBadge_kl6ey_1._hasError_kl6ey_27{cursor:help}._connectionBadge_kl6ey_1._connected_kl6ey_37{background-color:var(--color-success-bg);color:var(--color-success-dark);border:var(--border-width-1) solid var(--color-success-border)}._connectionBadge_kl6ey_1._connecting_kl6ey_49,._connectionBadge_kl6ey_1._reconnecting_kl6ey_51{background-color:var(--color-warning-bg);color:var(--color-warning-dark);border:var(--border-width-1) solid var(--color-warning-border)}._connectionBadge_kl6ey_1._disconnected_kl6ey_63{background-color:var(--color-error-bg);color:var(--color-error-dark);border:var(--border-width-1) solid var(--color-error-border)}._statusIndicator_kl6ey_77{width:8px;height:8px;border-radius:var(--radius-circle);transition:var(--transition-opacity)}._statusIndicator_kl6ey_77._connected_kl6ey_37{background-color:var(--color-success)}._statusIndicator_kl6ey_77._connecting_kl6ey_49,._statusIndicator_kl6ey_77._reconnecting_kl6ey_51{background-color:var(--color-warning);animation:_statusPulse_kl6ey_1 2s var(--ease-smooth) infinite}._statusIndicator_kl6ey_77._disconnected_kl6ey_63{background-color:var(--color-error)}._statusText_kl6ey_121{line-height:var(--line-height-snug);letter-spacing:var(--letter-spacing-normal)}@keyframes _statusPulse_kl6ey_1{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.95)}}@media (prefers-reduced-motion: reduce){._statusIndicator_kl6ey_77._connecting_kl6ey_49,._statusIndicator_kl6ey_77._reconnecting_kl6ey_51{animation:none}._connectionBadge_kl6ey_1{transition:none}}.dashboard-layout{width:100vw;height:100vh;display:flex;flex-direction:column;background:var(--color-bg-base);overflow:hidden}.dashboard-header{display:flex;justify-content:space-between;align-items:center;gap:var(--space-layout-md);padding:var(--space-component-md) var(--space-layout-sm);background:var(--color-bg-surface);border-bottom:var(--border-subtle);box-shadow:var(--shadow-sm);z-index:100}.filter-pills-bar{position:relative;padding:var(--spacing-1-5) var(--space-layout-md);background:color-mix(in srgb,var(--color-bg-overlay) 65%,transparent);border-bottom:var(--border-subtle);-webkit-backdrop-filter:blur(var(--blur-sm));backdrop-filter:blur(var(--blur-sm));min-height:0;display:flex;align-items:center;gap:var(--gap-md);z-index:90}.dashboard-title{margin:0;font-size:var(--font-size-h3);font-weight:var(--font-weight-semibold);color:var(--color-text-primary);letter-spacing:var(--letter-spacing-tight);white-space:nowrap}.view-toggle-container{display:flex;align-items:center;gap:var(--gap-md);flex:1;justify-content:center}.view-toggle-label{font-size:var(--font-size-body-sm);font-weight:var(--font-weight-medium);color:var(--color-text-tertiary);letter-spacing:var(--letter-spacing-wide);text-transform:uppercase;font-size:var(--font-size-caption)}.view-toggle-group{display:inline-flex;background:var(--color-bg-elevated);padding:var(--spacing-1);border-radius:var(--radius-full);border:var(--border-subtle);box-shadow:var(--shadow-inner);gap:var(--gap-xs)}.view-toggle-button{position:relative;padding:var(--space-component-sm) var(--space-component-lg);background:transparent;color:var(--color-text-tertiary);border:none;border-radius:var(--radius-full);font-family:var(--font-family-sans);font-size:var(--font-size-body-sm);font-weight:var(--font-weight-medium);cursor:pointer;transition:var(--transition-colors),var(--transition-transform);white-space:nowrap}.view-toggle-button:hover:not(.active){color:var(--color-text-secondary);background:var(--color-bg-overlay)}.view-toggle-button.active{background:var(--color-primary-500);color:var(--color-text-on-primary);box-shadow:var(--shadow-sm)}.view-toggle-button:active{transform:scale(.98)}.view-toggle-button:focus-visible{outline:none;box-shadow:var(--shadow-glow-primary)}.dashboard-actions{display:flex;align-items:center;gap:var(--gap-md)}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:var(--spacing-2);background:transparent;color:var(--color-text-tertiary);border:none;border-radius:var(--radius-md);cursor:pointer;transition:var(--transition-colors),var(--transition-transform)}.icon-button:hover{background:var(--color-bg-overlay);color:var(--color-text-secondary)}.icon-button:active{transform:scale(.95)}.icon-button:focus-visible{outline:none;box-shadow:var(--shadow-glow-primary)}.help-button:hover{color:var(--color-primary-500);background:var(--color-primary-50)}.help-button:focus-visible{box-shadow:var(--shadow-glow-primary)}.clear-button:hover{color:var(--color-error);background:var(--color-error-bg)}.clear-button:focus-visible{box-shadow:var(--shadow-glow-error)}.controls-toggle{display:inline-flex;align-items:center;gap:var(--gap-sm);padding:var(--space-component-sm) var(--space-component-md);background:var(--color-bg-elevated);color:var(--color-text-secondary);border:var(--border-default);border-radius:var(--radius-md);font-family:var(--font-family-sans);font-size:var(--font-size-body-sm);font-weight:var(--font-weight-medium);cursor:pointer;transition:var(--transition-colors),var(--transition-shadow),var(--transition-transform);white-space:nowrap}.controls-toggle.primary{background:var(--color-primary-500);color:var(--color-text-on-primary);border-color:var(--color-primary-500);box-shadow:var(--shadow-md);font-weight:var(--font-weight-semibold)}.controls-toggle.primary:hover{background:var(--color-primary-600);border-color:var(--color-primary-600);box-shadow:var(--shadow-lg)}.controls-toggle.primary.active{background:var(--color-primary-700);border-color:var(--color-primary-700)}.controls-toggle:hover{background:var(--color-bg-surface);color:var(--color-text-primary);border-color:var(--color-border-strong);box-shadow:var(--shadow-sm)}.controls-toggle.active{background:var(--color-success);color:var(--color-text-on-primary);border-color:var(--color-success);box-shadow:var(--shadow-sm)}.controls-toggle.active:hover{background:var(--color-success-dark);border-color:var(--color-success-dark)}.controls-toggle:active{transform:scale(.98)}.controls-toggle:focus-visible{outline:none;box-shadow:var(--shadow-glow-primary)}.controls-toggle.active:focus-visible{box-shadow:var(--shadow-glow-success)}.controls-toggle svg{width:16px;height:16px;flex-shrink:0;transition:var(--transition-transform)}.controls-toggle.active svg{transform:rotate(45deg)}.dashboard-main{flex:1;display:flex;overflow:hidden;position:relative}.graph-container{flex:1;overflow:hidden;position:relative}.controls-sidebar{width:400px;background:var(--color-glass-bg);backdrop-filter:blur(var(--blur-lg));-webkit-backdrop-filter:blur(var(--blur-lg));border-left:var(--border-subtle);box-shadow:var(--shadow-lg);overflow-y:auto;animation:slideInRight var(--duration-slow) var(--ease-smooth)}@keyframes slideInRight{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}.controls-sidebar-inner{padding:var(--space-layout-sm)}.controls-title{margin:0 0 var(--space-layout-sm) 0;font-size:var(--font-size-h4);font-weight:var(--font-weight-semibold);color:var(--color-text-primary);letter-spacing:var(--letter-spacing-tight)}.control-section{margin-bottom:var(--space-layout-md)}.control-section-title{margin:0 0 var(--space-component-md) 0;font-size:var(--font-size-h5);font-weight:var(--font-weight-medium);color:var(--color-text-secondary)}.controls-sidebar::-webkit-scrollbar{width:8px}.controls-sidebar::-webkit-scrollbar-track{background:var(--color-bg-elevated)}.controls-sidebar::-webkit-scrollbar-thumb{background:var(--color-border-default);border-radius:var(--radius-full);transition:var(--transition-colors)}.controls-sidebar::-webkit-scrollbar-thumb:hover{background:var(--color-border-strong)}@media (max-width: 1024px){.dashboard-header{flex-wrap:wrap;gap:var(--gap-md)}.view-toggle-container{order:3;width:100%;justify-content:center}.controls-sidebar{width:320px}}@media (max-width: 768px){.dashboard-title{font-size:var(--font-size-h4)}.view-toggle-button{padding:var(--spacing-2) var(--spacing-4);font-size:var(--font-size-caption)}.controls-toggle span{display:none}.controls-sidebar{width:280px}}@media (prefers-reduced-motion: reduce){.controls-sidebar{animation:none}.view-toggle-button,.icon-button,.controls-toggle{transition:none}.controls-toggle svg{transition:none}}@media print{.dashboard-header,.controls-sidebar{display:none}.dashboard-main{flex:1}}._container_z8335_5{display:flex;align-items:center;justify-content:center;min-height:400px;padding:var(--space-layout-md);background:var(--color-bg-base)}._content_z8335_23{max-width:500px;text-align:center;padding:var(--space-layout-lg);background:var(--color-bg-surface);border:var(--border-subtle);border-radius:var(--radius-xl);box-shadow:var(--shadow-lg)}._icon_z8335_43{color:var(--color-error);margin-bottom:var(--spacing-6);animation:_shake_z8335_1 .5s var(--ease-smooth)}._title_z8335_55{font-size:var(--font-size-h2);font-weight:var(--font-weight-bold);color:var(--color-text-primary);margin:0 0 var(--spacing-4) 0;line-height:var(--line-height-tight)}._description_z8335_71{font-size:var(--font-size-body);color:var(--color-text-secondary);line-height:var(--line-height-relaxed);margin:0 0 var(--spacing-6) 0}._details_z8335_85{margin:var(--spacing-6) 0;text-align:left;background:var(--color-bg-base);border:var(--border-default);border-radius:var(--radius-md);padding:var(--space-component-md)}._summary_z8335_103{font-size:var(--font-size-body-sm);font-weight:var(--font-weight-semibold);color:var(--color-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;padding:var(--spacing-2);margin:calc(var(--space-component-md) * -1);margin-bottom:var(--spacing-3)}._summary_z8335_103:hover{color:var(--color-text-primary)}._errorMessage_z8335_133{font-size:var(--font-size-body-sm);color:var(--color-error-light);margin-bottom:var(--spacing-3);line-height:var(--line-height-relaxed)}._stackTrace_z8335_147{font-family:var(--font-family-mono);font-size:var(--font-size-caption);color:var(--color-text-tertiary);background:var(--color-bg-elevated);padding:var(--space-component-sm);border-radius:var(--radius-sm);overflow-x:auto;white-space:pre-wrap;word-break:break-word;max-height:200px;overflow-y:auto}._actions_z8335_175{display:flex;gap:var(--gap-md);justify-content:center}._primaryButton_z8335_187{padding:var(--space-component-sm) var(--space-component-lg);font-size:var(--font-size-body-sm);font-weight:var(--font-weight-semibold);color:var(--color-text-on-primary);background:var(--color-primary-500);border:none;border-radius:var(--radius-md);cursor:pointer;box-shadow:var(--shadow-sm);transition:var(--transition-colors),var(--transition-shadow)}._primaryButton_z8335_187:hover{background:var(--color-primary-600);box-shadow:var(--shadow-md)}._primaryButton_z8335_187:active{background:var(--color-primary-700);box-shadow:var(--shadow-xs);transform:translateY(1px)}._primaryButton_z8335_187:focus-visible{outline:none;box-shadow:var(--shadow-glow-primary)}._secondaryButton_z8335_245{padding:var(--space-component-sm) var(--space-component-lg);font-size:var(--font-size-body-sm);font-weight:var(--font-weight-semibold);color:var(--color-text-secondary);background:transparent;border:var(--border-default);border-radius:var(--radius-md);cursor:pointer;transition:var(--transition-colors)}._secondaryButton_z8335_245:hover{background:var(--color-bg-overlay);color:var(--color-text-primary);border-color:var(--color-border-strong)}._secondaryButton_z8335_245:active{transform:translateY(1px)}._secondaryButton_z8335_245:focus-visible{outline:none;border-color:var(--color-border-focus);box-shadow:var(--shadow-glow-primary)}@keyframes _shake_z8335_1{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-4px)}20%,40%,60%,80%{transform:translate(4px)}}@media (prefers-reduced-motion: reduce){._icon_z8335_43{animation:none}}._container_1vp06_1{display:flex;flex-direction:column;gap:var(--space-layout-sm);height:100%;padding:var(--space-layout-sm);background:var(--color-bg-surface)}._header_1vp06_19{display:flex;align-items:center;justify-content:space-between;gap:var(--gap-sm)}._metrics_1vp06_33{display:flex;gap:var(--gap-lg);flex-wrap:wrap}._metricLabel_1vp06_45{display:block;font-size:var(--font-size-overline);color:var(--color-text-tertiary);text-transform:uppercase;letter-spacing:var(--letter-spacing-wider)}._metricValue_1vp06_61{font-size:var(--font-size-body-md);font-weight:var(--font-weight-semibold);color:var(--color-text-primary)}._actions_1vp06_73{display:flex;gap:var(--gap-sm)}._actions_1vp06_73 button{padding:var(--spacing-1) var(--spacing-3);border-radius:var(--radius-sm);border:none;cursor:pointer;background:#6366f140;color:var(--color-text-primary);transition:background-color var(--duration-fast) ease}._actions_1vp06_73 button:disabled{opacity:.5;cursor:not-allowed}._actions_1vp06_73 button:hover:not(:disabled){background:#6366f173}._tableContainer_1vp06_121{display:flex;flex-direction:column;background:#0f172a99;border:1px solid rgba(148,163,184,.1);border-radius:var(--radius-md);overflow:hidden;flex:2 1 0;min-height:0}._contentArea_1vp06_143{display:flex;gap:var(--space-layout-sm);flex:1;min-height:0}._detailPanel_1vp06_157{flex:1 1 0;background:#0f172a80;border:1px solid rgba(148,163,184,.15);border-radius:var(--radius-md);padding:var(--spacing-3);display:flex;flex-direction:column;gap:var(--gap-md);max-height:100%;overflow-y:auto}._headerRow_1vp06_183,._dataRow_1vp06_185{display:grid;grid-template-columns:200px 140px 160px 180px 160px 120px 100px;gap:var(--gap-sm);padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-body-xs);align-items:center}._headerRow_1vp06_183{background:#94a3b81a;font-weight:var(--font-weight-semibold);text-transform:uppercase;letter-spacing:var(--letter-spacing-wide)}._dataRow_1vp06_185{border-bottom:1px solid rgba(148,163,184,.08)}._virtualViewport_1vp06_225{overflow-y:auto}._dataRowStripe_1vp06_233{background:#94a3b80d}._dataRow_1vp06_185{cursor:pointer;transition:background-color var(--duration-fast) ease}._dataRow_1vp06_185:focus-visible{outline:2px solid var(--color-primary-400);outline-offset:2px}._dataRowSelected_1vp06_261{background:#6366f140;border-left:3px solid var(--color-primary-400)}._dataRowSelected_1vp06_261 span{color:var(--color-text-primary)}._emptyState_1vp06_279,._loading_1vp06_281,._error_1vp06_283{padding:var(--spacing-4);text-align:center;color:var(--color-text-secondary)}._error_1vp06_283{color:var(--color-error)}._retentionBanner_1vp06_303{display:flex;flex-direction:column;gap:var(--gap-xs);background:#94a3b814;border:1px solid rgba(148,163,184,.15);border-radius:var(--radius-sm);padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-body-xs);color:var(--color-text-secondary)}._retentionBanner_1vp06_303 strong{color:var(--color-text-primary);font-weight:var(--font-weight-semibold)}@media (max-width: 1280px){._contentArea_1vp06_143{flex-direction:column}._detailPanel_1vp06_157{max-height:none}}._detailHeader_1vp06_357{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--gap-sm)}._detailHeader_1vp06_357 h3{margin:0;font-size:var(--font-size-body-lg);color:var(--color-text-primary)}._detailHeader_1vp06_357 p{margin:0;font-size:var(--font-size-body-xs);color:var(--color-text-secondary)}._detailHeader_1vp06_357 button{padding:var(--spacing-1) var(--spacing-3);border-radius:var(--radius-sm);border:none;cursor:pointer;background:#6366f133;color:var(--color-primary-50);font-size:var(--font-size-body-xs);text-transform:uppercase;letter-spacing:var(--letter-spacing-wide)}._detailHeader_1vp06_357 button:hover{background:#6366f159}._detailSection_1vp06_427{display:flex;flex-direction:column;gap:var(--gap-sm)}._detailSection_1vp06_427 h4{margin:0;font-size:var(--font-size-body-sm);color:var(--color-text-secondary);text-transform:uppercase;letter-spacing:var(--letter-spacing-wide)}._detailList_1vp06_455{display:grid;grid-template-columns:minmax(110px,.45fr) 1fr;gap:var(--gap-xs) var(--gap-sm);font-size:var(--font-size-body-xs)}._detailList_1vp06_455 dt{color:var(--color-text-tertiary);font-weight:var(--font-weight-semibold)}._detailList_1vp06_455 dd{margin:0;color:var(--color-text-primary);word-break:break-word}._consumptionList_1vp06_491{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--gap-xs)}._consumptionList_1vp06_491 li{display:flex;flex-direction:column;gap:2px;padding:var(--spacing-2);border:1px solid rgba(148,163,184,.18);border-radius:var(--radius-sm);background:#1e293b8c}._consumerName_1vp06_529{font-weight:var(--font-weight-semibold);color:var(--color-text-primary)}._runBadge_1vp06_539{display:inline-flex;align-items:center;justify-content:center;align-self:flex-start;padding:2px 6px;border-radius:var(--radius-xs);font-size:var(--font-size-caption);background:#6366f140;color:var(--color-primary-100);margin-top:2px}._emptyDetail_1vp06_565,._emptyConsumption_1vp06_567{font-size:var(--font-size-body-sm);color:var(--color-text-secondary);margin:0}:root{--color-bg-base: #0a0a0b;--color-bg-elevated: #121214;--color-bg-surface: #1a1a1e;--color-bg-overlay: #232329;--color-bg-float: #2a2a32;--color-primary-50: #eef2ff;--color-primary-100: #e0e7ff;--color-primary-200: #c7d2fe;--color-primary-300: #a5b4fc;--color-primary-400: #818cf8;--color-primary-500: #6366f1;--color-primary-600: #4f46e5;--color-primary-700: #4338ca;--color-primary-800: #3730a3;--color-primary-900: #312e81;--color-secondary-50: #fdf4ff;--color-secondary-100: #fae8ff;--color-secondary-200: #f5d0fe;--color-secondary-300: #f0abfc;--color-secondary-400: #e879f9;--color-secondary-500: #d946ef;--color-secondary-600: #c026d3;--color-secondary-700: #a21caf;--color-secondary-800: #86198f;--color-secondary-900: #701a75;--color-tertiary-50: #ecfeff;--color-tertiary-100: #cffafe;--color-tertiary-200: #a5f3fc;--color-tertiary-300: #67e8f9;--color-tertiary-400: #22d3ee;--color-tertiary-500: #06b6d4;--color-tertiary-600: #0891b2;--color-tertiary-700: #0e7490;--color-tertiary-800: #155e75;--color-tertiary-900: #164e63;--color-success-light: #6ee7b7;--color-success: #10b981;--color-success-dark: #047857;--color-success-bg: rgba(16, 185, 129, .1);--color-success-border: rgba(16, 185, 129, .3);--color-warning-light: #fbbf24;--color-warning: #f59e0b;--color-warning-dark: #d97706;--color-warning-bg: rgba(245, 158, 11, .1);--color-warning-border: rgba(245, 158, 11, .3);--color-error-light: #f87171;--color-error: #ef4444;--color-error-dark: #dc2626;--color-error-bg: rgba(239, 68, 68, .1);--color-error-border: rgba(239, 68, 68, .3);--color-info-light: #60a5fa;--color-info: #3b82f6;--color-info-dark: #2563eb;--color-info-bg: rgba(59, 130, 246, .1);--color-info-border: rgba(59, 130, 246, .3);--color-active-light: #818cf8;--color-active: #6366f1;--color-active-dark: #4f46e5;--color-active-bg: rgba(99, 102, 241, .1);--color-active-border: rgba(99, 102, 241, .3);--color-idle-light: #94a3b8;--color-idle: #64748b;--color-idle-dark: #475569;--color-idle-bg: rgba(100, 116, 139, .1);--color-idle-border: rgba(100, 116, 139, .3);--color-text-primary: #f8fafc;--color-text-secondary: #cbd5e1;--color-text-tertiary: #94a3b8;--color-text-muted: #64748b;--color-text-disabled: #475569;--color-text-on-primary: #ffffff;--color-text-on-dark: #0f172a;--color-border-subtle: #1e293b;--color-border-default: #334155;--color-border-strong: #475569;--color-border-focus: #6366f1;--color-border-error: #ef4444;--color-divider: rgba(148, 163, 184, .1);--color-node-agent-bg: #1e293b;--color-node-agent-border: #3b82f6;--color-node-agent-border-selected: #6366f1;--color-node-agent-text: #f8fafc;--color-node-agent-badge: #334155;--color-node-agent-badge-text: #94a3b8;--color-node-message-bg: #422006;--color-node-message-border: #f59e0b;--color-node-message-border-selected: #d946ef;--color-node-message-text: #fef3c7;--color-node-message-metadata: #a16207;--color-edge-default: #475569;--color-edge-active: #6366f1;--color-edge-message: #f59e0b;--color-edge-error: #ef4444;--color-edge-label-bg: rgba(26, 26, 30, .95);--color-edge-label-text: #cbd5e1;--color-glass-bg: rgba(26, 26, 30, .8);--color-glass-border: rgba(148, 163, 184, .1);--color-overlay-backdrop: rgba(10, 10, 11, .7);--color-modal-backdrop: rgba(10, 10, 11, .85);--font-family-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif;--font-family-mono: "JetBrains Mono", "Fira Code", "Consolas", "Monaco", "Courier New", monospace;--font-family-display: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;--font-size-display-2xl: 72px;--font-size-display-xl: 60px;--font-size-display-lg: 48px;--font-size-display-md: 36px;--font-size-h1: 32px;--font-size-h2: 24px;--font-size-h3: 20px;--font-size-h4: 18px;--font-size-h5: 16px;--font-size-h6: 14px;--font-size-body-xl: 20px;--font-size-body-lg: 18px;--font-size-body: 16px;--font-size-body-sm: 14px;--font-size-body-xs: 12px;--font-size-caption: 12px;--font-size-overline: 10px;--font-size-tiny: 10px;--font-weight-light: 300;--font-weight-regular: 400;--font-weight-medium: 500;--font-weight-semibold: 600;--font-weight-bold: 700;--line-height-tight: 1.1;--line-height-snug: 1.375;--line-height-normal: 1.5;--line-height-relaxed: 1.625;--line-height-loose: 2;--letter-spacing-tight: -.02em;--letter-spacing-normal: 0;--letter-spacing-wide: .025em;--letter-spacing-wider: .05em;--letter-spacing-widest: .1em;--spacing-0: 0;--spacing-0-5: 2px;--spacing-1: 4px;--spacing-1-5: 6px;--spacing-2: 8px;--spacing-3: 12px;--spacing-4: 16px;--spacing-5: 20px;--spacing-6: 24px;--spacing-8: 32px;--spacing-10: 40px;--spacing-12: 48px;--spacing-16: 64px;--spacing-20: 80px;--spacing-24: 96px;--spacing-32: 128px;--spacing-40: 160px;--spacing-48: 192px;--spacing-56: 224px;--spacing-64: 256px;--space-component-xs: var(--spacing-2);--space-component-sm: var(--spacing-3);--space-component-md: var(--spacing-4);--space-component-lg: var(--spacing-6);--space-component-xl: var(--spacing-8);--space-layout-xs: var(--spacing-4);--space-layout-sm: var(--spacing-6);--space-layout-md: var(--spacing-8);--space-layout-lg: var(--spacing-12);--space-layout-xl: var(--spacing-16);--space-layout-2xl: var(--spacing-24);--gap-xs: var(--spacing-1);--gap-sm: var(--spacing-2);--gap-md: var(--spacing-3);--gap-lg: var(--spacing-4);--gap-xl: var(--spacing-6);--shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, .4);--shadow-sm: 0 2px 4px -1px rgba(0, 0, 0, .5), 0 1px 2px -1px rgba(0, 0, 0, .3);--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .6), 0 2px 4px -1px rgba(0, 0, 0, .4);--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .7), 0 4px 6px -2px rgba(0, 0, 0, .5);--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, .8), 0 10px 10px -5px rgba(0, 0, 0, .6);--shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, .9);--shadow-glow-primary: 0 0 0 3px rgba(99, 102, 241, .3);--shadow-glow-secondary: 0 0 0 3px rgba(217, 70, 239, .3);--shadow-glow-success: 0 0 0 3px rgba(16, 185, 129, .3);--shadow-glow-error: 0 0 0 3px rgba(239, 68, 68, .3);--shadow-glow-warning: 0 0 0 3px rgba(245, 158, 11, .3);--shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, .5);--shadow-inner-lg: inset 0 4px 8px 0 rgba(0, 0, 0, .6);--border-width-0: 0;--border-width-1: 1px;--border-width-2: 2px;--border-width-3: 3px;--border-width-4: 4px;--radius-none: 0;--radius-sm: 4px;--radius-md: 6px;--radius-lg: 8px;--radius-xl: 12px;--radius-2xl: 16px;--radius-3xl: 24px;--radius-full: 9999px;--radius-circle: 50%;--border-subtle: var(--border-width-1) solid var(--color-border-subtle);--border-default: var(--border-width-1) solid var(--color-border-default);--border-strong: var(--border-width-2) solid var(--color-border-strong);--border-focus: var(--border-width-2) solid var(--color-border-focus);--border-error: var(--border-width-2) solid var(--color-border-error);--duration-instant: 0ms;--duration-fast: .1s;--duration-normal: .2s;--duration-slow: .3s;--duration-slower: .4s;--duration-slowest: .5s;--ease-linear: linear;--ease-in: cubic-bezier(.4, 0, 1, 1);--ease-out: cubic-bezier(0, 0, .2, 1);--ease-in-out: cubic-bezier(.4, 0, .2, 1);--ease-smooth: cubic-bezier(.4, 0, .6, 1);--ease-bounce: cubic-bezier(.68, -.55, .265, 1.55);--ease-elastic: cubic-bezier(.175, .885, .32, 1.275);--ease-sharp: cubic-bezier(.4, 0, .6, 1);--transition-colors: color var(--duration-fast) var(--ease-smooth), background-color var(--duration-fast) var(--ease-smooth), border-color var(--duration-fast) var(--ease-smooth);--transition-opacity: opacity var(--duration-normal) var(--ease-out);--transition-transform: transform var(--duration-normal) var(--ease-smooth);--transition-shadow: box-shadow var(--duration-normal) var(--ease-out);--transition-all: all var(--duration-normal) var(--ease-smooth);--transition-base: var(--duration-normal) var(--ease-smooth);--blur-sm: 4px;--blur-md: 8px;--blur-lg: 12px;--blur-xl: 16px;--blur-2xl: 24px}*{margin:0;padding:0;box-sizing:border-box}html{font-size:16px}body{font-family:var(--font-family-sans);font-size:var(--font-size-body);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background:var(--color-bg-base);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}h1,h2,h3,h4,h5,h6{margin:0;font-weight:var(--font-weight-semibold);color:var(--color-text-primary)}h1{font-size:var(--font-size-h1);font-weight:var(--font-weight-bold);line-height:var(--line-height-tight);letter-spacing:var(--letter-spacing-tight)}h2{font-size:var(--font-size-h2);line-height:var(--line-height-tight);letter-spacing:var(--letter-spacing-tight)}h3{font-size:var(--font-size-h3);line-height:var(--line-height-snug)}h4{font-size:var(--font-size-h4);font-weight:var(--font-weight-medium);line-height:var(--line-height-snug)}h5,h6{font-size:var(--font-size-h5);font-weight:var(--font-weight-medium);line-height:var(--line-height-normal);color:var(--color-text-secondary)}code,pre{font-family:var(--font-family-mono);font-size:.9em;line-height:var(--line-height-relaxed)}button{font-family:inherit;cursor:pointer}#root{width:100vw;height:100vh;overflow:hidden}@keyframes pulse{0%,to{opacity:1}50%{opacity:.6}}.react-flow__controls{background:var(--color-bg-surface)!important;border:1px solid var(--color-border-default)!important}.react-flow__controls-button{background:var(--color-bg-overlay)!important;border:none!important;border-bottom:1px solid var(--color-border-subtle)!important;color:var(--color-text-primary)!important;transition:var(--transition-all)!important;width:32px!important;height:32px!important}.react-flow__controls-button:hover{background:var(--color-primary-500)!important;color:#fff!important}.react-flow__controls-button svg{fill:currentColor!important;width:18px!important;height:18px!important}@media (prefers-reduced-motion: reduce){*{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}*::-webkit-scrollbar{width:10px;height:10px}*::-webkit-scrollbar-track{background:var(--color-bg-base);border-radius:var(--radius-sm, 4px)}*::-webkit-scrollbar-thumb{background:var(--color-bg-overlay);border-radius:var(--radius-sm, 4px);border:2px solid var(--color-bg-base)}*::-webkit-scrollbar-thumb:hover{background:var(--color-border-default)}*::-webkit-scrollbar-thumb:active{background:var(--color-border-strong)}*,div[style*="overflow: auto"],div[style*="overflow-y: auto"],div[style*="overflow-x: auto"],.settings-content,.publish-content{scrollbar-width:thin;scrollbar-color:var(--color-bg-overlay) var(--color-bg-base)}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>🦆🐓 Flock 🐤🐧</title>
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-DFRnI_mt.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-fPLNdmp1.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="root"></div>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""WebSocket connection manager for real-time dashboard communication.
|
|
2
|
+
|
|
3
|
+
Manages WebSocket client pool, broadcasts events to all connected clients,
|
|
4
|
+
and implements heartbeat/ping mechanism to keep connections alive.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import contextlib
|
|
9
|
+
from collections import defaultdict, deque
|
|
10
|
+
from typing import Union
|
|
11
|
+
|
|
12
|
+
from fastapi import WebSocket
|
|
13
|
+
|
|
14
|
+
from flock.dashboard.events import (
|
|
15
|
+
AgentActivatedEvent,
|
|
16
|
+
AgentCompletedEvent,
|
|
17
|
+
AgentErrorEvent,
|
|
18
|
+
MessagePublishedEvent,
|
|
19
|
+
StreamingOutputEvent,
|
|
20
|
+
)
|
|
21
|
+
from flock.logging.logging import get_logger
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
logger = get_logger("dashboard.websocket")
|
|
25
|
+
|
|
26
|
+
# Type alias for dashboard events
|
|
27
|
+
DashboardEvent = Union[
|
|
28
|
+
AgentActivatedEvent,
|
|
29
|
+
MessagePublishedEvent,
|
|
30
|
+
StreamingOutputEvent,
|
|
31
|
+
AgentCompletedEvent,
|
|
32
|
+
AgentErrorEvent,
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class WebSocketManager:
|
|
37
|
+
"""Manages WebSocket connections and broadcasts dashboard events.
|
|
38
|
+
|
|
39
|
+
Features:
|
|
40
|
+
- Connection pool management (add/remove clients)
|
|
41
|
+
- Broadcast events to all connected clients
|
|
42
|
+
- Heartbeat/ping mechanism (DISABLED by default - causes unnecessary disconnects)
|
|
43
|
+
- Graceful handling of disconnected clients during broadcast
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, heartbeat_interval: int = 120, enable_heartbeat: bool = False):
|
|
47
|
+
"""Initialize WebSocket manager.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
heartbeat_interval: Seconds between heartbeat pings (default: 120)
|
|
51
|
+
enable_heartbeat: Enable heartbeat pings (default: False - disabled to prevent
|
|
52
|
+
unnecessary disconnects. WebSocket auto-reconnects on real network issues.)
|
|
53
|
+
"""
|
|
54
|
+
self.clients: set[WebSocket] = set()
|
|
55
|
+
self.heartbeat_interval = heartbeat_interval
|
|
56
|
+
self.enable_heartbeat = enable_heartbeat
|
|
57
|
+
self._heartbeat_task: asyncio.Task | None = None
|
|
58
|
+
self._shutdown = False
|
|
59
|
+
|
|
60
|
+
# Store streaming output events by agent_name for history (max 128344 per agent)
|
|
61
|
+
self._streaming_history: dict[str, deque[StreamingOutputEvent]] = defaultdict(
|
|
62
|
+
lambda: deque(maxlen=128344)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
async def add_client(self, websocket: WebSocket) -> None:
|
|
66
|
+
"""Add WebSocket client to connection pool.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
websocket: FastAPI WebSocket connection to add
|
|
70
|
+
"""
|
|
71
|
+
self.clients.add(websocket)
|
|
72
|
+
logger.info(f"WebSocket client added. Total clients: {len(self.clients)}")
|
|
73
|
+
|
|
74
|
+
# Start heartbeat task if enabled and not already running
|
|
75
|
+
if self.enable_heartbeat and self._heartbeat_task is None and not self._shutdown:
|
|
76
|
+
self._heartbeat_task = asyncio.create_task(self._heartbeat_loop())
|
|
77
|
+
|
|
78
|
+
async def remove_client(self, websocket: WebSocket) -> None:
|
|
79
|
+
"""Remove WebSocket client from connection pool.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
websocket: FastAPI WebSocket connection to remove
|
|
83
|
+
"""
|
|
84
|
+
self.clients.discard(websocket)
|
|
85
|
+
logger.info(f"WebSocket client removed. Total clients: {len(self.clients)}")
|
|
86
|
+
|
|
87
|
+
# Stop heartbeat task if no clients remain
|
|
88
|
+
if len(self.clients) == 0 and self._heartbeat_task is not None:
|
|
89
|
+
self._heartbeat_task.cancel()
|
|
90
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
91
|
+
await self._heartbeat_task
|
|
92
|
+
self._heartbeat_task = None
|
|
93
|
+
|
|
94
|
+
async def broadcast(self, event: DashboardEvent) -> None:
|
|
95
|
+
"""Broadcast event to all connected clients as JSON.
|
|
96
|
+
|
|
97
|
+
Handles disconnected clients gracefully by removing them from pool.
|
|
98
|
+
Uses return_exceptions=True to prevent one client failure from affecting others.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
event: Dashboard event to broadcast (AgentActivatedEvent, etc.)
|
|
102
|
+
"""
|
|
103
|
+
# Store streaming output events for history (always, even if no clients)
|
|
104
|
+
if isinstance(event, StreamingOutputEvent):
|
|
105
|
+
self._streaming_history[event.agent_name].append(event)
|
|
106
|
+
# logger.debug(
|
|
107
|
+
# f"Stored streaming event for {event.agent_name}, history size: {len(self._streaming_history[event.agent_name])}"
|
|
108
|
+
# )
|
|
109
|
+
|
|
110
|
+
# If no clients, still log but don't broadcast
|
|
111
|
+
if not self.clients:
|
|
112
|
+
logger.debug(
|
|
113
|
+
f"No clients connected, stored event but skipping broadcast of {type(event).__name__}"
|
|
114
|
+
)
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
# Log broadcast attempt
|
|
118
|
+
# logger.debug(f"Broadcasting {type(event).__name__} to {len(self.clients)} client(s)")
|
|
119
|
+
|
|
120
|
+
# Serialize event to JSON using Pydantic's model_dump_json
|
|
121
|
+
message = event.model_dump_json()
|
|
122
|
+
# logger.debug(f"Event JSON: {message[:200]}...") # Log first 200 chars
|
|
123
|
+
|
|
124
|
+
# Broadcast to all clients concurrently
|
|
125
|
+
# Use return_exceptions=True to handle client failures gracefully
|
|
126
|
+
# Use send_text() for FastAPI WebSocket (send JSON string as text)
|
|
127
|
+
# CRITICAL: Add timeout to prevent deadlock when client send buffer is full
|
|
128
|
+
clients_list = list(self.clients) # Copy to avoid modification during iteration
|
|
129
|
+
|
|
130
|
+
send_tasks = [
|
|
131
|
+
asyncio.wait_for(client.send_text(message), timeout=0.5) # 500ms timeout
|
|
132
|
+
for client in clients_list
|
|
133
|
+
]
|
|
134
|
+
results = await asyncio.gather(*send_tasks, return_exceptions=True)
|
|
135
|
+
|
|
136
|
+
# Remove clients that failed to receive the message
|
|
137
|
+
failed_clients = []
|
|
138
|
+
for client, result in zip(clients_list, results, strict=False):
|
|
139
|
+
if isinstance(result, Exception):
|
|
140
|
+
# Check if it's a timeout (backpressure) or other error
|
|
141
|
+
if isinstance(result, asyncio.TimeoutError):
|
|
142
|
+
logger.warning(
|
|
143
|
+
"Client send timeout (backpressure) - client is slow or disconnected, removing client"
|
|
144
|
+
)
|
|
145
|
+
else:
|
|
146
|
+
logger.warning(f"Failed to send to client: {result}")
|
|
147
|
+
failed_clients.append(client)
|
|
148
|
+
|
|
149
|
+
# Clean up failed clients
|
|
150
|
+
for client in failed_clients:
|
|
151
|
+
await self.remove_client(client)
|
|
152
|
+
|
|
153
|
+
async def _heartbeat_loop(self) -> None:
|
|
154
|
+
"""Send ping to all clients every heartbeat_interval seconds.
|
|
155
|
+
|
|
156
|
+
Keeps WebSocket connections alive and detects disconnected clients.
|
|
157
|
+
Runs continuously until cancelled or all clients disconnect.
|
|
158
|
+
"""
|
|
159
|
+
logger.info(f"Starting heartbeat loop (interval: {self.heartbeat_interval}s)")
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
while not self._shutdown and len(self.clients) > 0:
|
|
163
|
+
await asyncio.sleep(self.heartbeat_interval)
|
|
164
|
+
|
|
165
|
+
if not self.clients:
|
|
166
|
+
break
|
|
167
|
+
|
|
168
|
+
# Send ping to all clients
|
|
169
|
+
ping_tasks = []
|
|
170
|
+
for client in list(self.clients): # Copy to avoid modification during iteration
|
|
171
|
+
ping_tasks.append(self._ping_client(client))
|
|
172
|
+
|
|
173
|
+
# Execute pings concurrently
|
|
174
|
+
await asyncio.gather(*ping_tasks, return_exceptions=True)
|
|
175
|
+
|
|
176
|
+
except asyncio.CancelledError:
|
|
177
|
+
logger.info("Heartbeat loop cancelled")
|
|
178
|
+
raise
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logger.exception(f"Heartbeat loop error: {e}")
|
|
181
|
+
|
|
182
|
+
async def _ping_client(self, client: WebSocket) -> None:
|
|
183
|
+
"""Send ping to single client.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
client: WebSocket client to ping
|
|
187
|
+
"""
|
|
188
|
+
try:
|
|
189
|
+
await client.send_json({"type": "ping", "timestamp": asyncio.get_event_loop().time()})
|
|
190
|
+
except Exception as e:
|
|
191
|
+
logger.warning(f"Failed to ping client: {e}")
|
|
192
|
+
await self.remove_client(client)
|
|
193
|
+
|
|
194
|
+
async def start_heartbeat(self) -> None:
|
|
195
|
+
"""Start heartbeat task manually (for testing).
|
|
196
|
+
|
|
197
|
+
In production, heartbeat is disabled by default (enable_heartbeat=False).
|
|
198
|
+
Only starts if enable_heartbeat=True.
|
|
199
|
+
"""
|
|
200
|
+
if self.enable_heartbeat and self._heartbeat_task is None and not self._shutdown:
|
|
201
|
+
self._heartbeat_task = asyncio.create_task(self._heartbeat_loop())
|
|
202
|
+
|
|
203
|
+
async def shutdown(self) -> None:
|
|
204
|
+
"""Shutdown manager and close all WebSocket connections.
|
|
205
|
+
|
|
206
|
+
Cancels heartbeat task and closes all client connections gracefully.
|
|
207
|
+
"""
|
|
208
|
+
logger.info("Shutting down WebSocketManager")
|
|
209
|
+
self._shutdown = True
|
|
210
|
+
|
|
211
|
+
# Cancel heartbeat task
|
|
212
|
+
if self._heartbeat_task is not None:
|
|
213
|
+
self._heartbeat_task.cancel()
|
|
214
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
215
|
+
await self._heartbeat_task
|
|
216
|
+
self._heartbeat_task = None
|
|
217
|
+
|
|
218
|
+
# Close all client connections
|
|
219
|
+
close_tasks = []
|
|
220
|
+
for client in list(self.clients):
|
|
221
|
+
# Handle both real WebSocket and mock objects
|
|
222
|
+
if hasattr(client, "close") and callable(client.close):
|
|
223
|
+
result = client.close()
|
|
224
|
+
# Only await if it's a coroutine
|
|
225
|
+
if asyncio.iscoroutine(result):
|
|
226
|
+
close_tasks.append(result)
|
|
227
|
+
|
|
228
|
+
if close_tasks:
|
|
229
|
+
await asyncio.gather(*close_tasks, return_exceptions=True)
|
|
230
|
+
|
|
231
|
+
self.clients.clear()
|
|
232
|
+
logger.info("WebSocketManager shutdown complete")
|
|
233
|
+
|
|
234
|
+
def get_streaming_history(self, agent_name: str) -> list[StreamingOutputEvent]:
|
|
235
|
+
"""Get historical streaming output events for a specific agent.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
agent_name: Name of the agent to get history for
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
List of StreamingOutputEvent events for the agent
|
|
242
|
+
"""
|
|
243
|
+
return list(self._streaming_history.get(agent_name, []))
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
__all__ = ["DashboardEvent", "WebSocketManager"]
|