From e3562d3052d39fee29667b4f709730a6013d4029 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 23:25:40 +0000 Subject: [PATCH 01/12] feat(dashboard): promote the engine's model and service layer to the platform's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dashboard engine shipped model classes for agents, runs, versions, templates, sandboxes and recordings, but no migrations to back them — the platform's own copies had grown apart and were the only ones running. Replace the drifted copies with the platform's implementations, add the schema they need, and give the engine the seams a host app has to reach: - table_name_prefix, so the engine's active_agent_* tables and a host app that already owns them unprefixed are both supported without renaming production tables - Ownable, resolving the owner association from configuration rather than assuming an Account or a User exists - quota_checker / provider_credentials_resolver / trace_owner_resolver, so plan limits, per-tenant LLM credentials and agent attribution stay in the app that has that data - sandbox_backends, replacing the hardcoded Incus/Kubernetes/Cloud Run switch with a registry; the engine ships the in-memory backend and the operator registers the rest Conversation persistence (contexts, messages, generations), evaluations, scorecards and cost estimates come along, so a mounted engine has the same feature set the hosted product does. Queries that were PostgreSQL-only now have portable forms: COUNT FILTER becomes SUM CASE, DISTINCT ON becomes a max-id pass, jsonb traversal falls back to reading the column in Ruby, and jsonb columns are declared json on adapters without jsonb. The dashboard runs on SQLite and MySQL again. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fsu5fid9JgX97zFNkkQVNz --- lib/active_agent/dashboard.rb | 117 ++++ .../dashboard/agent_execution_job.rb | 36 +- .../dashboard/sandbox_cleanup_job.rb | 55 +- .../dashboard/sandbox_provision_job.rb | 83 ++- .../active_agent/dashboard/sandbox_run_job.rb | 287 +++++++++ .../dashboard/trace_retention_job.rb | 59 ++ .../models/active_agent/dashboard/agent.rb | 212 +++++-- .../active_agent/dashboard/agent_context.rb | 131 ++++ .../dashboard/agent_generation.rb | 50 ++ .../active_agent/dashboard/agent_memory.rb | 52 ++ .../dashboard/agent_memory_entry.rb | 16 + .../active_agent/dashboard/agent_message.rb | 45 ++ .../active_agent/dashboard/agent_run.rb | 88 ++- .../active_agent/dashboard/agent_template.rb | 50 +- .../active_agent/dashboard/agent_version.rb | 12 +- .../models/active_agent/dashboard/api_key.rb | 54 ++ .../dashboard/application_record.rb | 25 +- .../active_agent/dashboard/evaluation.rb | 82 +++ .../active_agent/dashboard/evaluation_run.rb | 22 + .../active_agent/dashboard/model_pricing.rb | 82 +++ .../active_agent/dashboard/provider_key.rb | 61 ++ .../dashboard/recording_action.rb | 18 +- .../dashboard/recording_snapshot.rb | 16 +- .../dashboard/sandbox_instance_tier.rb | 370 ++++++++++++ .../active_agent/dashboard/sandbox_run.rb | 9 +- .../active_agent/dashboard/sandbox_session.rb | 58 +- .../dashboard/session_recording.rb | 32 +- .../models/active_agent/telemetry_trace.rb | 2 + .../active_agent/dashboard/adapter_aware.rb | 19 + .../active_agent/dashboard/ownable.rb | 49 ++ .../dashboard/session_recordable.rb | 93 +++ .../dashboard/agent_executions.rb | 199 ++++++ .../dashboard/agent_message_serializer.rb | 25 + .../dashboard/telemetry_trace_serializer.rb | 124 ++++ .../dashboard/trace_interaction_serializer.rb | 230 +++++++ .../dashboard/agent_execution_service.rb | 564 ++++++++++++++++++ .../active_agent/dashboard/agent_registrar.rb | 199 ++++++ .../active_agent/dashboard/agent_scorecard.rb | 209 +++++++ .../active_agent/dashboard/agent_toolbox.rb | 506 ++++++++++++++++ .../dashboard/evaluation_runner_service.rb | 483 +++++++++++++++ .../dashboard/mcp_recording_middleware.rb | 243 ++++++++ .../dashboard/mock_sandbox_backend.rb | 54 ++ .../dashboard/playwright_mcp_client.rb | 150 +++++ .../dashboard/sandbox_orchestrator.rb | 244 ++++++++ .../dashboard/session_recording_service.rb | 230 +++++++ ...01_create_active_agent_dashboard_tables.rb | 311 ++++++++++ 46 files changed, 5725 insertions(+), 331 deletions(-) create mode 100644 lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_run_job.rb create mode 100644 lib/active_agent/dashboard/app/jobs/active_agent/dashboard/trace_retention_job.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_context.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_generation.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_memory.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_memory_entry.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_message.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/dashboard/api_key.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/dashboard/evaluation.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/dashboard/evaluation_run.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/dashboard/model_pricing.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/dashboard/provider_key.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_instance_tier.rb create mode 100644 lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/adapter_aware.rb create mode 100644 lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/ownable.rb create mode 100644 lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/session_recordable.rb create mode 100644 lib/active_agent/dashboard/app/queries/active_agent/dashboard/agent_executions.rb create mode 100644 lib/active_agent/dashboard/app/serializers/active_agent/dashboard/agent_message_serializer.rb create mode 100644 lib/active_agent/dashboard/app/serializers/active_agent/dashboard/telemetry_trace_serializer.rb create mode 100644 lib/active_agent/dashboard/app/serializers/active_agent/dashboard/trace_interaction_serializer.rb create mode 100644 lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_execution_service.rb create mode 100644 lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_registrar.rb create mode 100644 lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_scorecard.rb create mode 100644 lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_toolbox.rb create mode 100644 lib/active_agent/dashboard/app/services/active_agent/dashboard/evaluation_runner_service.rb create mode 100644 lib/active_agent/dashboard/app/services/active_agent/dashboard/mcp_recording_middleware.rb create mode 100644 lib/active_agent/dashboard/app/services/active_agent/dashboard/mock_sandbox_backend.rb create mode 100644 lib/active_agent/dashboard/app/services/active_agent/dashboard/playwright_mcp_client.rb create mode 100644 lib/active_agent/dashboard/app/services/active_agent/dashboard/sandbox_orchestrator.rb create mode 100644 lib/active_agent/dashboard/app/services/active_agent/dashboard/session_recording_service.rb create mode 100644 lib/active_agent/dashboard/db/migrate/20260812000001_create_active_agent_dashboard_tables.rb diff --git a/lib/active_agent/dashboard.rb b/lib/active_agent/dashboard.rb index 4002b3bb..3f813ef7 100644 --- a/lib/active_agent/dashboard.rb +++ b/lib/active_agent/dashboard.rb @@ -104,6 +104,75 @@ class << self # @return [String] attr_accessor :base_controller_class + # Called before each run/trace-ingest to enforce host-app limits. + # Receives (owner, kind) where kind is :execution or :trace_ingest, and + # returns nil to allow or a message String to deny. Denials surface as + # HTTP 402 (execution) / 429 (ingest). + # + # Unset means unlimited, which is what a self-hosted install wants. + # @return [Proc, nil] + attr_accessor :quota_checker + + # Resolves LLM provider credentials for a run. Receives + # (owner, provider_name) and returns a Hash merged into the agent's + # generation options (e.g. { access_token: "sk-..." } or + # { host: "http://localhost:11434" }), or nil to fall back to the + # host app's config/active_agent.yml. + # + # Unset means config/active_agent.yml is the only source, which is what + # a self-hosted install wants. + # @return [Proc, nil] + attr_accessor :provider_credentials_resolver + + # Extra sandbox backends contributed by the host app, as + # { "cloud_run" => "CloudRunService" }. The engine ships :mock and + # :local (Docker); cloud backends live in the app that operates them. + # @return [Hash{String => String}] + attr_accessor :sandbox_backends + + # Whether the dashboard may execute agents against real providers. + # Disable to run the dashboard as a read-only observability surface. + # @return [Boolean] + attr_accessor :execution_enabled + + # Maps an ingested trace to the owner that its newly observed agents + # belong to. Defaults to the trace's account in multi-tenant mode and to + # nobody in single-tenant mode. A host app whose agents hang off a + # different record (the platform's hang off the account's owning user) + # supplies its own mapping. + # @return [Proc, nil] + attr_accessor :trace_owner_resolver + + # How long telemetry traces are kept before TraceRetentionJob prunes + # them. A Duration applies to every trace; a callable receives each + # owner and returns that owner's window (nil keeps everything). Unset + # means nothing is ever deleted. + # @return [ActiveSupport::Duration, Proc, nil] + attr_accessor :trace_retention + + # Whether API keys and provider credentials are encrypted at rest with + # Active Record Encryption. On by default, which requires the host app + # to have run `rails db:encryption:init`. Turning it off stores those + # secrets in plain text — a deliberate downgrade, never a default. + # @return [Boolean] + attr_accessor :encrypt_credentials + + # Value stored in polymorphic *_type columns for dashboard agents + # (agent_memories.memorable_type, agent_contexts.contextable_type). + # Unset means the class name. A host app whose existing rows were + # written under its own constant sets its name here. + # @return [String, nil] + attr_accessor :agent_polymorphic_name + + # Table name prefix for the engine's models. The engine's own + # migrations create `active_agent_*` tables, so the default matches. + # + # A host app that already owns these tables under different names + # (the activeagents.ai platform grew them unprefixed) sets this to "" + # rather than renaming production tables. + # @return [String] + attr_accessor :table_name_prefix + # Returns whether multi-tenant mode is enabled. # # @return [Boolean] @@ -111,6 +180,35 @@ def multi_tenant? @multi_tenant == true end + # Returns whether agent execution is permitted. + # + # @return [Boolean] + def execution_enabled? + @execution_enabled != false + end + + # Asks the host app whether +owner+ may perform +kind+. + # + # @return [String, nil] denial message, or nil when allowed + def quota_denial(owner, kind) + return nil if quota_checker.nil? + + quota_checker.call(owner, kind) + end + + # Provider options for +owner+, or {} when the host app has none and + # config/active_agent.yml should be used as-is. + # + # @return [Hash] + def provider_credentials(owner, provider) + return {} if provider_credentials_resolver.nil? + + provider_credentials_resolver.call(owner, provider) || {} + rescue StandardError => e + Rails.logger.warn("[ActiveAgent::Dashboard] provider credential lookup failed: #{e.message}") + {} + end + # Returns the trace model class to use. # # @return [Class] The trace model class @@ -129,6 +227,16 @@ def agent_model ActiveAgent::Dashboard::Agent end + # Returns the configured owner class: the Account in multi-tenant mode, + # the User otherwise. Nil when the host app configured neither, which + # is the single-user self-hosted case. + # + # @return [Class, nil] + def owner_class + name = multi_tenant? ? account_class : user_class + name&.safe_constantize + end + # Configures the dashboard. # # @yield [config] Configuration block @@ -152,6 +260,15 @@ def reset! @storage_service = nil @ingest_api_key = nil @base_controller_class = "ActionController::Base" # deprecated no-op + @quota_checker = nil + @provider_credentials_resolver = nil + @sandbox_backends = {} + @execution_enabled = true + @table_name_prefix = "active_agent_" + @agent_polymorphic_name = nil + @encrypt_credentials = true + @trace_retention = nil + @trace_owner_resolver = nil end end diff --git a/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/agent_execution_job.rb b/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/agent_execution_job.rb index 776d8df6..7aac0bc3 100644 --- a/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/agent_execution_job.rb +++ b/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/agent_execution_job.rb @@ -2,23 +2,21 @@ module ActiveAgent module Dashboard - # Background job for executing agent runs. - # - # Handles the actual execution of an agent with the given input, - # updating the AgentRun record with results. - # class AgentExecutionJob < ApplicationJob - queue_as :default + queue_as :agents - def perform(agent_run_id) - run = AgentRun.find(agent_run_id) - return if run.finished? + def perform(run_id) + run = AgentRun.find(run_id) + return if run.cancelled? || run.complete? run.update!(status: :running, started_at: Time.current) + run.add_log("Starting execution", level: :info) begin - agent = run.agent - result = execute_agent(agent, run.input_prompt, **(run.input_params || {})) + agent_record = run.agent + + # Build the agent class dynamically based on configuration + result = execute_agent(agent_record, run) run.update!( output: result[:output], @@ -30,6 +28,8 @@ def perform(agent_run_id) output_tokens: result.dig(:usage, :output_tokens), total_tokens: result.dig(:usage, :total_tokens) ) + run.add_log("Execution completed successfully", level: :info) + rescue => e run.update!( status: :failed, @@ -37,19 +37,17 @@ def perform(agent_run_id) error_message: e.message, error_backtrace: e.backtrace&.first(10)&.join("\n") ) + run.add_log("Execution failed: #{e.message}", level: :error) + raise + ensure + run.broadcast_update end end private - def execute_agent(agent, input_prompt, **params) - # TODO: Build and execute actual ActiveAgent::Base subclass - # For now, return mock data - { - output: "Executed: #{input_prompt}", - metadata: { provider: agent.provider, model: agent.model }, - usage: { input_tokens: 100, output_tokens: 200, total_tokens: 300 } - } + def execute_agent(agent_record, run) + AgentExecutionService.call(agent_record, run) end end end diff --git a/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_cleanup_job.rb b/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_cleanup_job.rb index 6900cc11..69bd41fd 100644 --- a/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_cleanup_job.rb +++ b/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_cleanup_job.rb @@ -2,47 +2,42 @@ module ActiveAgent module Dashboard - # Background job for cleaning up sandbox environments. - # - # Removes containers, Cloud Run jobs, or other resources - # when a sandbox session expires. - # class SandboxCleanupJob < ApplicationJob - queue_as :default + queue_as :sandboxes + # Clean up Cloud Run resources for an expired sandbox def perform(sandbox_session_id) - session = SandboxSession.find_by(id: sandbox_session_id) - return unless session + sandbox = SandboxSession.find_by(id: sandbox_session_id) + return unless sandbox - cleanup_sandbox(session) - end - - private + Rails.logger.info("Cleaning up sandbox: #{sandbox.session_id}") - def cleanup_sandbox(session) - case ActiveAgent::Dashboard.sandbox_service - when :cloud_run - cleanup_cloud_run(session) - when :kubernetes - cleanup_kubernetes(session) - else - cleanup_local(session) + # Delete Cloud Run Job if exists + if sandbox.cloud_run_job_id.present? && !Rails.env.development? + delete_cloud_run_job(sandbox.cloud_run_job_id) end - end - def cleanup_local(session) - # Local mode: Nothing to clean up - Rails.logger.info "[ActiveAgent::Dashboard] Cleaned up local sandbox: #{session.session_id}" + # Optionally delete old sandbox records + # For now, keep for analytics + sandbox.update!(cloud_run_url: nil, cloud_run_job_id: nil) end - def cleanup_cloud_run(session) - # TODO: Implement Cloud Run cleanup - Rails.logger.info "[ActiveAgent::Dashboard] Would clean up Cloud Run job: #{session.cloud_run_job_id}" + # Periodic cleanup of all expired sandboxes + def self.cleanup_expired! + SandboxSession.expired_sessions.active.find_each do |sandbox| + sandbox.expire! + end end - def cleanup_kubernetes(session) - # TODO: Implement Kubernetes cleanup - Rails.logger.info "[ActiveAgent::Dashboard] Would clean up Kubernetes pod: #{session.cloud_run_job_id}" + private + + def delete_cloud_run_job(job_id) + require "google/cloud/run/v2" + + client = Google::Cloud::Run::V2::Jobs::Client.new + client.delete_job(name: job_id) + rescue => e + Rails.logger.warn("Failed to delete Cloud Run job #{job_id}: #{e.message}") end end end diff --git a/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_provision_job.rb b/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_provision_job.rb index e311a093..c5bcd044 100644 --- a/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_provision_job.rb +++ b/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_provision_job.rb @@ -2,63 +2,56 @@ module ActiveAgent module Dashboard - # Background job for provisioning sandbox environments. - # - # Creates isolated execution environments (Docker, Cloud Run, etc.) - # for running agents with tools. - # class SandboxProvisionJob < ApplicationJob - queue_as :default + queue_as :sandboxes + # Provision a Cloud Run sandbox for the session + # Each sandbox is an instance of the ActiveAgents application running in sandbox mode def perform(sandbox_session_id) - session = SandboxSession.find(sandbox_session_id) - return unless session.provisioning? + sandbox = SandboxSession.find(sandbox_session_id) + return if sandbox.ready? || sandbox.expired? - begin - result = provision_sandbox(session) - - session.mark_ready!( - sandbox_url: result[:url], - sandbox_job_id: result[:job_id] - ) - rescue => e - session.update!( - status: :failed, - error_message: e.message - ) + # In development/test, simulate provisioning + if Rails.env.development? || Rails.env.test? + simulate_provisioning(sandbox) + return end + + # Hand off to whichever backend this install registered — the engine + # ships only the in-memory one, so a real container/job comes from the + # host app's backend (see ActiveAgent::Dashboard.sandbox_backends). + result = SandboxOrchestrator.new.create_sandbox(sandbox) + + sandbox.mark_ready!( + cloud_run_url: result[:url], + cloud_run_job_id: result[:sandbox_id] + ) + + # Broadcast status update + broadcast_sandbox_update(sandbox) + rescue StandardError => e + Rails.logger.error("Sandbox provision failed: #{e.message}") + sandbox.update!(status: :failed) + broadcast_sandbox_update(sandbox) end private - def provision_sandbox(session) - case ActiveAgent::Dashboard.sandbox_service - when :cloud_run - provision_cloud_run(session) - when :kubernetes - provision_kubernetes(session) - else - provision_local(session) - end - end - - def provision_local(session) - # Local mode: No actual provisioning needed - # The sandbox runs in the same process or via Docker - { - url: "http://localhost:#{3000 + session.id}", - job_id: "local-#{session.session_id}" - } - end + def simulate_provisioning(sandbox) + # Simulate a small delay for provisioning + sleep(0.5) - def provision_cloud_run(session) - # TODO: Implement Cloud Run provisioning - raise NotImplementedError, "Cloud Run provisioning not yet implemented in engine" + sandbox.mark_ready!( + cloud_run_url: "http://localhost:3000/api/sandbox", + cloud_run_job_id: "local-#{sandbox.session_id[0..7]}" + ) end - def provision_kubernetes(session) - # TODO: Implement Kubernetes provisioning - raise NotImplementedError, "Kubernetes provisioning not yet implemented in engine" + def broadcast_sandbox_update(sandbox) + ActionCable.server.broadcast( + "sandbox_#{sandbox.session_id}", + { type: "status_update", sandbox: sandbox.summary } + ) end end end diff --git a/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_run_job.rb b/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_run_job.rb new file mode 100644 index 00000000..ac567cc6 --- /dev/null +++ b/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_run_job.rb @@ -0,0 +1,287 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + class SandboxRunJob < ApplicationJob + queue_as :sandboxes + + PROVIDER_MODELS = { + "anthropic" => "claude-haiku-4-5", + "openai" => "gpt-4o", + "ollama" => "llama3.1:8b" + }.freeze + + # Execute a task in the sandbox using ActiveAgent's generate_later + def perform(sandbox_session_id, run_id, task, provider = "anthropic") + sandbox = SandboxSession.find(sandbox_session_id) + return unless sandbox.can_run? + + started_at = Time.current + + # Broadcast that run has started + broadcast_run_started(sandbox, run_id, task, provider) + + begin + # Use ActiveAgent with generate_later for async processing + # The agent's after_generation callback will handle recording results + if defined?(ActiveAgent::Base) + execute_with_active_agent(sandbox, run_id, task, provider, started_at) + else + # Fallback to direct API calls if ActiveAgent not available + execute_with_fallback(sandbox, run_id, task, provider, started_at) + end + rescue => e + Rails.logger.error("Sandbox run failed: #{e.message}") + sandbox.update!(status: :ready, error_message: e.message) + broadcast_run_error(sandbox, run_id, provider, e.message) + end + end + + private + + def execute_with_active_agent(sandbox, run_id, task, provider, started_at) + # Use generate_now here since we're already in a background job + # The ActiveAgent will handle the API call and callbacks + response = SandboxDemoAgent.with( + task: task, + provider: provider, + sandbox_session_id: sandbox.id, + run_id: run_id, + started_at: started_at + ).ask.generate_now + + duration_ms = ((Time.current - started_at) * 1000).to_i + + # Extract result from response + content = response&.message&.content || "No response generated" + input_tokens = response&.usage&.[](:input_tokens) || 0 + output_tokens = response&.usage&.[](:output_tokens) || 0 + tokens = input_tokens + output_tokens + + # Record the run + run = sandbox.record_run!( + task: task, + result: content, + duration_ms: duration_ms, + tokens: tokens, + screenshots: [], + provider: provider + ) + + sandbox.update!(status: :ready) + broadcast_run_complete(sandbox, run_id, run) + + rescue => e + Rails.logger.error("ActiveAgent execution failed: #{e.message}") + # Fallback to direct API + execute_with_fallback(sandbox, run_id, task, provider, started_at) + end + + def execute_with_fallback(sandbox, run_id, task, provider, started_at) + result = case provider + when "anthropic" + execute_with_anthropic(task) + when "openai" + execute_with_openai(task) + when "ollama" + execute_with_ollama(task) + else + { content: "Error: Unknown provider '#{provider}'", tokens: 0, screenshots: [] } + end + + duration_ms = ((Time.current - started_at) * 1000).to_i + + # Record the run + run = sandbox.record_run!( + task: task, + result: result[:content], + duration_ms: duration_ms, + tokens: result[:tokens] || 0, + screenshots: result[:screenshots] || [], + provider: provider + ) + + sandbox.update!(status: :ready) + broadcast_run_complete(sandbox, run_id, run) + end + + def execute_with_anthropic(task) + api_key = Rails.application.credentials.dig(:anthropic, :api_key) || ENV["ANTHROPIC_API_KEY"] + + unless api_key.present? + Rails.logger.warn("Anthropic API key not configured") + return { content: "Error: Anthropic API key is not configured — set ANTHROPIC_API_KEY or add it to credentials", tokens: 0, screenshots: [] } + end + + require "net/http" + require "json" + + uri = URI("https://api.anthropic.com/v1/messages") + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.read_timeout = 60 + + request = Net::HTTP::Post.new(uri) + request["Content-Type"] = "application/json" + request["x-api-key"] = api_key + request["anthropic-version"] = "2023-06-01" + + request.body = { + model: PROVIDER_MODELS["anthropic"], + max_tokens: 1024, + system: default_system_prompt, + messages: [ { role: "user", content: task } ] + }.to_json + + response = http.request(request) + result = JSON.parse(response.body) + + if result["error"] + raise "Anthropic API error: #{result['error']['message']}" + end + + content = result.dig("content", 0, "text") || "No response generated" + input_tokens = result.dig("usage", "input_tokens") || 0 + output_tokens = result.dig("usage", "output_tokens") || 0 + + { content: content, tokens: input_tokens + output_tokens, screenshots: [] } + rescue => e + Rails.logger.error("Anthropic API call failed: #{e.message}") + { content: "Error: #{e.message}", tokens: 0, screenshots: [] } + end + + def execute_with_openai(task) + api_key = Rails.application.credentials.dig(:openai, :api_key) || ENV["OPENAI_API_KEY"] + + unless api_key.present? + Rails.logger.warn("OpenAI API key not configured") + return { content: "Error: OpenAI API key is not configured — set OPENAI_API_KEY or add it to credentials", tokens: 0, screenshots: [] } + end + + require "net/http" + require "json" + + uri = URI("https://api.openai.com/v1/chat/completions") + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.read_timeout = 60 + + request = Net::HTTP::Post.new(uri) + request["Content-Type"] = "application/json" + request["Authorization"] = "Bearer #{api_key}" + + request.body = { + model: PROVIDER_MODELS["openai"], + max_tokens: 1024, + messages: [ + { role: "system", content: default_system_prompt }, + { role: "user", content: task } + ] + }.to_json + + response = http.request(request) + result = JSON.parse(response.body) + + if result["error"] + raise "OpenAI API error: #{result['error']['message']}" + end + + content = result.dig("choices", 0, "message", "content") || "No response generated" + prompt_tokens = result.dig("usage", "prompt_tokens") || 0 + completion_tokens = result.dig("usage", "completion_tokens") || 0 + + { content: content, tokens: prompt_tokens + completion_tokens, screenshots: [] } + rescue => e + Rails.logger.error("OpenAI API call failed: #{e.message}") + { content: "Error: #{e.message}", tokens: 0, screenshots: [] } + end + + def execute_with_ollama(task) + ollama_url = Rails.application.credentials.dig(:ollama, :url) || ENV["OLLAMA_URL"] || "http://localhost:11434" + + require "net/http" + require "json" + + uri = URI("#{ollama_url}/api/generate") + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = uri.scheme == "https" + http.read_timeout = 120 # Ollama can be slower + + request = Net::HTTP::Post.new(uri) + request["Content-Type"] = "application/json" + + request.body = { + model: PROVIDER_MODELS["ollama"], + prompt: "#{default_system_prompt}\n\nUser: #{task}\n\nAssistant:", + stream: false + }.to_json + + response = http.request(request) + result = JSON.parse(response.body) + + if result["error"] + raise "Ollama API error: #{result['error']}" + end + + content = result["response"] || "No response generated" + # Ollama doesn't provide token counts in the same way + tokens = (result["prompt_eval_count"] || 0) + (result["eval_count"] || 0) + + { content: content, tokens: tokens, screenshots: [] } + rescue => e + Rails.logger.error("Ollama API call failed: #{e.message}") + { content: "Error: #{e.message}\n\nMake sure Ollama is running locally with: ollama serve", tokens: 0, screenshots: [] } + end + + def default_system_prompt + <<~PROMPT + You are a helpful AI assistant. Answer the user's question or complete their task concisely and accurately. + + If the task involves browser automation, describe what actions you would take step by step. + PROMPT + end + + def broadcast_run_started(sandbox, run_id, task, provider) + ActionCable.server.broadcast( + "sandbox_#{sandbox.session_id}", + { + type: "run_started", + run_id: run_id, + provider: provider, + task: task, + started_at: Time.current.iso8601 + } + ) + Rails.logger.info "[SandboxRunJob] Broadcast run_started for #{provider} (#{run_id})" + end + + def broadcast_run_complete(sandbox, run_id, run) + ActionCable.server.broadcast( + "sandbox_#{sandbox.session_id}", + { + type: "run_complete", + run_id: run_id, + provider: run[:provider], + run: run, + sandbox: sandbox.summary + } + ) + Rails.logger.info "[SandboxRunJob] Broadcast run_complete for #{run[:provider]} (#{run_id})" + end + + def broadcast_run_error(sandbox, run_id, provider, error) + ActionCable.server.broadcast( + "sandbox_#{sandbox.session_id}", + { + type: "run_error", + run_id: run_id, + provider: provider, + error: error, + sandbox: sandbox.summary + } + ) + Rails.logger.info "[SandboxRunJob] Broadcast run_error for #{provider} (#{run_id})" + end + end + end +end diff --git a/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/trace_retention_job.rb b/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/trace_retention_job.rb new file mode 100644 index 00000000..d89eb2d1 --- /dev/null +++ b/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/trace_retention_job.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Prunes telemetry traces past the configured retention window. + # + # NOT SCHEDULED BY DEFAULT: deleting trace history is a decision for the + # app that owns the data. To enable it, set a window and schedule the job: + # + # ActiveAgent::Dashboard.trace_retention = 30.days + # + # # config/recurring.yml + # trace_retention: + # class: ActiveAgent::Dashboard::TraceRetentionJob + # schedule: every day at 4am + # + # A host app with per-tenant retention rules (the activeagents.ai platform + # prunes per plan) passes a callable instead, receiving each owner and + # returning that owner's window. + class TraceRetentionJob < ApplicationJob + queue_as :default + + BATCH_SIZE = 5_000 + + def perform + window = ActiveAgent::Dashboard.trace_retention + return if window.nil? + + if ActiveAgent::Dashboard.multi_tenant? && (owners = ActiveAgent::Dashboard.owner_class) + owners.find_each { |owner| prune(traces.for_account(owner), window_for(window, owner)) } + else + prune(traces.all, window_for(window, nil)) + end + end + + private + + def traces + ActiveAgent::Dashboard.trace_model + end + + def window_for(window, owner) + window.respond_to?(:call) ? window.call(owner) : window + end + + # Deletes in batches so a long-neglected install doesn't build one + # enormous statement. + def prune(scope, window) + return if window.nil? + + cutoff = window.ago + loop do + deleted = scope.where(timestamp: ...cutoff).limit(BATCH_SIZE).delete_all + break if deleted < BATCH_SIZE + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb index 2fffcc22..098a6a1f 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb @@ -2,47 +2,40 @@ module ActiveAgent module Dashboard - # Represents an AI agent configuration. - # - # Agents are the core entity in the dashboard, storing all configuration - # needed to execute AI interactions including provider settings, instructions, - # tools, and appearance. - # - # Supports both local (single-user) and multi-tenant (account-scoped) modes. - # - # @example Creating an agent - # agent = ActiveAgent::Dashboard::Agent.create!( - # name: "Code Assistant", - # provider: "openai", - # model: "gpt-4o" - # ) - # - # @example Executing an agent - # run = agent.execute("Explain this code", code: "def foo; end") - # class Agent < ApplicationRecord - # Associations - owner is optional to support both modes - belongs_to :user, class_name: ActiveAgent::Dashboard.user_class, optional: true if ActiveAgent::Dashboard.user_class - belongs_to :account, class_name: ActiveAgent::Dashboard.account_class, optional: true if ActiveAgent::Dashboard.multi_tenant? - - has_many :agent_versions, class_name: "ActiveAgent::Dashboard::AgentVersion", dependent: :destroy - has_many :agent_runs, class_name: "ActiveAgent::Dashboard::AgentRun", dependent: :destroy + include Ownable + + has_many :agent_versions, dependent: :destroy + has_many :agent_runs, dependent: :destroy + has_many :evaluations, dependent: :destroy + has_many :agent_memories, as: :memorable, dependent: :destroy + + # Polymorphic rows (agent_memories, agent_contexts) store this string. + # A host app that grew these tables under its own Agent constant keeps + # its existing rows readable by setting agent_polymorphic_name. + def self.polymorphic_name + ActiveAgent::Dashboard.agent_polymorphic_name || super + end # Validations validates :name, presence: true, length: { minimum: 2, maximum: 100 } validates :slug, presence: true, format: { with: /\A[a-z0-9\-_]+\z/ } + # Slugs are unique per owner. Which column that means depends on the + # configured mode, so it is resolved at validation time rather than + # baked into a uniqueness scope when the class loads. + validate :slug_unique_within_owner validates :provider, presence: true validates :model, presence: true - - # Ensure slug uniqueness within scope - if ActiveAgent::Dashboard.multi_tenant? - validates :slug, uniqueness: { scope: :account_id } - else - validates :slug, uniqueness: { scope: :user_id } - end + validate :validate_action_prompts # Status enum - enum :status, { draft: 0, active: 1, archived: 2 } + # `observed` agents were discovered from reported telemetry rather than + # authored here. They can't be executed by the platform — we can't push + # instructions into someone else's app — so they're read-only until forked. + enum :status, { draft: 0, active: 1, archived: 2, observed: 3 } + + scope :observed_agents, -> { where(status: :observed) } + scope :authored, -> { where.not(status: :observed) } # Callbacks before_validation :generate_slug, on: :create @@ -52,7 +45,16 @@ class Agent < ApplicationRecord # Scopes scope :active_agents, -> { where(status: :active) } scope :by_provider, ->(provider) { where(provider: provider) } - scope :with_tool, ->(tool) { where("tools @> ?", [ tool ].to_json) } + # jsonb containment on PostgreSQL; a substring match on the serialized + # array everywhere else. The fallback can over-match a tool whose name + # is a prefix of another, so the JSON quoting is kept in the pattern. + scope :with_tool, ->(tool) { + if postgres? + where("tools @> ?", [ tool ].to_json) + else + where("tools LIKE ?", "%\"#{tool}\"%") + end + } # Available presets matching AgentAvatar component PRESET_TYPES = %w[ @@ -67,11 +69,45 @@ class Agent < ApplicationRecord # Available tools/MCPs AVAILABLE_TOOLS = %w[ - terminal playwright filesystem code database slack fetch search edit translate memory + terminal playwright filesystem code database slack fetch search edit translate memory agents ].freeze # Available providers - PROVIDERS = %w[openai anthropic ollama openrouter requesty].freeze + PROVIDERS = %w[openai anthropic ollama openrouter].freeze + + # The ActiveAgent class name this agent's runs are recorded under — the + # correlation key between platform Agent records and telemetry traces + # (TelemetryTrace#agent_class) and solid_agent contexts. + def telemetry_agent_class + base = agent_class_name.presence || name.parameterize(separator: "_").camelize + base.end_with?("Agent") ? base : "#{base}Agent" + end + + # The agent's long-term memory (solid_agent HasMemory contract) — the + # summary list its runs read/write via the memory tools. + def memory + AgentMemory.for(self) + end + + # The default action every agent has; uses the base instructions alone. + DEFAULT_ACTION = "ask" + + # All invokable action names: the default plus each named action prompt. + def available_actions + [ DEFAULT_ACTION ] + Array(action_prompts).filter_map { |ap| ap["name"].presence } + end + + def action_prompt_for(action_name) + Array(action_prompts).find { |ap| ap["name"] == action_name.to_s } + end + + # The system instructions an action executes under: named actions stack + # their prompt below the agent's base instructions; the default action + # uses the base instructions alone. + def composed_instructions_for(action_name) + action = action_prompt_for(action_name) + [ instructions, action&.dig("prompt") ].map(&:presence).compact.join("\n\n").presence + end # Returns the configuration as a hash for versioning def configuration_snapshot @@ -81,6 +117,7 @@ def configuration_snapshot provider: provider, model: model, instructions: instructions, + action_prompts: action_prompts, preset_type: preset_type, appearance: appearance, instruction_sets: instruction_sets, @@ -96,6 +133,7 @@ def restore_from_version!(version) config = version.configuration_snapshot update!( instructions: config["instructions"], + action_prompts: config["action_prompts"] || [], preset_type: config["preset_type"], appearance: config["appearance"], instruction_sets: config["instruction_sets"], @@ -111,6 +149,30 @@ def latest_version agent_versions.order(version_number: :desc).first end + # Maps each historical instructions digest to the first version that + # introduced it ("v3"), so run cohorts can label instruction changes with + # real agent versions instead of raw hashes. + def instructions_digest_versions + agent_versions.order(:version_number).each_with_object({}) do |version, map| + snapshot = version.configuration_snapshot + base = snapshot["instructions"] + label = "v#{version.version_number}" + + if base.present? + map[Digest::SHA256.hexdigest(base).first(8)] ||= label + end + + # Named actions run under composed instructions (base + action + # prompt), so their runs carry a different digest per action. + Array(snapshot["action_prompts"]).each do |action| + composed = [ base, action["prompt"] ].map(&:presence).compact.join("\n\n") + next if composed.blank? + + map[Digest::SHA256.hexdigest(composed).first(8)] ||= label + end + end + end + # Get version count def version_count agent_versions.count @@ -130,24 +192,26 @@ def perform end # Execute a run with this agent - def execute(input_prompt, **params) + def execute(input_prompt, action: nil, **params) run = agent_runs.create!( input_prompt: input_prompt, + action_name: normalized_action(action), input_params: params, status: :pending, trace_id: SecureRandom.uuid ) # Queue the execution job - ActiveAgent::Dashboard::AgentExecutionJob.perform_later(run.id) + AgentExecutionJob.perform_later(run.id) run end # Quick test execution (synchronous) - def test_execute(input_prompt, **params) + def test_execute(input_prompt, action: nil, **params) run = agent_runs.create!( input_prompt: input_prompt, + action_name: normalized_action(action), input_params: params, status: :running, trace_id: SecureRandom.uuid, @@ -155,7 +219,8 @@ def test_execute(input_prompt, **params) ) begin - result = build_and_execute_agent(input_prompt, **params) + # Build and execute the agent + result = AgentExecutionService.call(self, run) run.update!( output: result[:output], @@ -181,23 +246,24 @@ def test_execute(input_prompt, **params) private + def slug_unique_within_owner + return if slug.blank? + + siblings = self.class.for_owner(owner) + siblings = siblings.where.not(id: id) if persisted? + errors.add(:slug, "has already been taken") if siblings.exists?(slug: slug) + end + def generate_slug return if slug.present? base_slug = name.to_s.parameterize self.slug = base_slug - # Ensure uniqueness within scope + # Ensure uniqueness counter = 1 - scope = self.class.where(slug: slug) - scope = scope.where(account_id: account_id) if respond_to?(:account_id) && account_id - scope = scope.where(user_id: user_id) if respond_to?(:user_id) && user_id - - while scope.exists? + while self.class.for_owner(owner).exists?(slug: slug) self.slug = "#{base_slug}-#{counter}" - scope = self.class.where(slug: slug) - scope = scope.where(account_id: account_id) if respond_to?(:account_id) && account_id - scope = scope.where(user_id: user_id) if respond_to?(:user_id) && user_id counter += 1 end end @@ -210,17 +276,18 @@ def create_initial_version ) end + VERSIONED_FIELDS = %w[ + instructions action_prompts preset_type appearance instruction_sets + tools mcp_servers model_config response_format + ].freeze + def configuration_changed? - saved_changes.keys.any? do |key| - %w[instructions preset_type appearance instruction_sets tools mcp_servers model_config response_format].include?(key) - end + saved_changes.keys.any? { |key| VERSIONED_FIELDS.include?(key) } end def create_version_on_config_change next_version = (latest_version&.version_number || 0) + 1 - changed_fields = saved_changes.keys.select do |key| - %w[instructions preset_type appearance instruction_sets tools mcp_servers model_config response_format].include?(key) - end + changed_fields = saved_changes.keys.select { |key| VERSIONED_FIELDS.include?(key) } agent_versions.create!( version_number: next_version, @@ -229,6 +296,33 @@ def create_version_on_config_change ) end + # Unknown action names fall back to the default rather than failing the + # run — an action can be renamed between enqueue and execution. + def normalized_action(action) + action = action.to_s.presence + action && available_actions.include?(action) ? action : nil + end + + def validate_action_prompts + return if action_prompts.blank? + + unless action_prompts.is_a?(Array) && action_prompts.all? { |ap| ap.is_a?(Hash) } + errors.add(:action_prompts, "must be a list of action definitions") + return + end + + names = action_prompts.map { |ap| ap["name"].to_s } + names.each do |action_name| + unless action_name.match?(/\A[a-z][a-z0-9_]*\z/) + errors.add(:action_prompts, "action name '#{action_name}' must be snake_case") + end + if action_name == DEFAULT_ACTION + errors.add(:action_prompts, "'#{DEFAULT_ACTION}' is the built-in default action") + end + end + errors.add(:action_prompts, "action names must be unique") if names.uniq.size != names.size + end + def model_config_code return "" if model_config.blank? @@ -241,16 +335,6 @@ def instructions_code "\n prompt instructions: <<~INSTRUCTIONS\n #{instructions.gsub("\n", "\n ")}\n INSTRUCTIONS" end - - def build_and_execute_agent(input_prompt, **params) - # TODO: Implement actual ActiveAgent execution - # This will create a dynamic agent class and execute it - { - output: "Mock response for: #{input_prompt}", - metadata: { provider: provider, model: model }, - usage: { input_tokens: 10, output_tokens: 20, total_tokens: 30 } - } - end end end end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_context.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_context.rb new file mode 100644 index 00000000..674fc8f3 --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_context.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Conversation context persisted by SolidAgent::HasContext — one row per + # (contextable, agent, action). On this platform the contextable is the + # dashboard Agent record, so a context is that agent's interaction stream. + # + # Based on the solid_agent install generator's AgentContext, extended with + # record_generation_with_provenance! (the concern's duck-typed extension + # point) so each generation carries the telemetry trace_id and provenance. + class AgentContext < ApplicationRecord + belongs_to :contextable, polymorphic: true, optional: true + has_many :messages, class_name: "AgentMessage", dependent: :destroy + has_many :generations, class_name: "AgentGeneration", dependent: :destroy + + validates :agent_name, presence: true + validates :action_name, presence: true + + scope :recent, -> { order(updated_at: :desc) } + scope :for_agent, ->(name) { where(agent_name: name) } + scope :for_action, ->(name) { where(action_name: name) } + scope :with_trace, ->(trace_id) { where(trace_id: trace_id) } + scope :for_agents, ->(agents) { where(contextable_type: Agent.polymorphic_name, contextable_id: agents.select(:id)) } + + # Records a generation response and updates token counts. + # + # Defensive against response objects that don't expose provider/duration + # (activeagent 1.0.3 responses don't) — solid_agent's stock template would + # raise (and silently drop the generation) on those. + def record_generation!(response, extra_attributes = {}) + usage = response.respond_to?(:usage) ? response.usage : nil + + generation = generations.create!({ + content: response.message&.content, + model: value_if_responds(response, :model), + provider: value_if_responds(response, :provider), + finish_reason: value_if_responds(response, :finish_reason), + input_tokens: usage&.input_tokens || 0, + output_tokens: usage&.output_tokens || 0, + cached_tokens: value_if_responds(usage, :cached_tokens) || 0, + reasoning_tokens: value_if_responds(usage, :reasoning_tokens) || 0, + tool_calls: extract_tool_calls(response), + raw_response: value_if_responds(response, :raw_response), + duration_seconds: extract_duration_seconds(response, usage) + }.merge(extra_attributes)) + + increment!(:total_input_tokens, generation.input_tokens) + increment!(:total_output_tokens, generation.output_tokens) + + add_assistant_message(response.message&.content, metadata: { "tool_calls" => generation.tool_calls }) + + generation + end + + # Called by SolidAgent::HasContext when defined: persists the generation + # together with its provenance hash and the telemetry trace_id, so + # conversation records join to active_agent_telemetry_traces. + def record_generation_with_provenance!(response, provenance) + provenance = (provenance || {}).deep_stringify_keys + + record_generation!( + response, + trace_id: provenance["trace_id"], + provenance: provenance + ) + end + + def add_user_message(content, **attributes) + messages.create!(role: "user", content: content, **attributes) + end + + def add_assistant_message(content, **attributes) + messages.create!(role: "assistant", content: content, **attributes) + end + + def add_system_message(content) + messages.create!(role: "system", content: content) + end + + def add_tool_message(tool_call_id:, tool_name:, result:, arguments: nil, duration_ms: nil) + messages.create!( + role: "tool", + tool_call_id: tool_call_id, + tool_name: tool_name, + tool_result: result, + tool_arguments: arguments.presence || {}, + metadata: duration_ms ? { "duration_ms" => duration_ms } : {}, + content: result.is_a?(String) ? result : result.to_json + ) + end + + def total_tokens + total_input_tokens + total_output_tokens + end + + def input_params + options&.dig("input_params") || {} + end + + private + + def value_if_responds(response, method) + response.respond_to?(method) ? response.public_send(method) : nil + end + + def extract_duration_seconds(response, usage) + return response.duration if response.respond_to?(:duration) && response.duration + + duration_ms = usage&.respond_to?(:duration_ms) ? usage.duration_ms : nil + duration_ms ? duration_ms / 1000.0 : nil + end + + def extract_tool_calls(response) + message = response.message + return [] unless message.respond_to?(:tool_calls) + + tool_calls = message.tool_calls + return [] if tool_calls.blank? + + tool_calls.map do |tc| + { + id: value_if_responds(tc, :id), + name: value_if_responds(tc, :name), + arguments: value_if_responds(tc, :arguments) + } + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_generation.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_generation.rb new file mode 100644 index 00000000..250308a3 --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_generation.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # One LLM generation call within an agent conversation context. From the + # solid_agent install generator, extended with trace_id (joins to + # active_agent_telemetry_traces) and the SolidAgent provenance hash. + class AgentGeneration < ApplicationRecord + belongs_to :agent_context + + scope :recent, -> { order(created_at: :desc) } + scope :by_model, ->(model) { where(model: model) } + scope :with_tool_calls, -> { where.not(tool_calls: []) } + scope :with_trace, ->(trace_id) { where(trace_id: trace_id) } + + def total_tokens + input_tokens + output_tokens + end + + def has_tool_calls? + tool_calls.present? && tool_calls.any? + end + + def completed? + finish_reason == "stop" + end + + def truncated? + finish_reason == "length" + end + + # Provider prompt-cache hit on this generation? + def cache_hit? + cached_tokens.to_i.positive? + end + + # Extended thinking captured? + def thinking? + reasoning_tokens.to_i.positive? + end + + # The telemetry trace this generation belongs to, when recorded. + def telemetry_trace + return nil if trace_id.blank? + + ActiveAgent::Dashboard.trace_model.find_by(trace_id: trace_id) + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_memory.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_memory.rb new file mode 100644 index 00000000..ecd91956 --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_memory.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Agent-curated long-term memory for a subject record (solid_agent + # HasMemory contract). On this platform the memorable is usually the + # dashboard Agent record; agents write/read it through the save_memory / + # recall_memory tools during generation runs. + # + # Memory is scoped to (memorable, scope) — not to an agent class — so any + # agent operating on the same subject shares it, making it a handoff + # channel between agents. Entry source_agent records who wrote each note. + class AgentMemory < ApplicationRecord + DEFAULT_SCOPE = "default" + + belongs_to :memorable, polymorphic: true, optional: true + has_many :entries, class_name: "AgentMemoryEntry", dependent: :destroy + + validates :scope, presence: true + + def self.for(memorable, scope: DEFAULT_SCOPE) + find_or_create_by!(memorable: memorable, scope: scope.to_s) + end + + def remember(content, source_agent: nil, category: nil) + entries.create!(content: content, source_agent: source_agent, category: category) + end + + def recall(limit: 20, category: nil) + scope = entries.order(created_at: :desc) + scope = scope.where(category: category) if category.present? + scope.limit(limit || 20).to_a + end + + def summary_list + entries.order(:created_at).pluck(:content) + end + + # Formatted block suitable for injecting into another agent's + # instructions when handing a subject off. + def to_prompt + notes = entries.order(:created_at).map do |entry| + source = entry.source_agent.present? ? " (#{entry.source_agent})" : "" + "- #{entry.content}#{source}" + end + return "" if notes.empty? + + "Memory notes for this subject:\n#{notes.join("\n")}" + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_memory_entry.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_memory_entry.rb new file mode 100644 index 00000000..5876d41d --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_memory_entry.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # One agent-authored summary note in an AgentMemory. + class AgentMemoryEntry < ApplicationRecord + belongs_to :agent_memory + + validates :content, presence: true + + scope :chronological, -> { order(:created_at) } + scope :by_category, ->(category) { where(category: category) } + scope :from_agent, ->(agent_name) { where(source_agent: agent_name) } + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_message.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_message.rb new file mode 100644 index 00000000..16d86c40 --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_message.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # A single message in an agent conversation context (user, assistant, + # system, or tool). From the solid_agent install generator; the provenance + # and content_checksum columns are populated automatically by + # SolidAgent::HasContext. + class AgentMessage < ApplicationRecord + belongs_to :agent_context + + validates :role, presence: true, inclusion: { in: %w[user assistant system tool] } + + scope :by_role, ->(role) { where(role: role) } + scope :user_messages, -> { by_role("user") } + scope :assistant_messages, -> { by_role("assistant") } + scope :chronological, -> { order(created_at: :asc) } + + # Converts the message to a hash format for ActiveAgent prompts + def to_message_hash + hash = { role: role, content: content } + hash[:tool_calls] = tool_calls_data if role == "assistant" && tool_calls_data.present? + + if role == "tool" + hash[:tool_call_id] = tool_call_id + hash[:name] = tool_name + end + + hash + end + + def tool_calls_data + metadata&.dig("tool_calls") || [] + end + + def tool_call? + role == "assistant" && tool_calls_data.present? + end + + def tool_result? + role == "tool" + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_run.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_run.rb index 7960414a..f31f0359 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_run.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_run.rb @@ -2,22 +2,8 @@ module ActiveAgent module Dashboard - # Tracks individual agent execution runs. - # - # Each run captures input, output, timing, token usage, and any errors - # that occurred during execution. - # - # @example Creating a run - # run = agent.execute("Analyze this code", code: code) - # run.status # => "pending" - # - # @example Monitoring a run - # run.in_progress? # => true - # run.finished? # => false - # class AgentRun < ApplicationRecord - belongs_to :agent, class_name: "ActiveAgent::Dashboard::Agent" - has_one :session_recording, class_name: "ActiveAgent::Dashboard::SessionRecording", dependent: :nullify + belongs_to :agent # Status enum enum :status, { pending: 0, running: 1, complete: 2, failed: 3, cancelled: 4 } @@ -46,6 +32,60 @@ def add_log(message, level: :info) update!(logs: new_logs) end + # Appends a progress event to logs mid-run so pollers can stream what the + # agent is doing (pending llm/tool/agent calls). Events pair up by eid: + # a "started" event is pending until a "done"/"error" with the same eid + # lands. update_column: no validations/callbacks, safe from the run's own + # execution thread; reads current DB state so add_log interleaves safely. + def append_event(eid:, kind:, label:, status: "done", detail: nil, duration_ms: nil) + event = { + "at" => Time.current.iso8601(3), + "eid" => eid, + "kind" => kind.to_s, + "label" => label.to_s, + "status" => status.to_s + } + event["detail"] = detail.to_s.byteslice(0, 1200).to_s.scrub if detail + event["duration_ms"] = duration_ms if duration_ms + current = self.class.where(id: id).pick(:logs) || [] + update_column(:logs, current + [ event ]) + event + end + + # Stable short fingerprint of the instructions this run executed under — + # the grouping key (with model) for configuration cohorts when comparing + # instruction/model changes. + def instructions_digest + instructions = output_metadata&.dig("instructions") + return nil if instructions.blank? + + Digest::SHA256.hexdigest(instructions).first(8) + end + + # Deterministic memorable name for the digest ("calm-heron") — reads far + # better than hex when comparing cohorts, and is stable across runs and + # deployments because it's derived from the digest alone. + CODENAME_ADJECTIVES = %w[ + calm brisk quiet bold amber coral dusky fresh golden keen + lively mellow nimble pale rustic silver tidal vivid wry zesty + arid breezy crisp dapper eager foggy hazy icy jolly lunar + misty polar + ].freeze + CODENAME_NOUNS = %w[ + heron otter falcon cedar willow harbor mesa ridge grove delta + prairie summit canyon reef atoll fjord tundra oasis lagoon dune + glacier meadow bluff cove marsh basin knoll strait quarry vale + hollow crag + ].freeze + + def instructions_codename + digest = instructions_digest + return nil unless digest + + value = digest.to_i(16) + "#{CODENAME_ADJECTIVES[value % 32]}-#{CODENAME_NOUNS[(value / 32) % 32]}" + end + # Calculate duration if not set def calculated_duration_ms return duration_ms if duration_ms.present? @@ -73,6 +113,12 @@ def summary output_preview: output&.truncate(200), duration_ms: calculated_duration_ms, tokens: total_tokens, + provider: output_metadata&.dig("provider"), + model: output_metadata&.dig("model"), + action_name: action_name || output_metadata&.dig("action") || "ask", + instructions_digest: instructions_digest, + instructions_codename: instructions_codename, + instructions_preview: output_metadata&.dig("instructions")&.truncate(120), created_at: created_at, error: error_message } @@ -80,15 +126,9 @@ def summary # Stream output updates via ActionCable def broadcast_update - return unless defined?(ActionCable) - - ActionCable.server.broadcast( - "agent_run_#{id}", - { - type: "update", - run: summary - } - ) + payload = { type: "update", run: summary } + ActionCable.server.broadcast("agent_run_#{id}", payload) + ActionCable.server.broadcast("agent_runs_#{agent_id}", payload) end # Cancel a running execution diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_template.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_template.rb index d5e690be..1c2b1b1e 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_template.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_template.rb @@ -2,15 +2,6 @@ module ActiveAgent module Dashboard - # Pre-built agent templates for quick agent creation. - # - # Templates provide starting configurations for common use cases like - # code assistance, research, writing, and browser automation. - # - # @example Creating an agent from a template - # template = ActiveAgent::Dashboard::AgentTemplate.find_by(slug: "code-assistant") - # agent = template.create_agent_for(user) - # class AgentTemplate < ApplicationRecord # Validations validates :name, presence: true @@ -34,11 +25,9 @@ class AgentTemplate < ApplicationRecord automation ].freeze - # Create an agent from this template for a user/account - def create_agent_for(owner, name: nil) - agent_class = ActiveAgent::Dashboard::Agent - - agent = agent_class.new( + # Create an agent from this template for a user + def create_agent_for(user, name: nil) + agent = user.agents.build( name: name || self.name, description: description, provider: provider, @@ -53,13 +42,6 @@ def create_agent_for(owner, name: nil) status: :draft ) - # Set owner based on mode - if ActiveAgent::Dashboard.multi_tenant? && owner.respond_to?(:id) - agent.account = owner - elsif owner.respond_to?(:id) - agent.user = owner if agent.respond_to?(:user=) - end - if agent.save increment!(:usage_count) end @@ -84,8 +66,7 @@ def self.seed_defaults! model_config: { temperature: 0.3 }, instructions: "You are a senior software engineer with expertise in multiple programming languages. Help users with:\n- Code explanations and reviews\n- Debugging issues\n- Suggesting best practices\n- Writing tests\n\nAlways explain your reasoning and provide examples when helpful.", icon: "💻", - featured: true, - free_tier: true + featured: true }, { name: "Research Assistant", @@ -93,7 +74,7 @@ def self.seed_defaults! description: "Helps research topics, summarize information, and organize findings.", category: "research", provider: "anthropic", - model: "claude-sonnet-4-20250514", + model: "claude-sonnet-5", preset_type: "research", appearance: { hat: "safari", heldItem: "magnifyingGlass" }, instruction_sets: %w[github python], @@ -101,8 +82,7 @@ def self.seed_defaults! model_config: { temperature: 0.5 }, instructions: "You are a thorough research assistant. Help users by:\n- Searching for relevant information\n- Summarizing complex topics\n- Organizing findings into clear reports\n- Identifying key insights and patterns\n\nAlways cite sources when available and distinguish between facts and opinions.", icon: "🔍", - featured: true, - free_tier: true + featured: true }, { name: "Writing Assistant", @@ -118,8 +98,7 @@ def self.seed_defaults! model_config: { temperature: 0.7 }, instructions: "You are a skilled writer and editor. Help users with:\n- Writing and editing content\n- Improving clarity and flow\n- Adjusting tone for different audiences\n- Grammar and style corrections\n\nMaintain the author's voice while suggesting improvements.", icon: "✍️", - featured: true, - free_tier: true + featured: true }, { name: "Browser Automation", @@ -127,7 +106,7 @@ def self.seed_defaults! description: "Automates web browsing tasks like form filling, data extraction, and testing.", category: "automation", provider: "anthropic", - model: "claude-sonnet-4-20250514", + model: "claude-sonnet-5", preset_type: "playwright", appearance: { hat: "fedora", hatAccessory: "theaterMasks", heldItem: "browser" }, instruction_sets: %w[typescript], @@ -135,8 +114,7 @@ def self.seed_defaults! model_config: { temperature: 0.2 }, instructions: "You are a browser automation specialist. Help users by:\n- Navigating web pages\n- Filling out forms\n- Extracting data from websites\n- Testing web applications\n\nAlways wait for page loads and handle errors gracefully.", icon: "🎭", - featured: false, - free_tier: true + featured: false }, { name: "Data Analyst", @@ -152,8 +130,7 @@ def self.seed_defaults! model_config: { temperature: 0.3 }, instructions: "You are a data analyst. Help users by:\n- Analyzing datasets\n- Creating visualizations\n- Finding patterns and insights\n- Generating reports\n\nExplain your methodology and provide clear interpretations of results.", icon: "📊", - featured: true, - free_tier: true + featured: true }, { name: "DevOps Assistant", @@ -169,8 +146,7 @@ def self.seed_defaults! model_config: { temperature: 0.2 }, instructions: "You are a DevOps engineer. Help users with:\n- Infrastructure setup and management\n- CI/CD pipeline configuration\n- Container orchestration\n- Cloud resource management\n\nAlways prioritize security and follow best practices.", icon: "🚀", - featured: false, - free_tier: true + featured: false }, { name: "PlaywrightMCP Demo", @@ -178,7 +154,7 @@ def self.seed_defaults! description: "Free browser automation demo using Playwright MCP. Navigate sites, take screenshots, and extract content.", category: "automation", provider: "anthropic", - model: "claude-sonnet-4-20250514", + model: "claude-sonnet-5", preset_type: "playwright", appearance: { hat: "fedora", hatAccessory: "theaterMasks", heldItem: "browser" }, instruction_sets: [], @@ -198,7 +174,7 @@ def self.seed_defaults! ] templates.each do |template_attrs| - find_or_create_by!(slug: template_attrs[:slug]) do |t| + AgentTemplate.find_or_create_by!(slug: template_attrs[:slug]) do |t| t.assign_attributes(template_attrs) end end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_version.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_version.rb index b4d65414..2fc78e0a 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_version.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_version.rb @@ -2,18 +2,8 @@ module ActiveAgent module Dashboard - # Tracks version history for agent configurations. - # - # Each time an agent's configuration changes, a new version is created - # with a snapshot of the configuration at that point in time. - # - # @example Comparing versions - # v1 = agent.agent_versions.find_by(version_number: 1) - # v2 = agent.agent_versions.find_by(version_number: 2) - # changes = v2.diff(v1) - # class AgentVersion < ApplicationRecord - belongs_to :agent, class_name: "ActiveAgent::Dashboard::Agent" + belongs_to :agent validates :version_number, presence: true, uniqueness: { scope: :agent_id } validates :configuration_snapshot, presence: true diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/api_key.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/api_key.rb new file mode 100644 index 00000000..c0dadfa4 --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/api_key.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # API key generated from Settings -> API Keys. Authenticates requests to + # the telemetry ingest endpoint as a Bearer token. + # + # The token is generated server-side (never accepted from user input) and + # encrypted at rest with Active Record Encryption, so the host app needs + # `rails db:encryption:init` before creating keys. Deterministic + # encryption keeps find_by(token:) lookups working against the ciphertext. + # Set ActiveAgent::Dashboard.encrypt_credentials = false to store tokens + # in plain text instead; that is a downgrade, not a default. + class ApiKey < ApplicationRecord + TOKEN_PREFIX = "aa_" + + include Ownable + + encrypts :token, deterministic: true if ActiveAgent::Dashboard.encrypt_credentials + + validates :name, presence: true, length: { maximum: 100 } + validates :token, presence: true, uniqueness: true + + before_validation :generate_token, on: :create + + # Finds the key for a presented bearer token. Returns nil for blank or + # unknown tokens. + def self.authenticate(token) + return nil if token.blank? + + find_by(token: token) + end + + def touch_last_used! + # Throttled to avoid a write per ingest request. + update_column(:last_used_at, Time.current) if last_used_at.nil? || last_used_at < 1.minute.ago + end + + # Safe to display in the dashboard key list: "aa_3xam…k9Q2" + def masked_token + "#{token_prefix}…#{token.last(4)}" + end + + private + + def generate_token + return if token.present? + + self.token = "#{TOKEN_PREFIX}#{SecureRandom.base58(32)}" + self.token_prefix = token.first(TOKEN_PREFIX.length + 4) + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb index dd2b9f47..3530427f 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb @@ -4,16 +4,19 @@ module ActiveAgent module Dashboard # Base class for all Dashboard engine models. # - # Provides multi-tenant support when configured, allowing the same models - # to work in both local (single-tenant) and platform (multi-tenant) modes. + # Table names come from ActiveAgent::Dashboard.table_name_prefix through + # Rails' standard namespaced-model resolution, so the engine's own + # migrations (active_agent_agents, active_agent_agent_runs, ...) and a + # host app that already owns the tables unprefixed are both supported + # without touching the models. + # + # Ownership is configurable: multi-tenant installs scope records to an + # Account, single-tenant installs to a User, and a single-user install + # scopes to nothing at all. class ApplicationRecord < ::ActiveRecord::Base - self.abstract_class = true + include AdapterAware - # Override table name calculation to use active_agent_ prefix - # without the "dashboard_" from the module namespace - def self.table_name - @table_name ||= "active_agent_#{name.demodulize.underscore.pluralize}" - end + self.abstract_class = true class << self # Returns the owner association name based on configuration. @@ -32,10 +35,10 @@ def owner_association def for_owner(owner) return all if owner.nil? - if ActiveAgent::Dashboard.multi_tenant? - where(account: owner) + if ActiveAgent::Dashboard.multi_tenant? && column_names.include?("account_id") + where(account_id: owner.id) elsif column_names.include?("user_id") - where(user: owner) + where(user_id: owner.id) else all end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/evaluation.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/evaluation.rb new file mode 100644 index 00000000..477b352d --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/evaluation.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # An evaluation definition for an agent: a named set of criteria scored + # against the agent's recorded behavior — its recent generations + # (solid_agent's agent_generations dataset) and its telemetry traces. + # + # Criteria are stored as an array of { "key", "type", "config" } hashes. + # Rule-based criterion types score each sampled generation + # deterministically; telemetry criterion types score aggregates over the + # agent's traces (error rate, latency); the llm_judge type asks a judge + # model to score each sample and requires a configured provider. + class Evaluation < ApplicationRecord + belongs_to :agent + has_many :evaluation_runs, dependent: :destroy + + # judge_defined: the judge model authors the KPI criteria itself from the + # agent's instructions + sample interactions on the first run, then scores + # against them (criteria stay persisted/editable so scores are comparable + # across runs and models). + JUDGE_KINDS = %w[rules llm judge_defined].freeze + + RULE_CRITERION_TYPES = %w[ + response_present min_length max_latency_ms token_budget contains not_contains + ].freeze + # Scored from the agent's telemetry traces (aggregate, not per-sample). + TELEMETRY_CRITERION_TYPES = %w[trace_error_rate trace_latency].freeze + CRITERION_TYPES = (RULE_CRITERION_TYPES + TELEMETRY_CRITERION_TYPES + %w[llm_judge]).freeze + + validates :name, presence: true, uniqueness: { scope: :agent_id } + validates :judge_kind, inclusion: { in: JUDGE_KINDS } + validates :sample_size, numericality: { greater_than: 0, less_than_or_equal_to: 100 } + validate :validate_criteria + + scope :recent, -> { order(updated_at: :desc) } + + def latest_run + evaluation_runs.order(created_at: :desc).first + end + + def judge_defined? + judge_kind == "judge_defined" + end + + # Candidate models for per-cohort comparison scoring (config, optional). + def compare_models + Array(config["compare_models"]).map(&:to_s).reject(&:blank?) + end + + def run! + EvaluationRunnerService.call(self) + end + + def llm_criteria + criteria.select { |c| c["type"] == "llm_judge" } + end + + private + + def validate_criteria + if criteria.blank? + # judge_defined evaluations start empty — the judge authors the KPIs + # on the first run. + errors.add(:criteria, "must include at least one criterion") unless judge_defined? + return + end + + criteria.each do |criterion| + unless criterion.is_a?(Hash) && criterion["key"].present? + errors.add(:criteria, "entries must have a key") + next + end + + unless CRITERION_TYPES.include?(criterion["type"]) + errors.add(:criteria, "unknown criterion type #{criterion['type']}") + end + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/evaluation_run.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/evaluation_run.rb new file mode 100644 index 00000000..b59c0cb6 --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/evaluation_run.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # One execution of an Evaluation over a sample of the agent's generations. + # scores: { criterion_key => { "score", "min", "max", "passed", "total" } } + class EvaluationRun < ApplicationRecord + belongs_to :evaluation + + enum :status, { pending: 0, running: 1, complete: 2, failed: 3 } + + scope :recent, -> { order(created_at: :desc) } + + def average_score + values = scores.values.map { |s| s["score"] }.compact + return nil if values.empty? + + (values.sum.to_f / values.size).round(3) + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/model_pricing.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/model_pricing.rb new file mode 100644 index 00000000..5a1e5b0a --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/model_pricing.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Estimates LLM spend from token counts. The activeagent gem's telemetry + # records tokens only; the platform layers pricing on top for the cost + # figures shown in Traces and Metrics. + # + # Rates come from RubyLLM's model registry (USD per million tokens, + # maintained upstream per model) when the model is known there; the static + # pattern table below is the fallback for aliases/self-hosted models, and + # a conservative blended rate covers everything else so totals stay + # meaningful. Costs are always presented as estimates. + class ModelPricing + PRICES = [ + # [pattern, input $/1M, output $/1M] + [ /gpt-4o-mini/i, 0.15, 0.60 ], + [ /gpt-4o/i, 2.50, 10.00 ], + [ /gpt-4\.1-nano/i, 0.10, 0.40 ], + [ /gpt-4\.1-mini/i, 0.40, 1.60 ], + [ /gpt-4\.1/i, 2.00, 8.00 ], + [ /o3-mini|o4-mini/i, 1.10, 4.40 ], + [ /claude.*(fable|mythos)/i, 10.00, 50.00 ], + [ /claude.*haiku-?4/i, 1.00, 5.00 ], + [ /claude.*(haiku)/i, 0.80, 4.00 ], + [ /claude.*(sonnet)/i, 3.00, 15.00 ], + [ /claude.*opus-(5|4-[5-9])/i, 5.00, 25.00 ], + [ /claude.*(opus)/i, 15.00, 75.00 ], + [ /gemini.*flash/i, 0.10, 0.40 ], + [ /gemini.*pro/i, 1.25, 10.00 ], + [ /llama|mistral|mixtral|qwen|deepseek/i, 0.20, 0.60 ], + # Zero-prices "mock-*" traces recorded before the mock fallback was + # removed, so legacy rows never register as real spend. + [ /mock/i, 0.0, 0.0 ] + ].freeze + + # Fallback blended rate for unknown models ($/1M input, $/1M output) + DEFAULT_RATE = [ 1.00, 4.00 ].freeze + + # @return [Float, nil] estimated USD cost, nil when there is nothing to price + def self.estimate(model:, input_tokens:, output_tokens:) + input = input_tokens.to_i + output = output_tokens.to_i + return nil if input.zero? && output.zero? + + input_rate, output_rate = rate_for(model) + ((input * input_rate) + (output * output_rate)) / 1_000_000.0 + end + + def self.rate_for(model) + return DEFAULT_RATE if model.blank? + + registry_rate(model) || static_rate(model) + end + + # Exact per-model rates from RubyLLM's registry. Lookups are memoized — + # the registry scan is not free and trace serialization calls this per + # row. + def self.registry_rate(model) + @registry_rates ||= {} + return @registry_rates[model] if @registry_rates.key?(model) + + @registry_rates[model] = begin + info = RubyLLM.models.find(model.to_s) + tokens = info&.pricing&.text_tokens + if tokens&.input && tokens&.output + [ tokens.input, tokens.output ] + end + rescue StandardError + nil + end + end + + def self.static_rate(model) + PRICES.each do |pattern, input_rate, output_rate| + return [ input_rate, output_rate ] if model.to_s.match?(pattern) + end + DEFAULT_RATE + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/provider_key.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/provider_key.rb new file mode 100644 index 00000000..d6809af3 --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/provider_key.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Per-account LLM provider credential (Settings -> Provider API Keys). + # Generation runs (AgentExecutionService and the evaluation LLM judge) + # prefer these over the platform's ENV-configured keys, so users can run + # agents with their own OpenAI/Anthropic/OpenRouter accounts — or point + # ollama at their own host (e.g. a tunnel to a locally running instance). + # + # The credential is encrypted at rest with Active Record Encryption. API + # keys are never rendered back to the client — only a masked hint; ollama + # hosts are not secret and are shown in full (see #display_hint). + class ProviderKey < ApplicationRecord + # Providers that authenticate with an API key. + KEY_PROVIDERS = %w[openai anthropic openrouter].freeze + # Providers addressed by host URL instead of a key. + HOST_PROVIDERS = %w[ollama].freeze + PROVIDERS = (KEY_PROVIDERS + HOST_PROVIDERS).freeze + + include Ownable + + encrypts :credential if ActiveAgent::Dashboard.encrypt_credentials + + validates :provider, presence: true, inclusion: { in: PROVIDERS } + # One credential per provider per owner; which column that means + # depends on the configured mode, so it is checked at validation time. + validate :provider_unique_within_owner + validates :credential, presence: true, length: { maximum: 500 } + validates :credential, format: { with: %r{\Ahttps?://\S+\z}, message: "must be an http(s):// URL" }, + if: :host_based? + + def host_based? + HOST_PROVIDERS.include?(provider) + end + + # Options merged into generate_with for runs owned by this key's owner, + # overriding the host app's config/active_agent.yml credentials. + def generation_options + host_based? ? { host: credential } : { access_token: credential } + end + + # "sk-a…Q2z9" for keys; hosts are shown in full. + def display_hint + return credential if host_based? + + "#{credential.first(4)}…#{credential.last(4)}" + end + + private + + def provider_unique_within_owner + return if provider.blank? + + siblings = self.class.for_owner(owner) + siblings = siblings.where.not(id: id) if persisted? + errors.add(:provider, "already has a credential") if siblings.exists?(provider: provider) + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_action.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_action.rb index 518a31f3..479cf8aa 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_action.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_action.rb @@ -2,14 +2,9 @@ module ActiveAgent module Dashboard - # Represents a single browser action within a session recording. - # - # Actions capture user and agent interactions like clicks, typing, - # navigation, and form submissions. - # class RecordingAction < ApplicationRecord - belongs_to :session_recording, class_name: "ActiveAgent::Dashboard::SessionRecording" - has_one :snapshot, class_name: "ActiveAgent::Dashboard::RecordingSnapshot", dependent: :nullify + belongs_to :session_recording + has_one :snapshot, class_name: "RecordingSnapshot", dependent: :nullify ACTION_TYPES = %w[ navigate @@ -44,14 +39,14 @@ class RecordingAction < ApplicationRecord def screenshot_url(expires_in: 15.minutes) return nil unless screenshot_key.present? - ActiveAgent::Dashboard.storage_service&.signed_url_for(screenshot_key, expires_in: expires_in) + SessionRecordingService.signed_url_for(screenshot_key, expires_in: expires_in) end # Get the DOM snapshot content def dom_snapshot_content return nil unless dom_snapshot_key.present? - ActiveAgent::Dashboard.storage_service&.fetch_snapshot(dom_snapshot_key) + SessionRecordingService.fetch_snapshot(dom_snapshot_key) end # Format for API response @@ -98,8 +93,8 @@ def should_redact? /cvv/i, /ssn/i, /social.?security/i, - /\b\d{16}\b/, - /\b\d{3}-\d{2}-\d{4}\b/ + /\b\d{16}\b/, # Credit card numbers + /\b\d{3}-\d{2}-\d{4}\b/ # SSN format ] selector_is_sensitive = sensitive_patterns.any? { |p| selector&.match?(p) } @@ -109,6 +104,7 @@ def should_redact? end def safe_metadata + # Remove any sensitive data from metadata metadata.except("password", "credit_card", "cvv", "ssn") end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_snapshot.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_snapshot.rb index 8c4e852d..9111b16d 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_snapshot.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_snapshot.rb @@ -2,16 +2,11 @@ module ActiveAgent module Dashboard - # Stores screenshots and DOM snapshots from session recordings. - # - # Supports Active Storage for file attachment and provides - # signed URLs for secure access. - # class RecordingSnapshot < ApplicationRecord - belongs_to :session_recording, class_name: "ActiveAgent::Dashboard::SessionRecording" - belongs_to :recording_action, class_name: "ActiveAgent::Dashboard::RecordingAction", optional: true + belongs_to :session_recording + belongs_to :recording_action, optional: true - has_one_attached :file if defined?(ActiveStorage) + has_one_attached :file SNAPSHOT_TYPES = %w[screenshot dom full_page].freeze @@ -24,17 +19,16 @@ class RecordingSnapshot < ApplicationRecord # Get a signed URL for the file def signed_url(expires_in: 15.minutes) - return nil unless respond_to?(:file) && file.attached? + return nil unless file.attached? file.url(expires_in: expires_in) rescue StandardError + # Fallback if storage not configured nil end # Store file data def store!(data, filename: nil, content_type: nil) - return unless respond_to?(:file) - content_type ||= infer_content_type filename ||= generate_filename diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_instance_tier.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_instance_tier.rb new file mode 100644 index 00000000..8f629f39 --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_instance_tier.rb @@ -0,0 +1,370 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # SandboxInstanceTier + # + # Defines available instance sizes for sandbox sessions. + # Similar to Google Colab or Hugging Face Spaces hardware tiers. + # + # Usage: + # tier = SandboxInstanceTier.find(:gpu_t4) + # tier.cpu_cores # => 4 + # tier.memory_gb # => 16 + # tier.gpu # => "nvidia-t4" + # tier.hourly_cost # => 0.35 + # + class SandboxInstanceTier + include ActiveModel::Model + include ActiveModel::Attributes + + attribute :id, :string + attribute :name, :string + attribute :description, :string + attribute :cpu_cores, :integer + attribute :memory_gb, :integer + attribute :disk_gb, :integer + attribute :gpu, :string + attribute :gpu_memory_gb, :integer + attribute :hourly_cost, :decimal + attribute :monthly_cost, :decimal + attribute :available, :boolean, default: true + attribute :category, :string # free, pro, enterprise + + # Instance tier definitions + # Pricing modeled after Colab/HuggingFace/Lambda Labs + TIERS = { + # --- Free Tier --- + free: { + id: "free", + name: "Free", + description: "Basic CPU instance for testing", + cpu_cores: 2, + memory_gb: 4, + disk_gb: 10, + gpu: nil, + gpu_memory_gb: nil, + hourly_cost: 0.00, + monthly_cost: 0.00, + category: "free" + }, + + # --- CPU Tiers --- + cpu_small: { + id: "cpu_small", + name: "CPU Small", + description: "2 vCPUs, 8GB RAM", + cpu_cores: 2, + memory_gb: 8, + disk_gb: 20, + gpu: nil, + gpu_memory_gb: nil, + hourly_cost: 0.05, + monthly_cost: 36.00, + category: "pro" + }, + + cpu_medium: { + id: "cpu_medium", + name: "CPU Medium", + description: "4 vCPUs, 16GB RAM", + cpu_cores: 4, + memory_gb: 16, + disk_gb: 50, + gpu: nil, + gpu_memory_gb: nil, + hourly_cost: 0.10, + monthly_cost: 72.00, + category: "pro" + }, + + cpu_large: { + id: "cpu_large", + name: "CPU Large", + description: "8 vCPUs, 32GB RAM", + cpu_cores: 8, + memory_gb: 32, + disk_gb: 100, + gpu: nil, + gpu_memory_gb: nil, + hourly_cost: 0.20, + monthly_cost: 144.00, + category: "pro" + }, + + cpu_xlarge: { + id: "cpu_xlarge", + name: "CPU XLarge", + description: "16 vCPUs, 64GB RAM", + cpu_cores: 16, + memory_gb: 64, + disk_gb: 200, + gpu: nil, + gpu_memory_gb: nil, + hourly_cost: 0.40, + monthly_cost: 288.00, + category: "enterprise" + }, + + # --- GPU Tiers --- + gpu_t4: { + id: "gpu_t4", + name: "GPU T4", + description: "NVIDIA T4 (16GB VRAM), 4 vCPUs, 16GB RAM", + cpu_cores: 4, + memory_gb: 16, + disk_gb: 50, + gpu: "nvidia-tesla-t4", + gpu_memory_gb: 16, + hourly_cost: 0.35, + monthly_cost: 252.00, + category: "pro" + }, + + gpu_l4: { + id: "gpu_l4", + name: "GPU L4", + description: "NVIDIA L4 (24GB VRAM), 8 vCPUs, 32GB RAM", + cpu_cores: 8, + memory_gb: 32, + disk_gb: 100, + gpu: "nvidia-l4", + gpu_memory_gb: 24, + hourly_cost: 0.70, + monthly_cost: 504.00, + category: "pro" + }, + + gpu_a10g: { + id: "gpu_a10g", + name: "GPU A10G", + description: "NVIDIA A10G (24GB VRAM), 8 vCPUs, 32GB RAM", + cpu_cores: 8, + memory_gb: 32, + disk_gb: 100, + gpu: "nvidia-a10g", + gpu_memory_gb: 24, + hourly_cost: 1.00, + monthly_cost: 720.00, + category: "enterprise" + }, + + gpu_a100_40: { + id: "gpu_a100_40", + name: "GPU A100 40GB", + description: "NVIDIA A100 (40GB VRAM), 12 vCPUs, 85GB RAM", + cpu_cores: 12, + memory_gb: 85, + disk_gb: 200, + gpu: "nvidia-a100-40gb", + gpu_memory_gb: 40, + hourly_cost: 2.50, + monthly_cost: 1800.00, + category: "enterprise" + }, + + gpu_a100_80: { + id: "gpu_a100_80", + name: "GPU A100 80GB", + description: "NVIDIA A100 (80GB VRAM), 12 vCPUs, 170GB RAM", + cpu_cores: 12, + memory_gb: 170, + disk_gb: 200, + gpu: "nvidia-a100-80gb", + gpu_memory_gb: 80, + hourly_cost: 4.00, + monthly_cost: 2880.00, + category: "enterprise" + }, + + # --- High Memory Tiers --- + highmem_medium: { + id: "highmem_medium", + name: "High Memory Medium", + description: "4 vCPUs, 64GB RAM", + cpu_cores: 4, + memory_gb: 64, + disk_gb: 100, + gpu: nil, + gpu_memory_gb: nil, + hourly_cost: 0.25, + monthly_cost: 180.00, + category: "pro" + }, + + highmem_large: { + id: "highmem_large", + name: "High Memory Large", + description: "8 vCPUs, 128GB RAM", + cpu_cores: 8, + memory_gb: 128, + disk_gb: 200, + gpu: nil, + gpu_memory_gb: nil, + hourly_cost: 0.50, + monthly_cost: 360.00, + category: "enterprise" + } + }.freeze + + class << self + def all + TIERS.values.map { |attrs| new(attrs) } + end + + def find(id) + id = id.to_sym + raise ArgumentError, "Unknown tier: #{id}" unless TIERS.key?(id) + + new(TIERS[id]) + end + + def available + all.select(&:available) + end + + def by_category(category) + all.select { |t| t.category == category.to_s } + end + + def cpu_tiers + all.select { |t| t.gpu.nil? } + end + + def gpu_tiers + all.select { |t| t.gpu.present? } + end + + def free_tier + find(:free) + end + + def default_tier + find(:cpu_small) + end + end + + def free? + hourly_cost.zero? + end + + def has_gpu? + gpu.present? + end + + def display_price + if free? + "Free" + else + "$#{format('%.2f', hourly_cost)}/hr" + end + end + + def display_specs + specs = [ "#{cpu_cores} vCPU", "#{memory_gb}GB RAM", "#{disk_gb}GB disk" ] + specs << "#{gpu_memory_gb}GB #{gpu_display_name}" if has_gpu? + specs.join(" | ") + end + + def gpu_display_name + return nil unless gpu + + case gpu + when "nvidia-tesla-t4" then "T4" + when "nvidia-l4" then "L4" + when "nvidia-a10g" then "A10G" + when "nvidia-a100-40gb" then "A100 40GB" + when "nvidia-a100-80gb" then "A100 80GB" + else gpu.gsub("nvidia-", "").upcase + end + end + + # Convert to Incus resource limits + def to_incus_limits + limits = { + "limits.cpu" => cpu_cores.to_s, + "limits.memory" => "#{memory_gb}GB", + "limits.processes" => (cpu_cores * 250).to_s + } + + # GPU passthrough requires host configuration + if has_gpu? + limits["nvidia.runtime"] = "true" + limits["nvidia.require.cuda"] = "true" + end + + limits + end + + # Convert to Kubernetes resource spec + def to_kubernetes_resources + resources = { + requests: { + cpu: "#{cpu_cores * 500}m", + memory: "#{memory_gb / 2}Gi" + }, + limits: { + cpu: cpu_cores.to_s, + memory: "#{memory_gb}Gi" + } + } + + if has_gpu? + resources[:limits]["nvidia.com/gpu"] = "1" + end + + resources + end + + # GCP machine type mapping + def gcp_machine_type + if has_gpu? + # GPU instances use n1 or a2 series + case gpu + when "nvidia-a100-40gb", "nvidia-a100-80gb" + "a2-highgpu-1g" + else + "n1-standard-#{cpu_cores}" + end + elsif memory_gb > cpu_cores * 8 + # High memory ratio + "n2-highmem-#{cpu_cores}" + else + "n2-standard-#{cpu_cores}" + end + end + + # GCP accelerator config + def gcp_accelerator_config + return nil unless has_gpu? + + { + accelerator_type: gpu, + accelerator_count: 1 + } + end + + def as_json(*) + { + id: id, + name: name, + description: description, + specs: { + cpu_cores: cpu_cores, + memory_gb: memory_gb, + disk_gb: disk_gb, + gpu: gpu, + gpu_memory_gb: gpu_memory_gb + }, + pricing: { + hourly_cost: hourly_cost.to_f, + monthly_cost: monthly_cost.to_f, + display: display_price + }, + category: category, + available: available + } + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_run.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_run.rb index ae268fde..ecf2d0cb 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_run.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_run.rb @@ -2,14 +2,9 @@ module ActiveAgent module Dashboard - # Tracks individual task executions within a sandbox session. - # - # Each sandbox run represents a single agent task execution, - # capturing input, output, timing, and any errors. - # + # SandboxRun tracks individual agent task executions within a sandbox container. + # This model uses SQLite in sandbox mode for isolated, ephemeral storage. class SandboxRun < ApplicationRecord - belongs_to :sandbox_session, class_name: "ActiveAgent::Dashboard::SandboxSession", optional: true - enum :status, { pending: 0, running: 1, diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb index 4e52eb6c..67f763b1 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb @@ -2,27 +2,10 @@ module ActiveAgent module Dashboard - # Manages sandbox execution sessions for agent runs. - # - # Sandbox sessions provide isolated execution environments for running - # agents with tools like browser automation, file system access, etc. - # - # Supports both local (Docker/Incus) and cloud (Cloud Run) sandbox providers. - # - # @example Creating a sandbox session - # session = ActiveAgent::Dashboard::SandboxSession.create!( - # sandbox_type: "playwright_mcp" - # ) - # session.provision! - # class SandboxSession < ApplicationRecord - # Owner associations - optional to support anonymous users - belongs_to :user, class_name: ActiveAgent::Dashboard.user_class, optional: true if ActiveAgent::Dashboard.user_class - belongs_to :account, class_name: ActiveAgent::Dashboard.account_class, optional: true if ActiveAgent::Dashboard.multi_tenant? - belongs_to :agent_template, class_name: "ActiveAgent::Dashboard::AgentTemplate", optional: true + include Ownable - has_many :sandbox_runs, class_name: "ActiveAgent::Dashboard::SandboxRun", dependent: :destroy - has_one :session_recording, class_name: "ActiveAgent::Dashboard::SessionRecording", dependent: :nullify + belongs_to :agent_template, optional: true # Session statuses enum :status, { @@ -38,8 +21,8 @@ class SandboxSession < ApplicationRecord # Sandbox types SANDBOX_TYPES = %w[playwright_mcp terminal research].freeze - # Default limits (can be overridden by platform tier limits) - DEFAULT_LIMITS = { + # Free tier limits + FREE_TIER_LIMITS = { max_runs: 10, timeout_seconds: 300, max_tokens: 50_000, @@ -52,7 +35,7 @@ class SandboxSession < ApplicationRecord # Callbacks before_validation :generate_session_id, on: :create - before_create :set_defaults + before_create :set_expiration # Scopes scope :active, -> { where(status: [ :pending, :provisioning, :ready, :running ]) } @@ -85,8 +68,9 @@ def record_run!(task:, result:, duration_ms:, tokens:, screenshots: [], provider created_at: Time.current.iso8601 } + # Use pessimistic locking to prevent race conditions when multiple providers run in parallel with_lock do - reload + reload # Reload to get the latest state self.runs = runs + [ run ] self.runs_count = runs.size self.total_tokens += tokens @@ -98,33 +82,34 @@ def record_run!(task:, result:, duration_ms:, tokens:, screenshots: [], provider run end - # Provision the sandbox + # Provision the Cloud Run sandbox def provision! return if provisioning? || ready? update!(status: :provisioning) - # Use configured sandbox service + # In development, run synchronously for immediate feedback if Rails.env.development? || Rails.env.test? - ActiveAgent::Dashboard::SandboxProvisionJob.perform_now(id) + SandboxProvisionJob.perform_now(id) else - ActiveAgent::Dashboard::SandboxProvisionJob.perform_later(id) + SandboxProvisionJob.perform_later(id) end end - # Mark as ready with sandbox URL - def mark_ready!(sandbox_url:, sandbox_job_id: nil) + # Mark as ready with Cloud Run URL + def mark_ready!(cloud_run_url:, cloud_run_job_id: nil) update!( status: :ready, - cloud_run_url: sandbox_url, - cloud_run_job_id: sandbox_job_id + cloud_run_url: cloud_run_url, + cloud_run_job_id: cloud_run_job_id ) end # Expire the session def expire! update!(status: :expired) - ActiveAgent::Dashboard::SandboxCleanupJob.perform_later(id) if cloud_run_job_id.present? + # Cleanup Cloud Run resources + SandboxCleanupJob.perform_later(id) if cloud_run_job_id.present? end # Summary for API responses @@ -158,11 +143,10 @@ def generate_session_id self.session_id ||= SecureRandom.uuid end - def set_defaults - limits = ActiveAgent::Dashboard.sandbox_limits || DEFAULT_LIMITS - self.expires_at ||= limits[:session_duration_minutes].minutes.from_now - self.max_runs ||= limits[:max_runs] - self.timeout_seconds ||= limits[:timeout_seconds] + def set_expiration + self.expires_at ||= FREE_TIER_LIMITS[:session_duration_minutes].minutes.from_now + self.max_runs ||= FREE_TIER_LIMITS[:max_runs] + self.timeout_seconds ||= FREE_TIER_LIMITS[:timeout_seconds] end end end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb index 3b58ae15..35b93a70 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb @@ -2,31 +2,12 @@ module ActiveAgent module Dashboard - # Records browser sessions for playback and analysis. - # - # Session recordings capture the sequence of actions taken during - # an agent run or sandbox session, including screenshots, DOM snapshots, - # and timing information. - # - # @example Starting a recording - # recording = ActiveAgent::Dashboard::SessionRecording.start!( - # agent_run: run, - # name: "checkout_flow" - # ) - # - # @example Recording an action - # recording.record_action!( - # action_type: "click", - # selector: "button.submit", - # screenshot: screenshot_data - # ) - # class SessionRecording < ApplicationRecord - belongs_to :agent_run, class_name: "ActiveAgent::Dashboard::AgentRun", optional: true - belongs_to :sandbox_session, class_name: "ActiveAgent::Dashboard::SandboxSession", optional: true + belongs_to :agent_run, optional: true + belongs_to :sandbox_session, optional: true - has_many :recording_actions, class_name: "ActiveAgent::Dashboard::RecordingAction", dependent: :destroy - has_many :recording_snapshots, class_name: "ActiveAgent::Dashboard::RecordingSnapshot", dependent: :destroy + has_many :recording_actions, dependent: :destroy + has_many :recording_snapshots, dependent: :destroy enum :status, { recording: 0, completed: 1, failed: 2 } @@ -70,7 +51,7 @@ def self.start_user_session!(visitor_id: nil, parent_demo_id: nil, page_url: nil visitor_id: visitor_id, parent_demo_id: parent_demo_id, page_url: page_url, - user_agent: nil + user_agent: nil # Will be set from request } ) end @@ -88,11 +69,13 @@ def record_action!(action_type:, selector: nil, value: nil, screenshot: nil, dom metadata: metadata ) + # Handle screenshot attachment if provided if screenshot.present? snapshot = store_snapshot(screenshot, :screenshot, action) action.update!(screenshot_key: snapshot.storage_key) end + # Handle DOM snapshot if provided if dom_snapshot.present? snapshot = store_snapshot(dom_snapshot, :dom, action) action.update!(dom_snapshot_key: snapshot.storage_key) @@ -174,6 +157,7 @@ def elapsed_ms def store_snapshot(data, snapshot_type, action = nil) storage_key = generate_storage_key(snapshot_type, action&.sequence) + # For now, store metadata - actual file upload handled by service recording_snapshots.create!( recording_action: action, storage_key: storage_key, diff --git a/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb b/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb index 5fcd9438..1916978f 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb @@ -17,6 +17,8 @@ module ActiveAgent # ActiveAgent::TelemetryTrace.create_from_payload(trace_payload, sdk_info, account: account) # class TelemetryTrace < ::ActiveRecord::Base + include ActiveAgent::Dashboard::AdapterAware + self.table_name = "active_agent_telemetry_traces" # Optional account association for multi-tenant mode diff --git a/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/adapter_aware.rb b/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/adapter_aware.rb new file mode 100644 index 00000000..72d5be28 --- /dev/null +++ b/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/adapter_aware.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # The dashboard's aggregate queries are written portably, but a few of + # them have a much faster PostgreSQL form (jsonb traversal, containment). + # This is how those queries choose which one to run. + module AdapterAware + extend ActiveSupport::Concern + + class_methods do + # True when the backing store is PostgreSQL. + def postgres? + connection.adapter_name.to_s.downcase.include?("postgres") + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/ownable.rb b/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/ownable.rb new file mode 100644 index 00000000..a0b9f103 --- /dev/null +++ b/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/ownable.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Attaches a dashboard record to whatever the host app calls an owner. + # + # Multi-tenant installs own records through an Account, single-tenant + # installs through a User, and a single-user self-hosted install owns + # nothing at all — the associations simply aren't declared, so the engine + # boots in an app with no User model. + # + # Both foreign keys exist in the engine's schema either way, so switching + # a deployment between modes is a configuration change, not a migration. + module Ownable + extend ActiveSupport::Concern + + included do + if ActiveAgent::Dashboard.account_class + belongs_to :account, class_name: ActiveAgent::Dashboard.account_class, optional: true + end + + if ActiveAgent::Dashboard.user_class + belongs_to :user, class_name: ActiveAgent::Dashboard.user_class, optional: true + end + + scope :owned_by, ->(owner) { for_owner(owner) } + end + + # The record's owner under the current mode, or nil when unowned. + def owner + if ActiveAgent::Dashboard.multi_tenant? + respond_to?(:account) ? account : nil + else + respond_to?(:user) ? user : nil + end + end + + # Assigns +owner+ to whichever association this mode uses. Ignored when + # the host app configured no owner model. + def owner=(record) + if ActiveAgent::Dashboard.multi_tenant? + self.account = record if respond_to?(:account=) + elsif respond_to?(:user=) + self.user = record + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/session_recordable.rb b/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/session_recordable.rb new file mode 100644 index 00000000..21ad1a4f --- /dev/null +++ b/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/session_recordable.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Concern for adding session recording to models that track browser interactions + # + # Include this in models like AgentRun or SandboxSession to enable + # automatic session recording for playback and handoff. + # + # Usage: + # class AgentRun < ApplicationRecord + # include SessionRecordable + # end + # + # # Start recording + # run.start_session_recording! + # + # # Record an action + # run.record_browser_action!(type: :click, selector: "#submit", screenshot: png_data) + # + # # Complete recording + # run.complete_session_recording! + # + module SessionRecordable + extend ActiveSupport::Concern + + included do + has_one :session_recording, dependent: :destroy + end + + # Start a new session recording + def start_session_recording!(name: nil) + return session_recording if session_recording&.recording? + + create_session_recording!( + name: name || default_recording_name, + status: :recording, + metadata: { started_at: Time.current.iso8601 } + ) + end + + # Record a browser action + def record_browser_action!(type:, selector: nil, value: nil, screenshot: nil, dom_snapshot: nil, metadata: {}) + return unless session_recording&.recording? + + session_recording.record_action!( + action_type: type.to_s, + selector: selector, + value: value, + screenshot: screenshot, + dom_snapshot: dom_snapshot, + metadata: metadata + ) + end + + # Complete the recording + def complete_session_recording! + session_recording&.complete! + end + + # Fail the recording + def fail_session_recording!(error_message = nil) + session_recording&.fail!(error_message) + end + + # Check if recording is active + def recording_active? + session_recording&.recording? + end + + # Get the session recording service + def recording_service + return nil unless session_recording + + SessionRecordingService.new(session_recording) + end + + private + + def default_recording_name + prefix = if respond_to?(:name) && name.present? + name.parameterize + elsif respond_to?(:agent) && agent&.name + agent.name.parameterize + else + "session" + end + + "#{prefix}_#{Time.current.strftime('%Y%m%d_%H%M%S')}" + end + end + end +end diff --git a/lib/active_agent/dashboard/app/queries/active_agent/dashboard/agent_executions.rb b/lib/active_agent/dashboard/app/queries/active_agent/dashboard/agent_executions.rb new file mode 100644 index 00000000..3c5c532d --- /dev/null +++ b/lib/active_agent/dashboard/app/queries/active_agent/dashboard/agent_executions.rb @@ -0,0 +1,199 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # One agent execution, whoever ran it. + # + # The platform records executions it initiates as AgentRun rows; agents + # running inside a customer's own app only ever produce a TelemetryTrace. + # They are the same grain — one attempt at one agent action — so the + # dashboard treats them as one list with a `source` discriminator rather + # than as two competing tabs: + # + # dashboard — an AgentRun (we executed it; has lifecycle, logs, cancel) + # reported — a trace no AgentRun claims (their app executed it) + # + # A platform execution usually writes BOTH an AgentRun and a trace sharing a + # trace_id, so traces are counted only when unclaimed. The reverse is not + # symmetric: a run that fails before its root span exists has no trace at + # all, which is why runs — not traces — are the authoritative source when + # both exist. + # + # Distinct from AgentContext, which is a *stream*: one durable conversation + # per agent action that many executions append to. A stream is a parent of + # executions, not an alternative to them. + class AgentExecutions + SOURCES = %w[dashboard reported].freeze + + # What to rank a list of executions by. "Popular" is deliberately absent: + # it's an aggregate property of an agent or a model, not of one attempt — + # the agent list and the metrics table sort by it, this list can't. + SORTS = { + "recent" => "Most recent", + "longest" => "Longest running", + "cost" => "Highest cost", + "tokens" => "Most tokens" + }.freeze + DEFAULT_SORT = "recent" + + Row = Struct.new( + :id, :source, :status, :occurred_at, :duration_ms, :tokens, + :input_tokens, :output_tokens, :cost, + :trace_id, :agent_id, :action_name, :provider, :model, + :input_preview, :output_preview, :error_message, + keyword_init: true + ) do + def as_json(*) + to_h.transform_values { |value| value.is_a?(Time) ? value.iso8601(3) : value } + end + + # Runs are addressable by record id; reported executions only by trace. + def to_param + source == "dashboard" ? id.to_s : "trace-#{id}" + end + end + + # @param agents [ActiveRecord::Relation, Array] scope to these agents + # @param owner [Object, nil] tenant whose reported traces are in scope; + # nil in a single-tenant install, where every trace is in scope + # @param window_minutes [Integer, nil] nil means all time + # @param source [String, nil] "dashboard" | "reported" | nil for both + # @param status [String, nil] AgentRun status; also filters reported by OK/ERROR + # @param sort [String, nil] one of SORTS; unknown values fall back to recent + def initialize(agents:, owner: nil, window_minutes: nil, source: nil, status: nil, sort: nil) + @agent_ids = Array(agents.respond_to?(:pluck) ? agents.pluck(:id) : agents.map(&:id)) + @owner = owner + @window_minutes = window_minutes + @source = source.presence + @status = status.presence + @sort = SORTS.key?(sort.to_s) ? sort.to_s : DEFAULT_SORT + end + + # Ranked by the chosen dimension, descending. The two sources can only be + # ordered together in Ruby (no SQL union), which is also why this always + # ranks the whole window rather than the page. + def page(page: 1, per_page: 20) + all = sorted_rows + offset = (page.to_i - 1) * per_page.to_i + { rows: all[offset, per_page.to_i] || [], total: all.size } + end + + def sorted_rows + # Ties (and rows missing the sorted value entirely — an execution still + # running has no duration) fall back to newest first, so the list never + # reorders arbitrarily between requests. + rows.sort_by { |row| [ -sort_value(row), -(row.occurred_at || Time.at(0)).to_f ] } + end + + def rows + @rows ||= (include_dashboard? ? run_rows : []) + (include_reported? ? trace_rows : []) + end + + # Traces attributed to these agents that no AgentRun already accounts for. + # Shared with AgentScorecard so every surface counts the same executions. + def self.unclaimed_traces(agent_ids, since: nil, owner: nil) + traces = ActiveAgent::Dashboard.trace_model + runs_table = AgentRun.table_name + traces_table = traces.table_name + + scope = traces.where(agent_id: agent_ids) + scope = scope.for_account(owner) if owner + scope = scope.where(timestamp: since..) if since + scope + .joins(<<~SQL.squish) + LEFT JOIN #{runs_table} + ON #{runs_table}.trace_id = #{traces_table}.trace_id + AND #{runs_table}.agent_id = #{traces_table}.agent_id + SQL + .where(runs_table => { id: nil }) + end + + private + + def sort_value(row) + case @sort + when "longest" then row.duration_ms.to_f + when "cost" then row.cost.to_f + when "tokens" then row.tokens.to_i + else (row.occurred_at || Time.at(0)).to_f + end + end + + def include_dashboard? = @source.nil? || @source == "dashboard" + # Reported traces are in scope whether or not the install has tenants; + # a single-user dashboard still receives telemetry from its own apps. + def include_reported? = @source.nil? || @source == "reported" + + def since = @window_minutes ? @window_minutes.to_i.minutes.ago : nil + + def run_rows + scope = AgentRun.where(agent_id: @agent_ids) + scope = scope.where(created_at: since..) if since + scope = scope.where(status: @status) if @status + + scope.map do |run| + model = run.output_metadata&.dig("model") + Row.new( + id: run.id, + source: "dashboard", + status: run.status, + occurred_at: run.created_at, + duration_ms: run.calculated_duration_ms, + tokens: run.total_tokens.to_i, + input_tokens: run.input_tokens.to_i, + output_tokens: run.output_tokens.to_i, + cost: ModelPricing.estimate( + model: model, + input_tokens: run.input_tokens, + output_tokens: run.output_tokens + ), + trace_id: run.trace_id, + agent_id: run.agent_id, + action_name: run.action_name, + provider: run.output_metadata&.dig("provider"), + model: model, + input_preview: run.input_prompt.to_s.truncate(160), + output_preview: run.output.to_s.truncate(160).presence, + error_message: run.error_message + ) + end + end + + def trace_rows + scope = self.class.unclaimed_traces(@agent_ids, since: since, owner: @owner) + # A run status filter has no exact analogue on a trace; map the two that + # do rather than silently ignoring the filter. + scope = scope.where(status: "ERROR") if @status == "failed" + scope = scope.where.not(status: "ERROR") if @status == "complete" + return [] if @status.present? && !%w[failed complete].include?(@status) + + scope.map do |trace| + model = trace.model + Row.new( + id: trace.id, + source: "reported", + status: trace.status == "ERROR" ? "failed" : "complete", + occurred_at: trace.timestamp, + duration_ms: trace.total_duration_ms&.round, + tokens: trace.total_tokens.to_i, + input_tokens: trace.total_input_tokens.to_i, + output_tokens: trace.total_output_tokens.to_i, + cost: ModelPricing.estimate( + model: model, + input_tokens: trace.total_input_tokens, + output_tokens: trace.total_output_tokens + ), + trace_id: trace.trace_id, + agent_id: trace.agent_id, + action_name: trace.agent_action, + provider: trace.provider, + model: model, + input_preview: nil, + output_preview: nil, + error_message: trace.error_message + ) + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/serializers/active_agent/dashboard/agent_message_serializer.rb b/lib/active_agent/dashboard/app/serializers/active_agent/dashboard/agent_message_serializer.rb new file mode 100644 index 00000000..6fa0ea72 --- /dev/null +++ b/lib/active_agent/dashboard/app/serializers/active_agent/dashboard/agent_message_serializer.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Serializes AgentMessage records for the shared InteractionStream UI + # (Interactions view and the agent Conversation History run detail). + class AgentMessageSerializer + def self.call(message) + { + id: message.id, + role: message.role, + content: message.content, + tool_name: message.tool_name, + tool_call_id: message.tool_call_id, + tool_calls: message.tool_calls_data, + tool_arguments: message.tool_arguments.presence, + tool_result: message.tool_result, + duration_ms: message.metadata&.dig("duration_ms"), + content_checksum: message.content_checksum, + created_at: message.created_at.iso8601(3) + } + end + end + end +end diff --git a/lib/active_agent/dashboard/app/serializers/active_agent/dashboard/telemetry_trace_serializer.rb b/lib/active_agent/dashboard/app/serializers/active_agent/dashboard/telemetry_trace_serializer.rb new file mode 100644 index 00000000..21b1d166 --- /dev/null +++ b/lib/active_agent/dashboard/app/serializers/active_agent/dashboard/telemetry_trace_serializer.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Serializes TelemetryTrace records (gem span payloads) into the view-model + # consumed by the dashboard's TracesView: relative span offsets for the + # waterfall timeline, nesting depth, and token totals. + class TelemetryTraceSerializer + # Span types understood by the dashboard timeline (superset of the gem's + # ActiveAgent::Telemetry::Span::TYPES). + KNOWN_SPAN_TYPES = %w[root prompt generate llm tool thinking embedding response error].freeze + + def self.summary(trace) + new(trace).summary + end + + def self.detail(trace) + new(trace).detail + end + + def initialize(trace) + @trace = trace + end + + def summary + { + id: @trace.id, + trace_id: @trace.trace_id, + short_id: @trace.trace_id&.first(8), + agent: @trace.agent_class, + action: @trace.agent_action, + display_name: @trace.display_name, + service_name: @trace.service_name, + environment: @trace.environment, + provider: @trace.provider, + model: @trace.model, + status: @trace.status, + error: @trace.error_message, + duration_ms: @trace.total_duration_ms&.to_f&.round(2), + timestamp: @trace.timestamp&.iso8601(3), + timestamp_ms: @trace.timestamp&.to_f&.*(1000)&.round, + tokens: { + input: @trace.total_input_tokens || 0, + output: @trace.total_output_tokens || 0, + thinking: @trace.total_thinking_tokens || 0, + total: @trace.total_tokens + }, + estimated_cost: ModelPricing.estimate( + model: @trace.model, + input_tokens: @trace.total_input_tokens, + output_tokens: @trace.total_output_tokens + ), + spans: serialized_spans + } + end + + def detail + summary.merge( + resource_attributes: @trace.resource_attributes, + sdk_info: @trace.sdk_info + ) + end + + private + + # Maps raw gem spans (start_time/end_time ISO strings, parent_span_id + # links) to waterfall rows with millisecond offsets relative to the root + # span and a nesting depth. + def serialized_spans + spans = Array(@trace.spans) + return [] if spans.empty? + + by_id = spans.index_by { |s| s["span_id"] } + root = spans.find { |s| s["parent_span_id"].nil? } || spans.first + root_start = parse_time(root["start_time"]) + + spans.map do |span| + start = parse_time(span["start_time"]) + offset = (root_start && start) ? ((start - root_start) * 1000).round(2) : 0 + + { + span_id: span["span_id"], + name: span["name"], + type: normalized_type(span), + start: [ offset, 0 ].max, + duration: span["duration_ms"]&.to_f&.round(2) || 0, + nested: depth_of(span, by_id), + status: span["status"], + error: span["status"] == "ERROR", + tokens: span["tokens"], + attributes: span["attributes"] + } + end + end + + def normalized_type(span) + type = span["type"].to_s + KNOWN_SPAN_TYPES.include?(type) ? type : "root" + end + + def depth_of(span, by_id) + depth = 0 + current = span + while (parent_id = current["parent_span_id"]) + parent = by_id[parent_id] + break unless parent + + depth += 1 + current = parent + break if depth > 10 + end + depth + end + + def parse_time(value) + return nil if value.blank? + + Time.zone.parse(value.to_s) + rescue ArgumentError + nil + end + end + end +end diff --git a/lib/active_agent/dashboard/app/serializers/active_agent/dashboard/trace_interaction_serializer.rb b/lib/active_agent/dashboard/app/serializers/active_agent/dashboard/trace_interaction_serializer.rb new file mode 100644 index 00000000..87f93b98 --- /dev/null +++ b/lib/active_agent/dashboard/app/serializers/active_agent/dashboard/trace_interaction_serializer.rb @@ -0,0 +1,230 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Reconstructs a conversation stream from a telemetry trace, in the shape the + # dashboard's Interactions view already renders for platform-executed agents. + # + # Interactions normally reads solid_agent persistence (AgentContext / + # AgentMessage / AgentGeneration), which only exists for agents the platform + # ran itself. Agents running inside a customer's own app — a RubyLLM chat + # reporting through the telemetry endpoint, say — never write those tables, so + # the view was blind to them. + # + # The spans carry the same story when the reporter opts into content capture: + # the llm span holds `llm.prompt` / `llm.instructions` / `llm.completion`, and + # each tool span holds `tool.arguments` / `tool.result`. Ordering them by start + # time yields prompt → tool call → tool result → response. + class TraceInteractionSerializer + def self.summary(trace) + new(trace).summary + end + + def self.detail(trace) + new(trace).detail + end + + def initialize(trace) + @trace = trace + @spans = Array(trace.spans) + end + + def summary + { + id: "trace-#{@trace.id}", + source: "telemetry", + agent_name: @trace.agent_class, + action_name: @trace.agent_action, + display_name: @trace.display_name, + agent: nil, + service_name: @trace.service_name, + environment: @trace.environment, + model: @trace.model, + tokens: { + input: @trace.total_input_tokens, + output: @trace.total_output_tokens, + total: @trace.total_input_tokens.to_i + @trace.total_output_tokens.to_i + }, + message_count: messages.size, + # Tool activity is known even when content capture is off, so a run + # reported without prompts still shows what it did. + tool_count: tool_spans.size, + duration_ms: @trace.total_duration_ms&.to_f, + generation_count: llm_spans.size, + created_at: @trace.timestamp.iso8601, + last_activity_at: @trace.timestamp.iso8601 + } + end + + def detail + summary.merge( + instructions: llm_attribute("llm.instructions") || any_attribute("prompt.input.instructions"), + messages: messages, + generations: generations + ) + end + + private + + # The generation span. Older traces wrapped a separate `llm` span inside a + # root; newer ones merge them, since the provider loop *is* the interaction. + # Accept both, and never count the same generation twice. + def llm_spans + @llm_spans ||= begin + nested = @spans.select { |span| span["type"] == "llm" } + nested.presence || @spans.select { |span| span["parent_span_id"].nil? && span.dig("attributes", "llm.model") } + end + end + + def tool_spans + @tool_spans ||= @spans.select { |span| span["type"] == "tool" } + .sort_by { |span| span["start_time"].to_s } + end + + def llm_attribute(key) + llm_spans.filter_map { |span| span.dig("attributes", key).presence }.first + end + + # ActiveAgent's instrumentation puts prompt contents on the prompt span + # (RubyLLM puts everything on the llm span) — accept either home. + def any_attribute(key) + @spans.filter_map { |span| span.dig("attributes", key).presence }.first + end + + # ActiveAgent reports the outbound conversation as a JSON array of + # {role, content} on the prompt span rather than a single llm.prompt. + def outbound_messages + raw = any_attribute("prompt.input.messages") + return [] if raw.blank? + + entries = JSON.parse(raw) + entries.is_a?(Array) ? entries.select { |m| m.is_a?(Hash) && m["content"].present? } : [] + rescue JSON::ParserError + [] + end + + # prompt → (tool call → tool result)* → completion. Tool pairs are ordered by + # span start time, so the stream reads the way the run actually happened. + def messages + @messages ||= begin + stream = [] + index = 0 + prompt_span = @spans.find { |span| span["type"] == "prompt" } + + if (prompt = llm_attribute("llm.prompt")) + stream << message(index += 1, role: "user", content: prompt, at: @trace.timestamp, timing: timing_for(prompt_span)) + else + outbound_messages.each do |entry| + stream << message(index += 1, role: entry["role"].presence || "user", content: entry["content"], at: @trace.timestamp, timing: timing_for(prompt_span)) + end + end + + tool_spans.each do |span| + name = span.dig("attributes", "tool.name") + call_id = span.dig("attributes", "tool.call_id") + started = parse_time(span["start_time"]) || @trace.timestamp + finished = parse_time(span["end_time"]) || started + + stream << message( + index += 1, role: "assistant", content: nil, at: started, + tool_name: name, tool_call_id: call_id, + tool_calls: parsed(span.dig("attributes", "tool.arguments") || span.dig("attributes", "tool.input.args")), + timing: timing_for(span) + ) + stream << message( + index += 1, role: "tool", + content: span.dig("attributes", "tool.result") || span.dig("attributes", "tool.output.result"), + at: finished, + tool_name: name, tool_call_id: call_id, + timing: timing_for(span) + ) + end + + if (completion = llm_attribute("llm.completion") || llm_attribute("llm.output.message")) + stream << message(index += 1, role: "assistant", content: completion, at: end_time, timing: timing_for(llm_spans.first)) + end + + stream + end + end + + # Where a message's span sits on the trace's wall clock — lets the + # conversation view double as a waterfall (span-message pill bars). + def trace_start + @trace_start ||= @spans.filter_map { |span| parse_time(span["start_time"]) }.min + end + + def timing_for(span) + return {} unless span + + start = parse_time(span["start_time"]) + offset = (start && trace_start) ? ((start - trace_start) * 1000.0).round(2) : 0 + { + span_start_ms: [ offset, 0 ].max, + span_duration_ms: span["duration_ms"]&.to_f&.round(2) || 0, + trace_duration_ms: @trace.total_duration_ms&.to_f&.round(2) + } + end + + def message(index, role:, content:, at:, tool_name: nil, tool_call_id: nil, tool_calls: nil, timing: nil) + { + id: "#{@trace.id}-#{index}", + role: role, + content: content, + tool_name: tool_name, + tool_call_id: tool_call_id, + tool_calls: tool_calls, + content_checksum: nil, + created_at: at.iso8601(3), + **(timing || {}) + } + end + + def generations + llm_spans.map.with_index(1) do |span, index| + attributes = span["attributes"] || {} + tokens = span["tokens"] || {} + + { + id: "#{@trace.id}-llm-#{index}", + model: attributes["llm.model"], + provider: attributes["llm.provider"], + finish_reason: span["status"] == "ERROR" ? "error" : attributes["llm.finish_reason"], + tokens: { + input: tokens["input"].to_i, + output: tokens["output"].to_i, + total: tokens.values_at("input", "output", "thinking").compact.sum(&:to_i), + cached: 0, + thinking: tokens["thinking"].to_i + }, + cache_hit: false, + thinking: tokens["thinking"].to_i.positive?, + duration_seconds: span["duration_ms"] ? (span["duration_ms"].to_f / 1000).round(3) : nil, + trace_id: @trace.trace_id, + created_at: (parse_time(span["start_time"]) || @trace.timestamp).iso8601(3) + } + end + end + + # Tool arguments arrive JSON-encoded; hand the view an object when possible + # so it renders structurally rather than as an escaped string. + def parsed(value) + return nil if value.blank? + + JSON.parse(value) + rescue JSON::ParserError + value + end + + def end_time + parse_time(llm_spans.first&.dig("end_time")) || @trace.timestamp + end + + def parse_time(value) + Time.zone.parse(value.to_s) + rescue ArgumentError, TypeError + nil + end + end + end +end diff --git a/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_execution_service.rb b/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_execution_service.rb new file mode 100644 index 00000000..f624382f --- /dev/null +++ b/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_execution_service.rb @@ -0,0 +1,564 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Executes a dashboard-configured Agent through the activeagent gem and + # records a telemetry trace for the run. + # + # The requested provider is used when credentials are available — the + # account's own provider key (Settings -> Provider API Keys) when configured, + # else the platform keys in config/active_agent.yml. Without credentials the + # run fails with an actionable error: execution never falls back to mock + # output, so every stored run, trace and generation reflects a real provider + # response. (The gem's mock provider is a test double, accepted only in the + # test environment.) + # + # Traces are built with the gem's ActiveAgent::Telemetry::Span and persisted + # through TelemetryTrace.create_from_payload — the same normalizer used by + # the telemetry ingest endpoint — so platform-executed runs and SDK-reported + # runs share one pipeline. + class AgentExecutionService + # Raised when the requested provider has no usable credentials. The run is + # marked failed with this message — never silently degraded to mock output. + class ProviderNotConfiguredError < StandardError; end + + SERVICE_NAME = "activeagents-platform" + + def self.call(agent_record, run) + new(agent_record, run).call + end + + def initialize(agent_record, run) + @agent_record = agent_record + @run = run + @tool_invocations = [] + @event_sequence = 0 + end + + # Emits a progress event on the run (streamed to the UI by pollers). + # Never lets telemetry break execution. + def emit_event(**kwargs) + @run.append_event(**kwargs) + rescue StandardError => e + Rails.logger.warn("[AgentExecutionService] event emit failed: #{e.message}") + end + + def next_event_id + @event_sequence += 1 + "#{@run.id}-#{@event_sequence}" + end + + # Compact human preview of a tool result for the live activity feed: + # prefer the long readable field (page text, sub-agent output) over JSON. + def event_result_preview(result) + return nil unless result.respond_to?(:[]) + + readable = %i[text output content body].filter_map { |field| result[field] || result[field.to_s] } + .find { |value| value.is_a?(String) && value.strip.present? } + preview = readable ? readable.gsub(/\s+/, " ").strip : result.to_json + preview.byteslice(0, 1000).to_s.scrub + end + + def call + root_span = @root_span = build_root_span + record_prompt_span(root_span) + llm_span = root_span.add_span( + "llm.generate", + span_type: :llm, + "llm.provider" => provider.to_s, + "llm.model" => model + ) + + llm_eid = next_event_id + llm_started = Process.clock_gettime(Process::CLOCK_MONOTONIC) + emit_event(eid: llm_eid, kind: "llm", label: "#{provider}/#{model} generating", status: "started") + + begin + response = generate! + usage = response.usage + input = usage&.input_tokens.to_i + output = usage&.output_tokens.to_i + thinking = usage&.reasoning_tokens.to_i + + llm_span.set_tokens(input: input, output: output, thinking: thinking) + llm_span.finish + emit_event( + eid: llm_eid, kind: "llm", label: "#{provider}/#{model} generating", status: "done", + duration_ms: ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - llm_started) * 1000).round, + detail: "#{input} in / #{output} out tokens#{thinking.positive? ? " / #{thinking} thinking" : ""}" + ) + tool_calls = record_tool_spans(root_span, response) + persist_tool_messages(response) + sync_context_instructions + root_span.finish + + { + output: response.message&.content, + metadata: { + provider: provider.to_s, + model: model, + action: action_name, + instructions: composed_instructions, + requested_provider: @agent_record.provider, + trace_id: root_span.trace_id, + context_id: conversation_context&.id, + tool_calls: tool_calls + }, + usage: { + input_tokens: input, + output_tokens: output, + total_tokens: usage&.total_tokens || input + output + thinking + } + } + rescue StandardError => e + llm_span.record_error(e) + llm_span.finish + root_span.record_error(e) + root_span.finish + emit_event(eid: llm_eid, kind: "llm", label: "#{provider}/#{model} generating", status: "error", detail: e.message) + raise + ensure + record_trace(root_span) + end + end + + # The outbound prompt as a span, in the SDK's attribute shape — gives the + # Traces UI its System/User conversation rows and lets the context-pressure + # meter attribute instructions and tool schemas instead of lumping the + # whole input into "messages". + def record_prompt_span(root_span) + span = root_span.add_span("agent.prompt", span_type: :prompt) + if composed_instructions.present? + span.set_attribute("prompt.input.instructions", composed_instructions.to_s.byteslice(0, 6000).to_s.scrub) + end + if tool_schemas.present? + span.set_attribute("prompt.input.tools", tool_schemas.to_json.byteslice(0, 6000).to_s.scrub) + end + span.set_attribute( + "prompt.input.messages", + [ { role: "user", content: @run.input_prompt.to_s.byteslice(0, 4000).to_s.scrub } ].to_json + ) + span.set_attribute("messages.count", 1) + span.finish + rescue StandardError => e + Rails.logger.warn("[AgentExecutionService] prompt span failed: #{e.message}") + end + + # Per-run provider/model overrides (input_params) let callers replay the + # same agent under a different model — the basis of evaluation comparison + # runs. Absent overrides, the agent's own configuration applies. + def run_params + @run_params ||= (@run.input_params || {}).with_indifferent_access + end + + def requested_provider + @requested_provider ||= (run_params[:provider_override].presence || @agent_record.provider).to_s + end + + def requested_model + @requested_model ||= run_params[:model_override].presence || @agent_record.model + end + + # Returns the provider used for this execution, or raises when its + # credentials are missing. + def provider + @provider ||= begin + unless provider_available?(requested_provider) + raise ProviderNotConfiguredError, + "No credentials configured for provider '#{requested_provider}' — " \ + "add an API key in Settings -> Provider API Keys, or configure platform credentials" + end + requested_provider.to_sym + end + end + + # The named action this run invokes (falls back to the default). Named + # actions execute under composed instructions: base + the action's prompt. + def action_name + @action_name ||= begin + requested = @run.action_name.presence || Agent::DEFAULT_ACTION + @agent_record.available_actions.include?(requested) ? requested : Agent::DEFAULT_ACTION + end + end + + def composed_instructions + @composed_instructions ||= @agent_record.composed_instructions_for(action_name) + end + + # Routes a provider tool call to its implementation: memory tools bind to + # the agent record's AgentMemory (the solid_agent HasMemory contract); + # everything else is stateless and lives in AgentToolbox. + # + # Each call is wrapped in a live :tool span (real start/end around the + # execution) and recorded in @tool_invocations so tool names, arguments + # and durations reach Traces and the persisted conversation. + def execute_tool(name, **kwargs) + # Record the absolute URL browse_page will actually fetch, not the bare + # path the model passed — spans/events/persisted args stay unambiguous. + kwargs[:url] = AgentToolbox.resolve_browse_url(kwargs[:url]) if name.to_s == "browse_page" && kwargs[:url] + + span = @root_span&.add_span("tool.#{name}", span_type: :tool) + span&.set_attribute("tool.name", name.to_s) + # tool.input.args is the key the Traces UI and TraceInteractionSerializer + # read — the call's in: side. + span&.set_attribute("tool.input.args", kwargs.to_json.byteslice(0, 500).to_s.scrub) if kwargs.present? + started = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + event_kind = name.to_s == "call_agent" ? "agent" : "tool" + event_label = name.to_s == "call_agent" ? "call_agent → #{kwargs[:slug]}" : name.to_s + event_id = next_event_id + emit_event(eid: event_id, kind: event_kind, label: event_label, status: "started", detail: kwargs.to_json) + + result = begin + case name.to_s + when "save_memory" + entry = agent_memory.remember( + kwargs[:content].to_s, + source_agent: agent_class_name, + category: kwargs[:category] + ) + { saved: true, id: entry.id, content: entry.content } + when "recall_memory" + entries = agent_memory.recall(limit: kwargs[:limit], category: kwargs[:category]) + { + count: entries.size, + entries: entries.map do |entry| + { + content: entry.content, + category: entry.category, + source_agent: entry.source_agent, + created_at: entry.created_at&.iso8601 + }.compact + end + } + when "call_agent" + call_agent(slug: kwargs[:slug], message: kwargs[:message]) + else + AgentToolbox.call(name, **kwargs) + end + rescue StandardError => e + Rails.logger.warn("[AgentExecutionService] Tool #{name} failed: #{e.class} - #{e.message}") + { error: "#{name} failed: #{e.message}" } + end + + duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - started) * 1000).round(2) + errored = result.respond_to?(:key?) && (result.key?(:error) || result.key?("error")) + span&.set_attribute("tool.error", true) if errored + # Record the readable side of the result (most tools wrap one long text + # field); byteslicing whole-JSON breaks it mid-string and the UI can't + # parse the remainder. + result_text = + if result.respond_to?(:key?) && (result[:text] || result["text"]).is_a?(String) + result[:text] || result["text"] + else + result.to_json + end + span&.set_attribute("tool.output.result", result_text.byteslice(0, 4000).to_s.scrub) + span&.finish + emit_event( + eid: event_id, kind: event_kind, label: event_label, + status: errored ? "error" : "done", duration_ms: duration_ms, + detail: errored ? (result[:error] || result["error"]).to_s : event_result_preview(result) + ) + @tool_invocations << { + name: name.to_s, + arguments: kwargs, + duration_ms: duration_ms, + error: errored + } + + result + end + + private + + # Maximum agent-to-agent delegation depth for the call_agent tool. A + # thread-local counter guards it because the sub-agent runs synchronously + # on the same thread via Agent#test_execute. + MAX_CALL_DEPTH = 2 + + # Executes another agent of the same account synchronously and returns + # its reply, so agents can delegate to each other as a tool call. The + # sub-run is a real AgentRun with its own trace. + def call_agent(slug:, message:) + depth = Thread.current[:agent_call_depth].to_i + return { error: "call_agent depth limit (#{MAX_CALL_DEPTH}) reached" } if depth >= MAX_CALL_DEPTH + + target = workspace_agents.where.not(id: @agent_record.id).find_by(slug: slug.to_s) + return { error: "No agent with slug '#{slug}' in this workspace" } unless target + + Thread.current[:agent_call_depth] = depth + 1 + begin + sub_run = target.test_execute(message.to_s) + { + agent: target.slug, + run_id: sub_run.id, + status: sub_run.status, + output: sub_run.output.presence || sub_run.error_message + } + ensure + Thread.current[:agent_call_depth] = depth + end + end + + # Keeps the persisted context's instructions current so the Interactions + # view can render the conversation's system message. + def sync_context_instructions + context = conversation_context + return unless context + return if context.instructions == composed_instructions + + context.update_column(:instructions, composed_instructions) + rescue StandardError => e + Rails.logger.warn("[AgentExecutionService] Failed to sync context instructions: #{e.message}") + end + + # Agents callable via call_agent: everything the calling agent's owner + # owns. A single-user install has no owner, so every agent is in scope. + def workspace_agents + Agent.for_owner(owner).where.not(status: :archived) + end + + def agent_memory + @agent_memory ||= AgentMemory.for(@agent_record) + end + + def model + requested_model + end + + # Newer Anthropic models (Opus 4.7+, Sonnet 5, Fable 5/Mythos 5) reject + # sampling parameters with a 400 — they are thinking-first models steered + # by prompting/effort instead. + SAMPLING_UNSUPPORTED_MODELS = /\Aclaude-(opus-5|opus-4-[78]|sonnet-5|fable-5|mythos-5)/ + + def generate! + effective_provider = provider + provider_model = requested_model + model_options = @agent_record.model_config.to_h.symbolize_keys.slice(:temperature, :max_tokens, :top_p) + model_options.except!(:temperature, :top_p) if provider_model.to_s.match?(SAMPLING_UNSUPPORTED_MODELS) + # The owner's own credential (API key, or host URL for ollama) + # overrides the host app's config/active_agent.yml settings. + model_options.merge!(owner_provider_options(effective_provider)) + klass_name = agent_class_name + agent_record = @agent_record + input = @run.input_prompt + instructions = composed_instructions + action = action_name + run_trace_id = trace_id + tool_definitions = tool_schemas + service = self + + agent_class = Class.new(ActiveAgent::Base) do + # SolidAgent persists contexts under self.class.name; anonymous + # classes would fail its agent_name presence validation. + define_singleton_method(:name) { klass_name } + + # Persist the conversation (agent_contexts / agent_messages / + # agent_generations) via solid_agent. contextual: false — the context + # is loaded explicitly in the action below. + include SolidAgent::HasContext + has_context contextual: false + + if effective_provider == :mock + # Test environment only (see #provider_available?). + generate_with :mock + else + generate_with effective_provider, model: provider_model, **model_options + end + + # Expose the agent's server-executable tools as public methods so the + # gem's tools_function can route provider tool calls to them. The + # service routes each call to AgentToolbox or, for memory tools, to + # the run's AgentMemory. + tool_definitions.each do |definition| + define_method(definition[:name]) do |**kwargs| + service.execute_tool(definition[:name], **kwargs) + end + end + + # One method per invokable action (the default plus each named action + # prompt) — solid_agent keys the persisted context by action_name, so + # each action gets its own interaction stream. + define_method action do + # Thread the run's telemetry trace_id through prompt_options so + # SolidAgent's provenance (and AgentContext#record_generation_with_ + # provenance!) can correlate the persisted generation with its trace. + prompt_options[:trace_id] = run_trace_id + load_context(contextable: agent_record) + + options = { message: input } + options[:instructions] = instructions if instructions.present? + options[:tools] = tool_definitions if tool_definitions.present? + prompt(**options) + end + end + + agent_class.public_send(action).generate_now + end + + # Function-calling schemas for the agent's enabled tools that have + # server-side implementations (none for mock runs — the mock provider + # doesn't do tool calling). + def tool_schemas + return [] if provider == :mock + + AgentToolbox.definitions_for(@agent_record.tools) + end + + # Persists the tool interaction stream to the solid_agent conversation + # context so the Interactions view shows the full agent <-> tool + # exchange. Deduped by tool_call_id — newer solid_agent versions persist + # these from HasContext already, in which case this is a no-op. + def persist_tool_messages(response) + context = conversation_context + return unless context + return unless response.respond_to?(:messages) + + tool_messages = Array(response.messages).select do |message| + message.respond_to?(:role) && message.role.to_s == "tool" + end + + tool_messages.each_with_index do |message, index| + tool_call_id = message.respond_to?(:tool_call_id) ? message.tool_call_id : nil + next if tool_call_id.present? && context.messages.exists?(role: "tool", tool_call_id: tool_call_id) + + # Provider tool messages often carry no name (Ollama's don't); fall + # back to the service's own invocation record, matched by order. + invocation = @tool_invocations[index] + name = (message.name if message.respond_to?(:name)).presence || invocation&.dig(:name) + + context.add_tool_message( + tool_call_id: tool_call_id, + tool_name: name, + result: (message.content if message.respond_to?(:content)), + arguments: invocation&.dig(:arguments), + duration_ms: invocation&.dig(:duration_ms) + ) + end + rescue StandardError => e + Rails.logger.error("[AgentExecutionService] Failed to persist tool messages: #{e.message}") + end + + # Tool names for run metadata. Spans are recorded live in execute_tool; + # the response-message scan only covers calls the provider executed + # without routing through the service (none today, but cheap insurance). + def record_tool_spans(root_span, response) + return @tool_invocations.map { |invocation| invocation[:name] } if @tool_invocations.any? + + messages = response.respond_to?(:messages) ? Array(response.messages) : [] + tool_messages = messages.select { |message| message.respond_to?(:role) && message.role.to_s == "tool" } + + tool_messages.map do |message| + name = message.respond_to?(:name) && message.name.presence || "unknown" + tool_span = root_span.add_span("tool.#{name}", span_type: :tool) + tool_span.set_attribute("tool.name", name) + if message.respond_to?(:tool_call_id) && message.tool_call_id.present? + tool_span.set_attribute("tool.id", message.tool_call_id) + end + tool_span.finish + name + end + end + + def provider_available?(name) + # The gem's mock provider is a test double: accepted only in the test + # environment so app runs can never store fabricated output. + return Rails.env.test? if name.to_s == "mock" + return true if owner_provider_options(name).any? + + config = ActiveAgent.configuration[name.to_sym] + return false unless config.respond_to?(:[]) + + if name.to_s == "ollama" + config[:host].present? + else + config[:access_token].present? + end + rescue StandardError + false + end + + # Credential overrides for +name+: whatever the host app resolves for + # this owner first, then the dashboard's own stored ProviderKey. + def owner_provider_options(name) + @owner_provider_options ||= {} + @owner_provider_options[name.to_s] ||= begin + from_host = ActiveAgent::Dashboard.provider_credentials(owner, name.to_s) + from_host.presence || ProviderKey.for_owner(owner).find_by(provider: name.to_s)&.generation_options || {} + end + end + + def build_root_span + ActiveAgent::Telemetry::Span.new( + "#{agent_class_name}.prompt", + trace_id: trace_id, + span_type: :root, + "agent.class" => agent_class_name, + "agent.action" => action_name, + "agent.provider" => provider.to_s, + "agent.model" => model, + "service.name" => SERVICE_NAME, + "service.environment" => Rails.env, + "telemetry.sdk.name" => "activeagent", + "telemetry.sdk.version" => ActiveAgent::VERSION + ) + end + + def agent_class_name + @agent_record.telemetry_agent_class + end + + # Reuse the run's trace_id so AgentRun and TelemetryTrace correlate. + def trace_id + @trace_id ||= @run.trace_id.presence || SecureRandom.hex(16) + end + + # The solid_agent conversation context this execution persisted into + # (one per agent + action on this platform). + def conversation_context + AgentContext.find_by(contextable: @agent_record, agent_name: agent_class_name, action_name: action_name) + end + + # The agent's owner under the configured mode; nil when the install + # has no owner model at all. + def owner + @owner ||= @agent_record.owner + end + + def record_trace(root_span) + payload = { + trace_id: root_span.trace_id, + service_name: SERVICE_NAME, + environment: Rails.env, + timestamp: Time.current.iso8601(6), + resource_attributes: { "platform.agent_id" => @agent_record.id, "platform.run_id" => @run.id }, + spans: flatten_spans(root_span) + }.as_json + + sdk_info = { + name: "activeagent", + version: ActiveAgent::VERSION, + language: "ruby", + runtime_version: RUBY_VERSION + }.as_json + + trace_model = ActiveAgent::Dashboard.trace_model + return if trace_model.for_account(owner).exists?(trace_id: root_span.trace_id) + + trace_model.create_from_payload(payload, sdk_info, account: owner) + rescue StandardError => e + Rails.logger.error("[AgentExecutionService] Failed to record trace #{root_span.trace_id}: #{e.class} - #{e.message}") + nil + end + + # Flattens the span hierarchy the same way the gem's Tracer does before + # reporting (children stripped, parent_span_id links preserved). + def flatten_spans(span) + result = [ span.to_h.except(:children) ] + span.children.each { |child| result.concat(flatten_spans(child)) } + result + end + end + end +end diff --git a/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_registrar.rb b/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_registrar.rb new file mode 100644 index 00000000..3b30bceb --- /dev/null +++ b/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_registrar.rb @@ -0,0 +1,199 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Registers an Agent for each distinct agent action we observe in telemetry. + # + # Agents authored in the dashboard have records; agents running inside a + # customer's own app only ever reported traces, so the Agents list read 0 while + # Traces and Interactions were full of their runs. Ingest calls this to give + # every observed agent an identity that evaluations, versions, and per-agent + # metrics can hang off. + # + # Identity is (account, service_name, agent_class, agent_action) — one agent + # per action, not per class. Clara.respond (admin assistant, every MCP tool, + # ~$0.03/run) and Clara.title (no tools, temperature 0.2, ~$0.0004/run) are + # different agents that share a class name because one app method spawns both. + # Collapsing them would blend a $0.03 agent with a $0.0004 one into a single + # meaningless cost-per-run. + # + # Never raises: a registration failure must not fail a customer's telemetry. + class AgentRegistrar + # Guards against a misconfigured reporter with a dynamic agent class + # creating unbounded records. + MAX_OBSERVED_PER_OWNER = 200 + + def self.call(trace) + new(trace).call + rescue StandardError => e + Rails.logger.warn("[AgentRegistrar] #{e.class}: #{e.message}") + nil + end + + def initialize(trace) + @trace = trace + end + + def call + return if @trace.agent_id.present? + return if agent_class.blank? + + owner = owner_for_trace + return if owner.nil? && ActiveAgent::Dashboard.multi_tenant? + + agent = find_or_create_agent(owner) + return if agent.nil? + + @trace.update_columns(agent_id: agent.id) + touch_observation(agent) + agent + end + + private + + attr_reader :trace + + def agent_class + @trace.agent_class.presence + end + + def action_name + @trace.agent_action.presence + end + + # Name carries the action so the two Claras are distinguishable anywhere a + # bare agent name is shown. + def display_name + action_name ? "#{agent_class}.#{action_name}" : agent_class + end + + # Who the agents observed on this trace belong to. Multi-tenant installs + # resolve the trace's tenant; a single-tenant dashboard owns everything + # itself, so there is nothing to attribute to. + def owner_for_trace + resolver = ActiveAgent::Dashboard.trace_owner_resolver + return resolver.call(@trace) if resolver + + ActiveAgent::Dashboard.multi_tenant? ? @trace.try(:account) : nil + end + + def find_or_create_agent(owner) + agents = Agent.for_owner(owner) + existing = agents.find_by( + service_name: @trace.service_name, + agent_class_name: agent_class, + action_name: action_name + ) + return existing if existing + + return if agents.observed_agents.count >= MAX_OBSERVED_PER_OWNER + + create_observed_agent(owner) + rescue ActiveRecord::RecordNotUnique + # Expected when concurrent ingest registers the same agent twice: the other + # writer won, so adopt its record. If no such record exists the collision + # was something else (a slug clash, say) and must not be swallowed. + agents.find_by( + service_name: @trace.service_name, + agent_class_name: agent_class, + action_name: action_name + ) || raise + end + + def create_observed_agent(owner) + agent = Agent.new( + name: display_name, + slug: observed_slug(owner), + status: :observed, + service_name: @trace.service_name, + agent_class_name: agent_class, + action_name: action_name, + source: @trace.sdk_info&.dig("name"), + provider: llm_attribute("llm.provider") || "openai", + model: llm_attribute("llm.model") || "unknown", + instructions: llm_attribute("llm.instructions").to_s, + tools: observed_tools, + first_observed_at: @trace.timestamp, + last_observed_at: @trace.timestamp + ) + agent.owner = owner + agent.save! + agent + end + + # Slugs are unique per owner, but a host app may have grown a global + # unique index instead, and two tenants observing the same agent in the + # same service would then collide — the second silently failing to + # register. Check globally and suffix until free; it costs one query. + def observed_slug(_owner) + base = [ @trace.service_name, agent_class, action_name ].compact.join("-").parameterize + return base unless Agent.exists?(slug: base) + + 5.times do + candidate = "#{base}-#{SecureRandom.hex(3)}" + return candidate unless Agent.exists?(slug: candidate) + end + + "#{base}-#{SecureRandom.uuid}" + end + + # Config we can only learn by watching: the model actually used, the + # instructions actually sent (when content capture is on), and the tools + # actually called — which is how Clara.respond acquires a tool list while + # Clara.title correctly stays empty. + def llm_attribute(key) + spans.filter_map { |span| span.dig("attributes", key).presence }.first + end + + # The roster the agent was OFFERED (ActiveAgent puts it on the prompt span + # as prompt.input.tools), falling back to the tools actually CALLED for + # SDKs without roster capture. A tool the agent never happens to call is + # still part of its configuration. + def observed_tools + offered = begin + JSON.parse(llm_attribute("prompt.input.tools").to_s) + rescue JSON::ParserError + [] + end + names = Array(offered).filter_map { |tool| tool.is_a?(Hash) ? tool["name"].presence : nil } + names.presence || spans.filter_map { |span| span.dig("attributes", "tool.name").presence }.uniq + end + + def spans + @spans ||= Array(@trace.spans) + end + + # Config is learned progressively. An agent first seen before content + # capture was enabled registers with no instructions; the trace that finally + # carries them should fill that in rather than leave the record permanently + # blank. Only fills gaps — an operator's edits are never overwritten. + def touch_observation(agent) + updates = {} + + if agent.last_observed_at.blank? || agent.last_observed_at < @trace.timestamp + updates[:last_observed_at] = @trace.timestamp + end + + if agent.instructions.blank? && (instructions = llm_attribute("llm.instructions")).present? + updates[:instructions] = instructions + end + + # Union, not gap-fill: later traces legitimately grow the roster (new + # tools ship). Names are only ever added, so operator edits survive. + if observed_tools.any? + merged = Array(agent.tools) | observed_tools + updates[:tools] = merged if merged != Array(agent.tools) + end + + if agent.model.blank? || agent.model == "unknown" + model = llm_attribute("llm.model") + updates[:model] = model if model.present? + end + + return if updates.empty? + + agent.update_columns(updates.merge(updated_at: Time.current)) + end + end + end +end diff --git a/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_scorecard.rb b/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_scorecard.rb new file mode 100644 index 00000000..ed4cf1cf --- /dev/null +++ b/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_scorecard.rb @@ -0,0 +1,209 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Per-agent scorecard stats for the dashboard's agent cards, computed with + # grouped queries (no per-agent N+1) over two sources: + # + # * agent_runs — executions the platform itself ran, and + # * active_agent_telemetry_traces — executions reported by an SDK in the + # customer's own app, attributed to an Agent by AgentRegistrar. + # + # Agents discovered by observation (status: :observed) only ever have the + # second kind, so a runs-only scorecard reported 0 for every tile while the + # Traces view showed real traffic. + # + # A platform run writes BOTH an AgentRun and a trace sharing a trace_id, so + # traces are counted only when no AgentRun claims the same trace_id — + # otherwise every platform execution would count twice. + class AgentScorecard + WINDOW = 30.days + + # @param agents [Enumerable] + # @return [Hash{Integer => Hash}] agent_id => stats + def self.for_agents(agents) + ids = agents.map(&:id) + return {} if ids.empty? + + window_start = WINDOW.ago + windowed = AgentRun.where(agent_id: ids, created_at: window_start..) + + run_counts = windowed.group(:agent_id).count + completed_counts = windowed.where(status: :complete).group(:agent_id).count + avg_durations = windowed.where.not(duration_ms: nil).group(:agent_id).average(:duration_ms) + token_sums = windowed.group(:agent_id).sum("COALESCE(total_tokens, 0)") + last_runs = AgentRun.where(agent_id: ids).group(:agent_id).maximum(:created_at) + eval_runs = latest_evaluation_runs(ids) + + trace_stats = telemetry_stats(ids, window_start) + trace_last = unclaimed_traces(ids, nil).group(:agent_id).maximum(:timestamp) + costs = estimated_costs(ids, windowed, window_start) + + ids.index_with do |id| + runs = run_counts[id].to_i + stats = trace_stats[id] || {} + traced = stats[:count].to_i + total = runs + traced + succeeded = completed_counts[id].to_i + stats[:ok].to_i + eval_run = eval_runs[id] + + { + window_days: (WINDOW / 1.day).to_i, + runs: total, + # Which sources contributed, so the UI can say where numbers came from. + run_sources: run_sources(runs, traced), + success_rate: total.positive? ? (succeeded * 100.0 / total).round(1) : nil, + avg_duration_ms: blended_duration(avg_durations[id], runs, stats[:avg_duration], traced), + tokens: token_sums[id].to_i + stats[:tokens].to_i, + cost: costs[id], + eval_score: eval_run&.average_score, + eval_samples_passed: eval_run&.samples_passed, + eval_samples_evaluated: eval_run&.samples_evaluated, + last_run_at: [ last_runs[id], trace_last[id] ].compact.max&.iso8601 + } + end + end + + # Count, success, latency and tokens for SDK-reported executions, in one + # grouped query. The success count uses SUM(CASE ...) rather than the + # tidier COUNT(*) FILTER so the same statement runs on SQLite and MySQL. + def self.telemetry_stats(agent_ids, window_start) + traces = ActiveAgent::Dashboard.trace_model.table_name + + rows = unclaimed_traces(agent_ids, window_start) + .group("#{traces}.agent_id") + .pluck( + Arel.sql("#{traces}.agent_id"), + Arel.sql("COUNT(*)"), + Arel.sql("SUM(CASE WHEN #{traces}.status <> 'ERROR' THEN 1 ELSE 0 END)"), + Arel.sql("AVG(#{traces}.total_duration_ms)"), + Arel.sql( + "SUM(COALESCE(total_input_tokens, 0) + COALESCE(total_output_tokens, 0) + " \ + "COALESCE(total_thinking_tokens, 0))" + ) + ) + + rows.to_h do |agent_id, count, ok, avg_duration, tokens| + [ agent_id, { count: count, ok: ok.to_i, avg_duration: avg_duration, tokens: tokens } ] + end + end + private_class_method :telemetry_stats + + # Estimated USD spend per agent over the window, across both sources. + # Rates are per model (ModelPricing), so the token columns are plucked + # alongside the model rather than summed in SQL — two queries, no payload + # loading: the trace's model lives in the spans jsonb and is extracted + # there, the run's in output_metadata. + def self.estimated_costs(agent_ids, windowed_runs, window_start) + costs = Hash.new(0.0) + priced = Set.new + + run_models(windowed_runs).each do |agent_id, model, input, output| + cost = ModelPricing.estimate(model: model, input_tokens: input, output_tokens: output) + next unless cost + + costs[agent_id] += cost + priced << agent_id + end + + trace_models(unclaimed_traces(agent_ids, window_start)).each do |agent_id, model, input, output| + cost = ModelPricing.estimate(model: model, input_tokens: input, output_tokens: output) + next unless cost + + costs[agent_id] += cost + priced << agent_id + end + + # nil, not 0.0, for agents with nothing to price — the card shows "—" + # rather than claiming a real $0.00 spend. + agent_ids.index_with { |id| priced.include?(id) ? costs[id].round(4) : nil } + end + private_class_method :estimated_costs + + # [agent_id, model, input_tokens, output_tokens] per run. The model + # lives in the output_metadata JSON, which only PostgreSQL can reach + # from SQL; elsewhere the column is read back in Ruby. + def self.run_models(runs) + if AgentRun.postgres? + runs.pluck(:agent_id, Arel.sql("output_metadata ->> 'model'"), :input_tokens, :output_tokens) + else + runs.pluck(:agent_id, :output_metadata, :input_tokens, :output_tokens).map do |agent_id, metadata, input, output| + [ agent_id, metadata.is_a?(Hash) ? metadata["model"] : nil, input, output ] + end + end + end + private_class_method :run_models + + # The same, for reported traces: the model is on the first llm span, + # dug out of the spans JSON. + def self.trace_models(traces) + table = ActiveAgent::Dashboard.trace_model.table_name + + if ActiveAgent::Dashboard.trace_model.postgres? + traces.pluck( + Arel.sql("#{table}.agent_id"), + Arel.sql( + "(SELECT s.value -> 'attributes' ->> 'llm.model' " \ + "FROM jsonb_array_elements(spans) AS s " \ + "WHERE s.value ->> 'type' = 'llm' LIMIT 1)" + ), + Arel.sql("total_input_tokens"), + Arel.sql("total_output_tokens") + ) + else + traces.pluck(Arel.sql("#{table}.agent_id"), :spans, :total_input_tokens, :total_output_tokens) + .map do |agent_id, spans, input, output| + llm = Array(spans).find { |span| span.is_a?(Hash) && span["type"].to_s == "llm" } + [ agent_id, llm&.dig("attributes", "llm.model"), input, output ] + end + end + end + private_class_method :trace_models + + # Traces attributed to these agents that no AgentRun already accounts for. + # window_start nil scans all time (used for "last activity"). Shared with + # AgentExecutions so the cards, the list and the counts never disagree. + def self.unclaimed_traces(agent_ids, window_start) + AgentExecutions.unclaimed_traces(agent_ids, since: window_start) + end + private_class_method :unclaimed_traces + + def self.run_sources(runs, traced) + sources = [] + sources << "platform" if runs.positive? + sources << "telemetry" if traced.positive? + sources + end + private_class_method :run_sources + + # Duration averages weight by how many executions each source contributed, + # so a blend of platform runs and traces isn't skewed by the smaller set. + def self.blended_duration(run_avg, runs, trace_avg, traced) + weighted = (run_avg.to_f * runs) + (trace_avg.to_f * traced) + counted = (run_avg ? runs : 0) + (trace_avg ? traced : 0) + return nil if counted.zero? + + (weighted / counted).round + end + private_class_method :blended_duration + + # Latest complete evaluation run per agent. DISTINCT ON would do this in + # one pass on PostgreSQL, but it has no portable equivalent, so the + # highest id per agent (runs are only ever appended) is selected first + # and those rows fetched by id. + def self.latest_evaluation_runs(agent_ids) + evaluations = Evaluation.table_name + runs = EvaluationRun.table_name + + newest = EvaluationRun.complete.joins(:evaluation) + .where(evaluations => { agent_id: agent_ids }) + .group("#{evaluations}.agent_id") + .pluck(Arel.sql("#{evaluations}.agent_id"), Arel.sql("MAX(#{runs}.id)")) + + by_run_id = newest.to_h { |agent_id, run_id| [ run_id, agent_id ] } + EvaluationRun.where(id: by_run_id.keys).index_by { |run| by_run_id[run.id] } + end + private_class_method :latest_evaluation_runs + end + end +end diff --git a/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_toolbox.rb b/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_toolbox.rb new file mode 100644 index 00000000..6c9c77a2 --- /dev/null +++ b/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_toolbox.rb @@ -0,0 +1,506 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + require "resolv" + + # Server-executable tools for platform generation runs, keyed by the tool + # names an Agent enables in the builder (Agent::AVAILABLE_TOOLS). + # + # AgentExecutionService exposes each supported tool to the provider as a + # function-calling schema and routes invocations here, so real runs produce + # tool-call roundtrips that show up as :tool spans in Traces, tool messages + # in Interactions, and tool counts in Metrics. + # + # Only tools with a safe server-side implementation are mapped; anything + # else an agent enables (terminal, playwright, ...) is ignored for platform + # execution. + class AgentToolbox + FETCH_LIMIT_BYTES = 50_000 + FETCH_TIMEOUT_SECONDS = 5 + + # agent tool name => function-calling tool definitions (common format). + DEFINITIONS = { + "fetch" => [ + { + name: "fetch_url", + description: "Fetch a public http(s) URL and return the beginning of its body as text. Use for reading web pages or APIs.", + parameters: { + type: "object", + properties: { + url: { type: "string", description: "Absolute http(s) URL to fetch" } + }, + required: [ "url" ] + } + } + ], + "playwright" => [ + { + name: "browse_page", + description: "Browse a page on the trusted documentation site (docs.activeagents.ai). Returns the page's readable text plus a links list of same-site paths with their link text. To follow (\"click\") a link, call browse_page again with its path. Start at \"/\" and navigate only via paths from the links list — do not invent paths.", + parameters: { + type: "object", + properties: { + url: { type: "string", description: "URL or path on docs.activeagents.ai" } + }, + required: [ "url" ] + } + } + ], + # A real browser via a Playwright MCP server (PlaywrightMcpClient). + # Stateful: navigate changes what snapshot/click see, so these bypass + # the toolbox result cache. + "playwright_mcp" => [ + { + name: "browser_navigate", + description: "Open a URL in the managed browser. Returns a text snapshot of the page with element refs.", + parameters: { + type: "object", + properties: { + url: { type: "string", description: "Absolute http(s) URL to open" } + }, + required: [ "url" ] + } + }, + { + name: "browser_snapshot", + description: "Accessibility snapshot of the current browser page: its readable structure, with element refs usable by browser_click.", + parameters: { type: "object", properties: {}, required: [] } + }, + { + name: "browser_click", + description: "Click an element from the latest snapshot by its ref (e.g. e12). Returns the updated page snapshot.", + parameters: { + type: "object", + properties: { + ref: { type: "string", description: "Element ref from the snapshot" }, + element: { type: "string", description: "Human-readable description of the element" } + }, + required: [ "ref" ] + } + } + ], + # Subject-bound like memory: routed by AgentExecutionService (needs the + # calling agent's account scope), so NOT in FUNCTIONS below. + "agents" => [ + { + name: "call_agent", + description: "Delegate a task to another agent in this workspace and return its reply. Use when another agent has tools, memory, or expertise this task needs.", + parameters: { + type: "object", + properties: { + slug: { type: "string", description: "The slug of the agent to call, e.g. local-qwen-assistant" }, + message: { type: "string", description: "The task or question for that agent" } + }, + required: [ "slug", "message" ] + } + } + ], + "search" => [ + { + name: "web_search", + description: "Search the web (DuckDuckGo instant answers) and return a short summary with related topics.", + parameters: { + type: "object", + properties: { + query: { type: "string", description: "The search query" } + }, + required: [ "query" ] + } + } + ], + "code" => [ + { + name: "calculate", + description: "Evaluate an arithmetic expression (+, -, *, /, %, parentheses) and return the numeric result.", + parameters: { + type: "object", + properties: { + expression: { type: "string", description: "Arithmetic expression, e.g. (2 + 3) * 4.5" } + }, + required: [ "expression" ] + } + } + ], + # Memory tools mirror solid_agent's HasMemory contract. They are NOT in + # FUNCTIONS below — execution is subject-bound, so AgentExecutionService + # routes them to the run's AgentMemory instead of this module. + "memory" => [ + { + name: "save_memory", + description: "Persist a short summary note to long-term memory. Use for facts, decisions, task outcomes, or anything a future agent or session should know. Keep each note self-contained.", + parameters: { + type: "object", + properties: { + content: { type: "string", description: "The summary note to remember" }, + category: { type: "string", description: "Optional label, e.g. fact, task, handoff" } + }, + required: [ "content" ] + } + }, + { + name: "recall_memory", + description: "Read back previously saved memory notes for the current subject, most recent first. Use before starting work to pick up prior context or another agent's handoff.", + parameters: { + type: "object", + properties: { + category: { type: "string", description: "Only return notes with this label" }, + limit: { type: "integer", description: "Maximum notes to return (default 20)" } + }, + required: [] + } + } + ] + }.freeze + + # Function name => implementation method, for routing tool calls. + FUNCTIONS = { + "fetch_url" => :fetch_url, + "web_search" => :web_search, + "calculate" => :calculate, + "browse_page" => :browse_page, + "browser_navigate" => :browser_navigate, + "browser_snapshot" => :browser_snapshot, + "browser_click" => :browser_click + }.freeze + + # Stateful tools whose results must never be replayed from cache. + UNCACHED_FUNCTIONS = %w[browser_navigate browser_snapshot browser_click].freeze + + # Hosts browse_page may fetch — the platform's own trusted docs. + BROWSE_ALLOWED_HOSTS = %w[docs.activeagents.ai].freeze + + class << self + # Tool definitions for the subset of an agent's enabled tools that have + # server-side implementations. + def definitions_for(tool_names) + Array(tool_names).flat_map { |name| DEFINITIONS[name.to_s] || [] } + end + + def function?(name) + FUNCTIONS.key?(name.to_s) + end + + # Executes a tool call. Returns a result hash; errors are returned as + # { error: ... } so the model can react instead of the run crashing. + # + # Results are cached by (tool, args) with a short TTL — repeated + # interactions replay the persisted result (tagged cached: true) + # instead of re-running the side effect. + def call(name, **kwargs) + return { error: "Unknown tool: #{name}" } unless function?(name) + return public_send(FUNCTIONS.fetch(name.to_s), **kwargs) if UNCACHED_FUNCTIONS.include?(name.to_s) + + cached_fetch(name, kwargs) do + public_send(FUNCTIONS.fetch(name.to_s), **kwargs) + end + rescue ArgumentError => e + { error: "Invalid arguments for #{name}: #{e.message}" } + rescue StandardError => e + Rails.logger.warn("[AgentToolbox] #{name} failed: #{e.class} - #{e.message}") + { error: "#{name} failed: #{e.message}" } + end + + MAX_REDIRECTS = 3 + + def fetch_url(url:) + uri = URI.parse(url.to_s) + return { error: "Only http(s) URLs are supported" } unless uri.is_a?(URI::HTTP) + return { error: "URL host is not allowed" } unless public_host?(uri.host) + + response = nil + (MAX_REDIRECTS + 1).times do + response = Net::HTTP.start( + uri.host, uri.port, + use_ssl: uri.scheme == "https", + open_timeout: FETCH_TIMEOUT_SECONDS, + read_timeout: FETCH_TIMEOUT_SECONDS + ) { |http| http.get(uri.request_uri.presence || "/", { "User-Agent" => "ActiveAgents-Toolbox/1.0" }) } + + break unless response.is_a?(Net::HTTPRedirection) && response["Location"].present? + + uri = URI.join(uri, response["Location"]) + return { error: "Only http(s) URLs are supported" } unless uri.is_a?(URI::HTTP) + return { error: "Redirected to a disallowed host" } unless public_host?(uri.host) + end + + body = response.body.to_s.byteslice(0, FETCH_LIMIT_BYTES).to_s.scrub + { + url: uri.to_s, + status: response.code.to_i, + content_type: response["Content-Type"], + body: body, + truncated: response.body.to_s.bytesize > FETCH_LIMIT_BYTES + } + rescue URI::InvalidURIError + { error: "Invalid URL" } + end + + def web_search(query:) + uri = URI("https://api.duckduckgo.com/?#{URI.encode_www_form(q: query.to_s, format: "json", no_html: 1, skip_disambig: 1)}") + response = Net::HTTP.start( + uri.host, uri.port, + use_ssl: true, open_timeout: FETCH_TIMEOUT_SECONDS, read_timeout: FETCH_TIMEOUT_SECONDS + ) { |http| http.get(uri.request_uri) } + data = JSON.parse(response.body) + + { + query: query, + abstract: data["AbstractText"].presence, + answer: data["Answer"].presence, + heading: data["Heading"].presence, + related_topics: Array(data["RelatedTopics"]).first(5).filter_map { |topic| topic["Text"] } + } + end + + def calculate(expression:) + { expression: expression, result: Calculator.evaluate(expression.to_s) } + rescue Calculator::Error => e + { error: e.message } + end + + # Trusted-docs browser: fetch_url restricted to BROWSE_ALLOWED_HOSTS, + # with HTML reduced to readable text so small models aren't drowned in + # markup. Accepts bare paths ("/docs/agents") against the docs host. + # Resolves a bare path ("/docs/agents") to an absolute URL on the + # trusted docs host, so the fetched URL is unambiguous everywhere it is + # recorded (spans, run events, persisted tool arguments). + def resolve_browse_url(url) + url = url.to_s + return url if url.match?(%r{\Ahttps?://}) + + "https://#{BROWSE_ALLOWED_HOSTS.first}#{url.start_with?('/') ? url : "/#{url}"}" + end + + PLAYWRIGHT_RESULT_LIMIT = 8_000 + + def browser_navigate(url:) + playwright_mcp("browser_navigate", { url: url }) + end + + def browser_snapshot + playwright_mcp("browser_snapshot", {}) + end + + def browser_click(ref:, element: nil) + playwright_mcp("browser_click", { ref: ref, element: element || ref }) + end + + SNAPSHOT_LINK = /\[Snapshot\]\(([^)]+)\)/ + + def playwright_mcp(tool, arguments, retried: false) + result = PlaywrightMcpClient.instance.call_tool(tool, arguments) + text = inline_snapshot(result[:text].to_s) + if text.length > PLAYWRIGHT_RESULT_LIMIT + text = "#{text[0, PLAYWRIGHT_RESULT_LIMIT]}\n…(truncated, #{text.length} chars total)" + end + result[:is_error] ? { error: text.presence || "browser tool failed" } : { text: text } + rescue PlaywrightMcpClient::Error => e + # One fresh-session retry: the first call after a server (re)start can + # race the browser launch. + unless retried + PlaywrightMcpClient.reset! + return playwright_mcp(tool, arguments, retried: true) + end + { error: e.message } + end + + # The MCP server saves page snapshots to files; when it runs beside the + # app with its output dir on a shared path, read them back so the model + # sees the page inline. + def inline_snapshot(text) + match = SNAPSHOT_LINK.match(text) + return text unless match + + file = Rails.root.join(".playwright-mcp", File.basename(match[1])) + return text unless File.exist?(file) + + "#{text}\n\n### Page snapshot\n#{File.read(file)}" + end + + def browse_page(url:) + url = resolve_browse_url(url) + host = URI.parse(url.to_s).host + unless BROWSE_ALLOWED_HOSTS.include?(host) + return { error: "browse_page is limited to trusted hosts: #{BROWSE_ALLOWED_HOSTS.join(', ')}" } + end + + result = fetch_url(url: url) + return result if result[:error] + + # A redirect may not stay on the trusted host — re-check after fetch. + final_host = URI.parse(result[:url].to_s).host + unless BROWSE_ALLOWED_HOSTS.include?(final_host) + return { error: "Page redirected off the trusted docs host (#{final_host})" } + end + + if result[:status] == 404 + return { + url: result[:url], status: 404, + error: "Page not found. Don't guess paths — browse '/' and follow the paths in the result's links list." + } + end + + links = extract_links(result[:body].to_s, result[:url]) + + text = result[:body].to_s + .gsub(%r{<(script|style)[^>]*>.*?}mi, " ") + .gsub(/<[^>]+>/, " ") + .then { |stripped| CGI.unescapeHTML(stripped) } + .gsub(/\s*\n\s*/, "\n") + .gsub(/[ \t]+/, " ") + .gsub(/\n{2,}/, "\n") + .strip + + { + url: result[:url], status: result[:status], + text: text.byteslice(0, 20_000).to_s.scrub, + links: links, + truncated: result[:truncated] || text.bytesize > 20_000 + } + rescue URI::InvalidURIError + { error: "Invalid URL" } + end + + BROWSE_LINK_LIMIT = 40 + + # Same-site links from the page HTML, so the model can navigate by + # following real paths instead of guessing them. "Clicking" a link is + # calling browse_page with its path. + def extract_links(html, base_url) + base = URI.parse(base_url.to_s) + links = html.scan(%r{]*href=["']([^"'\s]+)["'][^>]*>(.*?)}mi).filter_map do |href, inner| + next if href.start_with?("javascript:", "mailto:", "tel:", "data:", "#") + + resolved = begin + URI.join(base, href) + rescue URI::Error + next + end + next unless resolved.is_a?(URI::HTTP) && BROWSE_ALLOWED_HOSTS.include?(resolved.host) + + text = CGI.unescapeHTML(inner.gsub(/<[^>]+>/, " ")).gsub(/\s+/, " ").strip + entry = { path: resolved.request_uri } + entry[:text] = text.byteslice(0, 80).to_s.scrub if text.present? + entry + end + links.uniq { |link| link[:path] }.first(BROWSE_LINK_LIMIT) + rescue URI::Error + [] + end + + private + + CACHE_TTL = 5.minutes + + # Uses SolidAgent::ToolCache when the installed solid_agent provides it + # (it carries the canonical key scheme + error-skipping semantics); + # falls back to an equivalent Rails.cache fetch on older gem versions. + def cached_fetch(name, kwargs, &block) + if defined?(SolidAgent::ToolCache) + SolidAgent::ToolCache.fetch(tool: name.to_s, args: kwargs, ttl: CACHE_TTL, &block) + else + key = "solid_agent:tool_cache:#{name}:#{Digest::SHA256.hexdigest(kwargs.sort.to_h.to_json)}" + cached = Rails.cache.read(key) + return cached.merge(cached: true) unless cached.nil? + + result = block.call + unless result.respond_to?(:key?) && (result.key?(:error) || result.key?("error")) + Rails.cache.write(key, result, expires_in: CACHE_TTL) + end + result + end + end + + # SSRF guard for fetch_url: reject hosts that resolve to loopback, + # private, or link-local addresses. + def public_host?(host) + return false if host.blank? + + addresses = Resolv.getaddresses(host) + return false if addresses.empty? + + addresses.all? do |address| + ip = IPAddr.new(address) + !(ip.loopback? || ip.private? || ip.link_local?) + end + rescue IPAddr::InvalidAddressError, Resolv::ResolvError + false + end + end + + # Minimal recursive-descent arithmetic evaluator (no eval). + # Grammar: expr := term (('+'|'-') term)*; term := factor (('*'|'/'|'%') factor)*; + # factor := '-'? (number | '(' expr ')') + module Calculator + class Error < StandardError; end + + module_function + + def evaluate(expression) + tokens = tokenize(expression) + result, rest = parse_expr(tokens) + raise Error, "Unexpected input: #{rest.join(' ')}" unless rest.empty? + + result = result.to_f + result % 1 == 0 ? result.to_i : result.round(10) + end + + def tokenize(expression) + tokens = expression.scan(%r{\d+(?:\.\d+)?|[-+*/%()]|\S}) + invalid = tokens.grep_v(%r{\A(?:\d+(?:\.\d+)?|[-+*/%()])\z}) + raise Error, "Unsupported characters: #{invalid.uniq.join(' ')}" if invalid.any? + raise Error, "Empty expression" if tokens.empty? + + tokens + end + + def parse_expr(tokens) + value, tokens = parse_term(tokens) + while tokens.first == "+" || tokens.first == "-" + op = tokens.shift + rhs, tokens = parse_term(tokens) + value = op == "+" ? value + rhs : value - rhs + end + [ value, tokens ] + end + + def parse_term(tokens) + value, tokens = parse_factor(tokens) + while [ "*", "/", "%" ].include?(tokens.first) + op = tokens.shift + rhs, tokens = parse_factor(tokens) + raise Error, "Division by zero" if rhs.zero? && op != "*" + value = case op + when "*" then value * rhs + when "/" then value / rhs + else value % rhs + end + end + [ value, tokens ] + end + + def parse_factor(tokens) + raise Error, "Unexpected end of expression" if tokens.empty? + + if tokens.first == "-" + tokens.shift + value, tokens = parse_factor(tokens) + return [ -value, tokens ] + end + + token = tokens.shift + if token == "(" + value, tokens = parse_expr(tokens) + raise Error, "Missing closing parenthesis" unless tokens.shift == ")" + [ value, tokens ] + elsif token =~ /\A\d/ + [ token.include?(".") ? token.to_f : Rational(token), tokens ] + else + raise Error, "Unexpected token: #{token}" + end + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/services/active_agent/dashboard/evaluation_runner_service.rb b/lib/active_agent/dashboard/app/services/active_agent/dashboard/evaluation_runner_service.rb new file mode 100644 index 00000000..97c93b31 --- /dev/null +++ b/lib/active_agent/dashboard/app/services/active_agent/dashboard/evaluation_runner_service.rb @@ -0,0 +1,483 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Runs an Evaluation against the agent's most recent persisted generations + # (solid_agent's agent_generations). + # + # Rule-based criteria are scored deterministically from the recorded data. + # The llm_judge criterion asks a judge model (through the activeagent gem) + # to score each sample 0.0..1.0; it requires configured provider + # credentials and is skipped — never faked — when none are available. + class EvaluationRunnerService + PASS_THRESHOLD = 0.7 + + def self.call(evaluation) + new(evaluation).call + end + + def initialize(evaluation) + @evaluation = evaluation + end + + def call + run = @evaluation.evaluation_runs.create!(status: :running) + + # judge_defined evaluations author their KPI criteria on first run. + ensure_judge_defined_kpis! if @evaluation.judge_defined? + + sample_criteria, telemetry_criteria = @evaluation.criteria.partition do |criterion| + !Evaluation::TELEMETRY_CRITERION_TYPES.include?(criterion["type"]) + end + + if @evaluation.compare_models.any? + return score_comparison(run, sample_criteria, telemetry_criteria) + end + + samples = sample_criteria.any? ? sample_generations : [] + if sample_criteria.any? && samples.empty? + run.update!( + status: :failed, + error_message: "No generations to evaluate yet — run the agent first", + completed_at: Time.current + ) + return run + end + + scores = {} + per_sample_scores = Hash.new { |h, k| h[k] = [] } + + telemetry_criteria.each do |criterion| + scores[criterion["key"]] = score_telemetry_criterion(criterion) + end + + sample_criteria.each do |criterion| + stats = criterion_stats(criterion, samples, per_sample_scores) + scores[criterion["key"]] = stats + end + + run.update!( + status: :complete, + scores: scores, + samples_evaluated: samples.size, + samples_passed: passed_count(per_sample_scores), + completed_at: Time.current + ) + run + rescue StandardError => e + run&.update!(status: :failed, error_message: e.message, completed_at: Time.current) + raise + end + + private + + # Scores each sample-based criterion once per candidate model cohort and + # asks the judge for a comparative verdict — "haiku vs qwen, judged". + def score_comparison(run, sample_criteria, telemetry_criteria) + cohorts = @evaluation.compare_models.index_with { |model| sample_generations(model: model) } + active = cohorts.select { |_model, samples| samples.any? } + + if active.empty? + run.update!( + status: :failed, + error_message: "No generations recorded under #{@evaluation.compare_models.join(', ')} — run the agent under those models first", + completed_at: Time.current + ) + return run + end + + scores = {} + per_model_sample_scores = {} + + telemetry_criteria.each do |criterion| + scores[criterion["key"]] = score_telemetry_criterion(criterion) + end + + sample_criteria.each do |criterion| + scores[criterion["key"]] = active.each_with_object({}) do |(model, samples), by_model| + per_sample = (per_model_sample_scores[model] ||= Hash.new { |h, k| h[k] = [] }) + by_model[model] = criterion_stats(criterion, samples, per_sample) + end + end + + # Models that were requested but have no recorded generations are + # reported, not silently dropped. + missing = cohorts.keys - active.keys + scores["_missing_models"] = missing if missing.any? + + if active.size >= 2 && sample_criteria.any? + verdict = comparison_verdict(active, sample_criteria, scores) + scores["_verdict"] = verdict if verdict + end + + run.update!( + status: :complete, + scores: scores, + samples_evaluated: active.values.sum(&:size), + samples_passed: per_model_sample_scores.values.sum { |per_sample| passed_count(per_sample) }, + completed_at: Time.current + ) + run + end + + # Shared per-criterion scoring: returns the stats hash (or skipped) and + # appends each sample's score into per_sample_scores for pass counting. + def criterion_stats(criterion, samples, per_sample_scores) + sample_scores = samples.map { |generation| score_sample(criterion, generation) } + + # nil means the criterion could not be scored (e.g. llm_judge without + # provider credentials); it is reported as skipped, not zero. + scored = sample_scores.compact + return { "skipped" => true, "reason" => skip_reason(criterion) } if scored.empty? + + samples.each_with_index do |generation, index| + per_sample_scores[generation.id] << sample_scores[index] if sample_scores[index] + end + + { + "score" => (scored.sum / scored.size).round(3), + "min" => scored.min.round(3), + "max" => scored.max.round(3), + "passed" => scored.count { |s| s >= PASS_THRESHOLD }, + "total" => scored.size + } + end + + def passed_count(per_sample_scores) + per_sample_scores.count do |_id, values| + values.any? && values.all? { |s| s >= PASS_THRESHOLD } + end + end + + def sample_generations(model: nil) + scope = AgentGeneration + .joins(:agent_context) + .where(agent_contexts: { contextable: @evaluation.agent }) + scope = scope.where(model: model) if model + scope.order(created_at: :desc).limit(@evaluation.sample_size).to_a + end + + # --- Telemetry criteria --------------------------------------------------- + # + # Scored from the agent's telemetry traces over a config window — an + # aggregate per criterion, not per sample. min/max/passed/total mirror the + # aggregate so results render like sample-based criteria in the UI. + + def score_telemetry_criterion(criterion) + config = criterion["config"] || {} + window_hours = config.fetch("window_hours", 168).to_i.clamp(1, 720) + + # Multi-tenant installs need an owner to scope traces to; a + # single-tenant one reads every trace it has. + if ActiveAgent::Dashboard.multi_tenant? && owner.nil? + return { "skipped" => true, "reason" => "No tenant for telemetry lookup" } + end + + traces = telemetry_traces(window_hours) + total = traces.count + if total.zero? + return { + "skipped" => true, + "reason" => "No telemetry traces for #{@evaluation.agent.telemetry_agent_class} in the last #{window_hours}h" + } + end + + score, observed = case criterion["type"] + when "trace_error_rate" + max_rate = config.fetch("max_error_rate", 5.0).to_f + errors = traces.with_errors.count + rate = errors * 100.0 / total + value = if rate <= max_rate + 1.0 + elsif rate.zero? + 1.0 + else + max_rate.positive? ? (max_rate / rate).clamp(0.0, 1.0) : 0.0 + end + [ value, { "error_rate" => rate.round(2), "errors" => errors, "max_error_rate" => max_rate } ] + when "trace_latency" + budget = config.fetch("max_avg_ms", 5_000).to_f + avg = traces.average(:total_duration_ms).to_f + value = avg.zero? || avg <= budget ? 1.0 : (budget / avg).clamp(0.0, 1.0) + [ value, { "avg_duration_ms" => avg.round, "max_avg_ms" => budget } ] + end + + { + "score" => score.round(3), + "min" => score.round(3), + "max" => score.round(3), + "passed" => score >= PASS_THRESHOLD ? 1 : 0, + "total" => 1, + "source" => "telemetry", + "window_hours" => window_hours, + "traces" => total, + "observed" => observed + } + end + + def telemetry_traces(window_hours) + ActiveAgent::Dashboard.trace_model + .for_account(owner) + .for_agent(@evaluation.agent.telemetry_agent_class) + .for_date_range(window_hours.hours.ago, Time.current) + end + + # Returns 0.0..1.0, or nil when the criterion cannot be scored. + def score_sample(criterion, generation) + config = criterion["config"] || {} + + case criterion["type"] + when "response_present" + generation.content.present? ? 1.0 : 0.0 + when "min_length" + min = config.fetch("chars", 40).to_i + length = generation.content.to_s.length + [ length.to_f / min, 1.0 ].min + when "max_latency_ms" + budget = config.fetch("ms", 5_000).to_f + duration_ms = generation.duration_seconds.to_f * 1000 + return 1.0 if duration_ms.zero? # duration not recorded + duration_ms <= budget ? 1.0 : [ budget / duration_ms, 1.0 ].min + when "token_budget" + budget = config.fetch("output_tokens", 1_000).to_f + tokens = generation.output_tokens.to_f + tokens <= budget ? 1.0 : [ budget / tokens, 1.0 ].min + when "contains" + matches_pattern?(generation.content, config) ? 1.0 : 0.0 + when "not_contains" + matches_pattern?(generation.content, config) ? 0.0 : 1.0 + when "llm_judge" + llm_judge_score(criterion, generation) + end + end + + def matches_pattern?(content, config) + pattern = config["pattern"].to_s + return false if pattern.blank? + + content.to_s.match?(Regexp.new(pattern, Regexp::IGNORECASE)) + rescue RegexpError + content.to_s.downcase.include?(pattern.downcase) + end + + # --- Judge-defined KPIs -------------------------------------------------- + # + # The judge reads the agent's goals (its instructions) plus sample + # interactions and authors 3-6 measurable KPIs, persisted as llm_judge + # criteria with provenance. Stable persisted KPIs are what make scores + # comparable across evaluation runs and across models. + + KPI_LIMIT = 6 + + def ensure_judge_defined_kpis! + return if @evaluation.criteria.any? { |c| c["type"] == "llm_judge" && c["defined_by"].present? } + + unless judge_available? + raise "Judge-defined KPIs need provider credentials (add a provider API key in Settings)" + end + + response = judge_class.prompt( + message: kpi_definition_prompt, + instructions: "You define measurable evaluation KPIs for AI agents. Respond ONLY with JSON." + ).generate_now + + kpis = parse_kpis(response.message&.content) + raise "Judge returned no usable KPIs — try again or add criteria manually" if kpis.empty? + + judge_label = @evaluation.judge_model.presence || judge_provider.to_s + defined_at = Time.current.iso8601 + kpi_criteria = kpis.first(KPI_LIMIT).map do |kpi| + prompt_text = [ + kpi["description"].to_s, + kpi["scoring_guidance"].presence && "Scoring guidance: #{kpi['scoring_guidance']}" + ].compact.join("\n") + { + "key" => kpi["key"].to_s.parameterize(separator: "_").presence || kpi["description"].to_s.parameterize(separator: "_").first(40), + "type" => "llm_judge", + "defined_by" => judge_label, + "defined_at" => defined_at, + "config" => { "prompt" => prompt_text, "description" => kpi["description"] } + } + end + + @evaluation.update!( + criteria: @evaluation.criteria + kpi_criteria, + config: @evaluation.config.merge( + "kpi_provenance" => { "judge" => judge_label, "defined_at" => defined_at } + ) + ) + end + + def kpi_definition_prompt + samples = @evaluation.agent.agent_runs.successful.recent.limit(5).map do |run| + "User: #{run.input_prompt.to_s.truncate(400)}\nAgent: #{run.output.to_s.truncate(600)}" + end + + <<~PROMPT + Define evaluation KPIs for this AI agent. + + The agent's system instructions (its goals): + --- + #{@evaluation.agent.instructions.to_s.truncate(2_000).presence || '(no instructions configured)'} + --- + + Sample interactions: + --- + #{samples.join("\n---\n").presence || '(no interactions recorded yet)'} + --- + + Define 3-#{KPI_LIMIT} measurable KPIs that capture whether the agent accomplishes its goals. + Each KPI must be scorable from a single interaction's output on a 0.0-1.0 scale. + Respond ONLY with JSON: + {"kpis": [{"key": "snake_case_id", "description": "what to measure", "scoring_guidance": "how to assign 0.0-1.0"}]} + PROMPT + end + + def parse_kpis(content) + json = content.to_s[/\{.*\}/m] + return [] unless json + + Array(JSON.parse(json)["kpis"]).select { |kpi| kpi.is_a?(Hash) && kpi["description"].present? } + rescue JSON::ParserError + [] + end + + # Comparative verdict across model cohorts: the judge sees each model's + # per-KPI mean scores and declares which best accomplishes the goals. + def comparison_verdict(active_cohorts, sample_criteria, scores) + return nil unless judge_available? + + lines = sample_criteria.map do |criterion| + key = criterion["key"] + cells = active_cohorts.keys.map do |model| + stats = scores.dig(key, model) + value = stats && stats["score"] + "#{model}=#{value.nil? ? 'skipped' : value} (#{stats&.dig('total') || 0} samples)" + end + "#{key}: #{cells.join(', ')}" + end + + response = judge_class.prompt( + message: <<~PROMPT, + An AI agent was evaluated under multiple models. Its goals: + --- + #{@evaluation.agent.instructions.to_s.truncate(1_000).presence || '(no instructions configured)'} + --- + + Per-KPI mean scores (0.0-1.0) per model: + #{lines.join("\n")} + + Which model best accomplishes the agent's goals? + Respond ONLY with JSON: {"winner": "", "rationale": ""} + PROMPT + instructions: "You are an impartial evaluation judge comparing model cohorts. Respond ONLY with JSON." + ).generate_now + + json = response.message&.content.to_s[/\{.*\}/m] + verdict = json ? JSON.parse(json) : nil + return nil unless verdict.is_a?(Hash) && verdict["winner"].present? + + { + "winner" => verdict["winner"], + "rationale" => verdict["rationale"].to_s, + "judge" => @evaluation.judge_model.presence || judge_provider.to_s + } + rescue StandardError => e + Rails.logger.error("[EvaluationRunnerService] Verdict error: #{e.class} - #{e.message}") + nil + end + + # --- LLM judge ----------------------------------------------------------- + + def llm_judge_score(criterion, generation) + return nil unless judge_available? + return nil if generation.content.blank? + + response = judge_class.prompt( + message: judge_prompt(criterion, generation), + instructions: "You are an impartial evaluation judge. Respond ONLY with JSON: {\"score\": }" + ).generate_now + + parse_judge_score(response.message&.content) + rescue StandardError => e + Rails.logger.error("[EvaluationRunnerService] Judge error: #{e.class} - #{e.message}") + nil + end + + def judge_prompt(criterion, generation) + <<~PROMPT + Criterion: #{criterion.dig('config', 'prompt').presence || criterion['key'].to_s.humanize} + + Agent output to evaluate: + --- + #{generation.content.to_s.truncate(4_000)} + --- + + Score the output against the criterion from 0.0 (fails completely) to 1.0 (fully satisfies). + Respond only with JSON: {"score": } + PROMPT + end + + def parse_judge_score(content) + match = content.to_s.match(/"score"\s*:\s*(\d+(?:\.\d+)?)/) + return nil unless match + + match[1].to_f.clamp(0.0, 1.0) + end + + # The judge needs real provider credentials; scoring with the mock + # provider would fabricate results. + def judge_available? + judge_provider.present? + end + + def judge_provider + @judge_provider ||= + %i[anthropic openai openrouter].find do |name| + owner_provider_options(name).any? || global_provider_token?(name) + end || (:ollama if owner_provider_options(:ollama).any?) + end + + def global_provider_token?(name) + config = ActiveAgent.configuration[name] + config.respond_to?(:[]) && config[:access_token].present? + end + + # The evaluated agent owner's credential for +name+ (Settings -> + # Provider API Keys, or whatever the host app resolves); preferred over + # the host's config/active_agent.yml credentials for the judge. + def owner_provider_options(name) + @owner_provider_options ||= {} + @owner_provider_options[name.to_s] ||= begin + from_host = ActiveAgent::Dashboard.provider_credentials(owner, name.to_s) + from_host.presence || ProviderKey.for_owner(owner).find_by(provider: name.to_s)&.generation_options || {} + end + end + + def owner + @owner ||= @evaluation.agent.owner + end + + def judge_class + provider = judge_provider + model = @evaluation.judge_model.presence + options = {} + options[:model] = model if model + options.merge!(owner_provider_options(provider)) + + @judge_class ||= Class.new(ActiveAgent::Base) do + define_singleton_method(:name) { "EvaluationJudgeAgent" } + generate_with provider, **options + end + end + + def skip_reason(criterion) + if criterion["type"] == "llm_judge" + "LLM judge requires provider credentials (add a provider API key in Settings or set ANTHROPIC_API_KEY / OPENAI_API_KEY)" + else + "No scorable samples" + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/services/active_agent/dashboard/mcp_recording_middleware.rb b/lib/active_agent/dashboard/app/services/active_agent/dashboard/mcp_recording_middleware.rb new file mode 100644 index 00000000..e5c1c374 --- /dev/null +++ b/lib/active_agent/dashboard/app/services/active_agent/dashboard/mcp_recording_middleware.rb @@ -0,0 +1,243 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Middleware for recording MCP tool calls during browser automation + # + # This middleware intercepts Playwright MCP tool calls and records them + # to a SessionRecording for playback and debugging. + # + # Usage: + # middleware = MCPRecordingMiddleware.new(session_recording: recording) + # + # # Process a tool call + # result = middleware.intercept(tool_call) do + # # Execute the actual MCP tool + # mcp_client.call_tool(tool_call) + # end + # + class MCPRecordingMiddleware + # Map of Playwright MCP tool names to our action types + PLAYWRIGHT_TOOLS = { + "browser_navigate" => "navigate", + "browser_click" => "click", + "browser_type" => "type", + "browser_fill_form" => "form_fill", + "browser_press_key" => "key_press", + "browser_snapshot" => "snapshot", + "browser_take_screenshot" => "snapshot", + "browser_hover" => "hover", + "browser_select_option" => "select", + "browser_file_upload" => "file_upload", + "browser_handle_dialog" => "dialog", + "browser_evaluate" => "evaluate", + "browser_wait_for" => "wait", + "browser_drag" => "drag", + "browser_scroll" => "scroll" + }.freeze + + attr_reader :recording_service + + def initialize(session_recording: nil, sandbox_session: nil, agent_run: nil) + @recording = session_recording + @sandbox_session = sandbox_session + @agent_run = agent_run + + # Create or find recording if not provided + @recording ||= find_or_create_recording + @recording_service = SessionRecordingService.new(@recording) if @recording + end + + # Intercept an MCP tool call and record it + def intercept(tool_name:, parameters:) + action_type = PLAYWRIGHT_TOOLS[tool_name] + + # If not a Playwright tool, just execute and return + unless action_type + return yield if block_given? + return nil + end + + # Execute the tool + result = nil + screenshot = nil + error = nil + + begin + result = yield if block_given? + + # Extract screenshot from result if present + screenshot = extract_screenshot(result) + rescue => e + error = e.message + raise + ensure + # Record the action + record_action(action_type, tool_name, parameters, result, screenshot, error) + end + + result + end + + # Convenience method for recording a navigation + def record_navigate(url:, screenshot: nil) + @recording_service&.navigate(url: url, screenshot: screenshot) + end + + # Convenience method for recording a click + def record_click(selector:, screenshot: nil, element_description: nil) + @recording_service&.click( + selector: selector, + screenshot: screenshot, + metadata: { element: element_description }.compact + ) + end + + # Convenience method for recording text input + def record_type(selector:, text:, screenshot: nil) + @recording_service&.type(selector: selector, text: text, screenshot: screenshot) + end + + # Record current page state for handoff + def capture_for_handoff(url:, cookies: nil, local_storage: nil, form_values: nil) + @recording_service&.capture_handoff_state( + url: url, + cookies: cookies, + local_storage: local_storage, + form_values: form_values + ) + end + + # Complete the recording + def complete! + @recording_service&.complete! + end + + # Fail the recording + def fail!(error_message = nil) + @recording_service&.fail!(error_message) + end + + private + + def find_or_create_recording + if @sandbox_session + SessionRecording.recording.find_by(sandbox_session: @sandbox_session) || + SessionRecording.start!(sandbox_session: @sandbox_session) + elsif @agent_run + SessionRecording.recording.find_by(agent_run: @agent_run) || + SessionRecording.start!(agent_run: @agent_run) + end + end + + def record_action(action_type, tool_name, parameters, result, screenshot, error) + return unless @recording_service + + metadata = { + tool_name: tool_name, + parameters: parameters, + error: error + }.compact + + case action_type + when "navigate" + @recording_service.navigate( + url: parameters["url"], + screenshot: screenshot + ) + when "click" + @recording_service.click( + selector: parameters["ref"] || parameters["selector"], + screenshot: screenshot, + metadata: metadata.merge( + element: parameters["element"], + button: parameters["button"] + ).compact + ) + when "type" + @recording_service.type( + selector: parameters["ref"] || parameters["selector"], + text: parameters["text"], + screenshot: screenshot, + metadata: metadata + ) + when "form_fill" + @recording_service.fill_form( + fields: parameters["fields"] || [], + screenshot: screenshot + ) + when "key_press" + @recording_service.key_press( + key: parameters["key"], + screenshot: screenshot + ) + when "snapshot" + # For snapshots, extract DOM if available + dom = extract_dom(result) + @recording_service.capture_snapshot( + screenshot: screenshot, + dom: dom, + full_page: parameters["fullPage"] + ) + when "hover" + @recording_service.hover( + selector: parameters["ref"] || parameters["selector"], + screenshot: screenshot + ) + when "select" + @recording_service.select_option( + selector: parameters["ref"] || parameters["selector"], + values: parameters["values"] || [], + screenshot: screenshot + ) + when "file_upload" + @recording_service.file_upload( + paths: parameters["paths"] || [], + screenshot: screenshot + ) + when "dialog" + @recording_service.dialog( + accept: parameters["accept"], + text: parameters["promptText"], + screenshot: screenshot + ) + when "evaluate" + @recording_service.evaluate( + function: parameters["function"], + result: result, + screenshot: screenshot + ) + else + # Generic recording for other actions + @recording&.record_action!( + action_type: action_type, + selector: parameters["ref"] || parameters["selector"], + value: parameters.to_json, + screenshot: screenshot, + metadata: metadata + ) + end + end + + def extract_screenshot(result) + return nil unless result.is_a?(Hash) || result.respond_to?(:[]) + + # Try different paths where screenshot data might be + result["screenshot"] || + result[:screenshot] || + result.dig("data", "screenshot") || + result.dig(:data, :screenshot) + end + + def extract_dom(result) + return nil unless result.is_a?(Hash) || result.respond_to?(:[]) + + result["dom"] || + result[:dom] || + result["snapshot"] || + result[:snapshot] || + result.dig("data", "dom") + end + end + end +end diff --git a/lib/active_agent/dashboard/app/services/active_agent/dashboard/mock_sandbox_backend.rb b/lib/active_agent/dashboard/app/services/active_agent/dashboard/mock_sandbox_backend.rb new file mode 100644 index 00000000..43b056d5 --- /dev/null +++ b/lib/active_agent/dashboard/app/services/active_agent/dashboard/mock_sandbox_backend.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # In-memory sandbox backend. Ships with the engine so the sandbox surface + # is exercisable in development and tests without any container runtime; + # backends that talk to real infrastructure are registered by the host app + # (see ActiveAgent::Dashboard.sandbox_backends). + class MockSandboxBackend + def initialize + @sandboxes = {} + end + + def create_sandbox(session, instance_tier: nil) + tier = instance_tier || SandboxInstanceTier.free_tier + name = "mock-sandbox-#{SecureRandom.hex(4)}" + + @sandboxes[name] = { + container_name: name, + container_ip: "127.0.0.1", + url: "http://127.0.0.1:8080", + session_id: session.session_id, + status: "running", + instance_tier: tier.id, + resources: { + cpu_cores: tier.cpu_cores, + memory_gb: tier.memory_gb, + gpu: tier.gpu + }, + hourly_cost: tier.hourly_cost.to_f, + created_at: Time.current + } + @sandboxes[name] + end + + def status(sandbox_id) + @sandboxes[sandbox_id] || { status: "not_found" } + end + + def terminate(sandbox_id) + @sandboxes.delete(sandbox_id) + true + end + + def list_sandboxes + @sandboxes.values + end + + def cleanup_expired + 0 + end + end + end +end diff --git a/lib/active_agent/dashboard/app/services/active_agent/dashboard/playwright_mcp_client.rb b/lib/active_agent/dashboard/app/services/active_agent/dashboard/playwright_mcp_client.rb new file mode 100644 index 00000000..40e6152b --- /dev/null +++ b/lib/active_agent/dashboard/app/services/active_agent/dashboard/playwright_mcp_client.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + require "net/http" + require "resolv" + + # Minimal MCP client (streamable HTTP transport) for a Playwright MCP + # server — typically `npx @playwright/mcp --port 8931` running beside the + # app in development, or a sandbox-provisioned browser container in + # production. Speaks just enough JSON-RPC for tools/call: initialize once + # per process, then call tools under the session id the server hands back. + class PlaywrightMcpClient + DEFAULT_URL = ENV.fetch("PLAYWRIGHT_MCP_URL", "http://host.orb.internal:8931/mcp") + OPEN_TIMEOUT_SECONDS = 5 + READ_TIMEOUT_SECONDS = 60 + + class Error < StandardError; end + + def self.instance + @instance ||= new + end + + def self.reset! + @instance = nil + end + + def initialize(url: DEFAULT_URL) + @uri = URI(url) + @mutex = Mutex.new + end + + # Returns { text:, is_error: } — the tool result's text content. + def call_tool(name, arguments = {}) + Rails.logger.debug("[PlaywrightMcpClient] call #{name} args=#{arguments.inspect[0, 200]}") + ensure_session! + response = post( + { jsonrpc: "2.0", id: next_id, method: "tools/call", + params: { name: name, arguments: arguments } }, + session: @session_id + ) + result = response["result"] + unless result + Rails.logger.warn("[PlaywrightMcpClient] #{name} unexpected response: #{response.inspect[0, 500]}") + raise Error, (response.dig("error", "message") || "empty MCP response") + end + + text = Array(result["content"]).filter_map { |block| block["text"] }.join("\n") + { text: text, is_error: result["isError"] ? true : false } + rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout, SocketError => e + self.class.reset! + raise Error, "Playwright MCP server unreachable at #{@uri} (#{e.class}): start it with `npx @playwright/mcp --port 8931`" + end + + private + + def ensure_session! + @mutex.synchronize do + next if @session_id + + _body, response = post_raw( + { jsonrpc: "2.0", id: next_id, method: "initialize", + params: { protocolVersion: "2025-03-26", capabilities: {}, + clientInfo: { name: "activeagents", version: "1.0" } } } + ) + @session_id = response["mcp-session-id"] + raise Error, "MCP server did not return a session id" unless @session_id + + post({ jsonrpc: "2.0", method: "notifications/initialized" }, session: @session_id) + end + end + + def post(payload, session: nil) + body, _response = post_raw(payload, session: session) + body + end + + def post_raw(payload, session: nil) + # Tool calls run inside the provider SDK's streaming enumerator — a + # fiber, where Net::HTTP reads of SSE bodies misbehave (headers arrive, + # body comes back empty). A dedicated thread always does real blocking + # IO outside any fiber/scheduler context. + Thread.new { blocking_post_raw(payload, session: session) }.value + end + + def blocking_post_raw(payload, session: nil) + http = Net::HTTP.new(@uri.host, @uri.port) + # Container->host bridge hostnames (host.orb.internal) publish an IPv6 + # address whose path doesn't reach the server; dual-stack connects then + # fail intermittently. Pin to IPv4 while keeping the Host header. + if (ipv4 = ipv4_address) + http.ipaddr = ipv4 + end + http.open_timeout = OPEN_TIMEOUT_SECONDS + http.read_timeout = READ_TIMEOUT_SECONDS + request = Net::HTTP::Post.new(@uri.request_uri) + request["Content-Type"] = "application/json" + request["Accept"] = "application/json, text/event-stream" + request["Mcp-Session-Id"] = session if session + request.body = payload.to_json + + response = http.request(request) + Rails.logger.debug( + "[PlaywrightMcpClient] #{payload[:method]} -> #{response.code} " \ + "ct=#{response['Content-Type']} bytes=#{response.body.to_s.bytesize} session=#{session ? 'yes' : 'no'}" + ) + unless response.code.to_i.between?(200, 299) + Rails.logger.warn("[PlaywrightMcpClient] HTTP #{response.code}: #{response.body.to_s[0, 300]}") + raise Error, "MCP server returned HTTP #{response.code}" + end + + parsed = parse_body(response) + if parsed.empty? && payload[:id] + Rails.logger.warn("[PlaywrightMcpClient] unparsed body (#{response['Content-Type']}): #{response.body.to_s[0, 500]}") + end + [ parsed, response ] + end + + # Streamable HTTP answers as plain JSON or as an SSE stream whose data: + # lines carry the JSON-RPC response — accept both. + def parse_body(response) + body = response.body.to_s + return {} if body.empty? + + if response["Content-Type"].to_s.include?("text/event-stream") + body.lines + .select { |line| line.start_with?("data:") } + .filter_map { |line| JSON.parse(line.delete_prefix("data:").strip) rescue nil } + .find { |json| json["result"] || json["error"] } || {} + else + JSON.parse(body) + end + rescue JSON::ParserError + {} + end + + def ipv4_address + return @ipv4_address if defined?(@ipv4_address) + + @ipv4_address = Resolv.getaddresses(@uri.host).find { |address| address =~ Resolv::IPv4::Regex } + rescue Resolv::ResolvError + @ipv4_address = nil + end + + def next_id + @id = (@id || 0) + 1 + end + end + end +end diff --git a/lib/active_agent/dashboard/app/services/active_agent/dashboard/sandbox_orchestrator.rb b/lib/active_agent/dashboard/app/services/active_agent/dashboard/sandbox_orchestrator.rb new file mode 100644 index 00000000..81e1a4e9 --- /dev/null +++ b/lib/active_agent/dashboard/app/services/active_agent/dashboard/sandbox_orchestrator.rb @@ -0,0 +1,244 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # SandboxOrchestrator + # + # Unified interface for managing agent sandbox sessions. + # Supports multiple backends for cloud-agnostic deployment: + # + # - incus: Self-hosted Incus containers (any Linux host) + # - cloud_run: Google Cloud Run Jobs (serverless) + # - kubernetes: Kubernetes pods (GKE, EKS, self-hosted k8s) + # + # Configuration: + # Set SANDBOX_BACKEND environment variable to choose backend. + # Default: "incus" for simplicity + # + # Usage: + # orchestrator = SandboxOrchestrator.new + # result = orchestrator.create_sandbox(session) + # status = orchestrator.status(container_id) + # orchestrator.terminate(container_id) + # + class SandboxOrchestrator + # The engine ships only the in-memory backend. Anything that talks to + # real infrastructure (Incus, Kubernetes, Cloud Run) is registered by + # the app that operates it, so the engine carries none of those SDKs: + # + # ActiveAgent::Dashboard.sandbox_backends = { + # "cloud_run" => "CloudRunService" + # } + BUILT_IN_BACKENDS = { "mock" => "ActiveAgent::Dashboard::MockSandboxBackend" }.freeze + + # Backends disagree on what to call each verb. Candidates are tried in + # order and the first the backend responds to wins, so a host-registered + # class needs no adapter of its own. + ADAPTER_METHODS = { + create: %i[create_sandbox create_sandbox_pod create_sandbox_job], + status: %i[status container_status pod_status job_status], + terminate: %i[terminate terminate_pod cancel_job], + list: %i[list_sandboxes list_sandbox_pods list_jobs], + cleanup: %i[cleanup_expired cleanup_expired_pods cleanup_expired_jobs] + }.freeze + + class UnsupportedBackendError < StandardError; end + + # All backend names available in this install. + def self.backends + BUILT_IN_BACKENDS.merge(ActiveAgent::Dashboard.sandbox_backends.to_h.transform_keys(&:to_s)) + end + + # The backend used when none is named: whatever the host app configured + # as sandbox_service, falling back to the in-memory one. + def self.default_backend + name = ENV["SANDBOX_BACKEND"].presence || ActiveAgent::Dashboard.sandbox_service.to_s + backends.key?(name) ? name : "mock" + end + + def initialize(backend: nil) + @backend_name = (backend || self.class.default_backend).to_s + class_name = self.class.backends[@backend_name] + raise UnsupportedBackendError, "Unknown backend: #{@backend_name}" if class_name.nil? + + @backend = class_name.constantize.new + end + + attr_reader :backend_name + + # Create a new sandbox for the given session + # + # @param sandbox_session [SandboxSession] The session to create a sandbox for + # @param instance_tier [String, Symbol, SandboxInstanceTier] Optional instance tier + # @return [Hash] Sandbox details including ID/name and URL + def create_sandbox(sandbox_session, instance_tier: nil) + # Resolve tier + tier = resolve_tier(instance_tier) + + method = adapter_method(:create) + result = if accepts_instance_tier?(method) + @backend.public_send(method, sandbox_session, instance_tier: tier) + else + @backend.public_send(method, sandbox_session) + end + + # Normalize response format across backends + { + sandbox_id: result[:container_name] || result[:pod_name] || result[:job_name], + url: result[:url], + ip: result[:container_ip] || result[:pod_ip], + backend: @backend_name, + instance_tier: result[:instance_tier] || tier&.id, + resources: result[:resources], + hourly_cost: result[:hourly_cost] || tier&.hourly_cost&.to_f, + created_at: result[:created_at] || Time.current + } + end + + # List available instance tiers + # + # @param category [String, nil] Optional category filter (free, pro, enterprise) + # @return [Array] Available tiers + def available_tiers(category: nil) + tiers = SandboxInstanceTier.available + tiers = tiers.select { |t| t.category == category.to_s } if category + tiers + end + + # Get a specific instance tier + # + # @param tier_id [String, Symbol] Tier ID + # @return [SandboxInstanceTier] + def get_tier(tier_id) + SandboxInstanceTier.find(tier_id) + end + + # Get the status of a sandbox + # + # @param sandbox_id [String] The sandbox ID (container name, pod name, etc.) + # @return [Hash] Sandbox status + def status(sandbox_id) + @backend.public_send(adapter_method(:status), sandbox_id) + end + + # Terminate a sandbox + # + # @param sandbox_id [String] The sandbox ID to terminate + # @return [Boolean] true if terminated + def terminate(sandbox_id) + @backend.public_send(adapter_method(:terminate), sandbox_id) + end + + # List all active sandboxes + # + # @return [Array] List of sandbox statuses + def list_sandboxes + @backend.public_send(adapter_method(:list)) + end + + # Cleanup expired sandboxes + # + # @return [Integer] Number of sandboxes cleaned up + def cleanup_expired + @backend.public_send(adapter_method(:cleanup)) + end + + # Check if the backend is healthy + # + # @return [Boolean] true if backend is reachable + def healthy? + # A backend that can list is reachable; one that cannot is assumed + # healthy because there is nothing to probe. + list_sandboxes if ADAPTER_METHODS[:list].any? { |m| @backend.respond_to?(m) } + true + rescue => e + Rails.logger.error("Sandbox backend health check failed: #{e.message}") + false + end + + # Get backend-specific configuration info + # + # @return [Hash] Backend configuration + def backend_info + { + name: @backend_name, + class: @backend.class.name, + healthy: healthy?, + features: backend_features + } + end + + private + + # The backend's method for +verb+, or a clear error naming what it + # would have to implement. + def adapter_method(verb) + ADAPTER_METHODS.fetch(verb).find { |m| @backend.respond_to?(m) } || + raise(UnsupportedBackendError, + "#{@backend.class} implements none of #{ADAPTER_METHODS.fetch(verb).join(', ')}") + end + + def accepts_instance_tier?(method) + @backend.method(method).parameters.any? { |_type, name| name == :instance_tier } + end + + def resolve_tier(tier_param) + return nil unless tier_param + + if tier_param.is_a?(SandboxInstanceTier) + tier_param + else + SandboxInstanceTier.find(tier_param) + end + rescue ArgumentError + Rails.logger.warn("Unknown instance tier: #{tier_param}, using default") + SandboxInstanceTier.default_tier + end + + def backend_features + # A host-registered backend can describe itself; otherwise fall back + # to what the engine knows about the backends it has seen. + return @backend.features if @backend.respond_to?(:features) + + case @backend_name + when "incus" + { + cloud_agnostic: true, + isolation: "namespaces + apparmor", + networking: "bridge", + persistent_storage: true, + live_migration: true, + self_hosted: true + } + when "kubernetes" + { + cloud_agnostic: true, + isolation: "pods + gvisor", + networking: "cni + network_policies", + persistent_storage: true, + live_migration: false, + self_hosted: true + } + when "cloud_run" + { + cloud_agnostic: false, + isolation: "gvisor", + networking: "vpc_connector", + persistent_storage: false, + live_migration: false, + self_hosted: false + } + when "mock" + { + cloud_agnostic: true, + isolation: "none", + networking: "mock", + persistent_storage: false, + live_migration: false, + self_hosted: true + } + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/services/active_agent/dashboard/session_recording_service.rb b/lib/active_agent/dashboard/app/services/active_agent/dashboard/session_recording_service.rb new file mode 100644 index 00000000..2ea360e9 --- /dev/null +++ b/lib/active_agent/dashboard/app/services/active_agent/dashboard/session_recording_service.rb @@ -0,0 +1,230 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Service for managing browser session recordings + # Captures agent browser interactions for replay and handoff to human users + class SessionRecordingService + class RecordingError < StandardError; end + + attr_reader :recording + + def initialize(recording = nil) + @recording = recording + end + + # Start a new recording session + def self.start(agent_run: nil, sandbox_session: nil, name: nil) + recording = SessionRecording.start!( + agent_run: agent_run, + sandbox_session: sandbox_session, + name: name + ) + + new(recording) + end + + # Find or create a recording for a context + def self.for_context(agent_run: nil, sandbox_session: nil) + recording = SessionRecording.recording.find_by( + agent_run: agent_run, + sandbox_session: sandbox_session + ) + + recording ? new(recording) : start(agent_run: agent_run, sandbox_session: sandbox_session) + end + + # Record a browser navigation + def navigate(url:, screenshot: nil) + record_action( + action_type: "navigate", + value: url, + screenshot: screenshot, + metadata: { url: url } + ) + end + + # Record a click action + def click(selector:, screenshot: nil, metadata: {}) + record_action( + action_type: "click", + selector: selector, + screenshot: screenshot, + metadata: metadata.merge(element: selector) + ) + end + + # Record a type/input action + def type(selector:, text:, screenshot: nil, metadata: {}) + record_action( + action_type: "type", + selector: selector, + value: text, + screenshot: screenshot, + metadata: metadata + ) + end + + # Record a form fill (multiple fields) + def fill_form(fields:, screenshot: nil) + record_action( + action_type: "form_fill", + value: fields.to_json, + screenshot: screenshot, + metadata: { fields: fields, field_count: fields.size } + ) + end + + # Record a key press + def key_press(key:, screenshot: nil) + record_action( + action_type: "key_press", + value: key, + screenshot: screenshot + ) + end + + # Record a scroll action + def scroll(position:, screenshot: nil) + record_action( + action_type: "scroll", + metadata: { scroll_position: position }, + screenshot: screenshot + ) + end + + # Record a hover action + def hover(selector:, screenshot: nil) + record_action( + action_type: "hover", + selector: selector, + screenshot: screenshot + ) + end + + # Record a select option action + def select_option(selector:, values:, screenshot: nil) + record_action( + action_type: "select", + selector: selector, + value: values.join(", "), + screenshot: screenshot, + metadata: { values: values } + ) + end + + # Record a file upload + def file_upload(paths:, screenshot: nil) + record_action( + action_type: "file_upload", + value: paths.join(", "), + screenshot: screenshot, + metadata: { paths: paths } + ) + end + + # Record a dialog interaction + def dialog(accept:, text: nil, screenshot: nil) + record_action( + action_type: "dialog", + value: text, + screenshot: screenshot, + metadata: { accepted: accept, prompt_text: text } + ) + end + + # Record a JavaScript evaluation + def evaluate(function:, result: nil, screenshot: nil) + record_action( + action_type: "evaluate", + value: function, + screenshot: screenshot, + metadata: { result: result&.to_s&.truncate(1000) } + ) + end + + # Capture a manual snapshot (screenshot + DOM) + def capture_snapshot(screenshot: nil, dom: nil, full_page: false) + snapshot_type = full_page ? "full_page" : "snapshot" + + record_action( + action_type: "snapshot", + screenshot: screenshot, + dom_snapshot: dom, + metadata: { full_page: full_page } + ) + end + + # Capture browser state for handoff + def capture_handoff_state(url:, cookies: nil, local_storage: nil, session_storage: nil, form_values: nil) + @recording.update!( + metadata: @recording.metadata.merge( + handoff_state: { + url: url, + cookies: cookies, + local_storage: local_storage, + session_storage: session_storage, + form_values: form_values, + captured_at: Time.current.iso8601 + } + ) + ) + end + + # Complete the recording + def complete! + @recording.complete! + @recording + end + + # Mark recording as failed + def fail!(error_message = nil) + @recording.fail!(error_message) + @recording + end + + # Get the recording timeline for playback + def timeline + @recording.timeline + end + + # Get handoff state for resuming session + def handoff_state + @recording.metadata["handoff_state"] + end + + # Class method: Generate signed URL for stored snapshot + def self.signed_url_for(storage_key, expires_in: 15.minutes) + snapshot = RecordingSnapshot.find_by(storage_key: storage_key) + snapshot&.signed_url(expires_in: expires_in) + end + + # Class method: Fetch snapshot content + def self.fetch_snapshot(storage_key) + snapshot = RecordingSnapshot.find_by(storage_key: storage_key) + return nil unless snapshot&.file&.attached? + + snapshot.file.download + rescue StandardError => e + Rails.logger.error "Failed to fetch snapshot #{storage_key}: #{e.message}" + nil + end + + private + + def record_action(action_type:, selector: nil, value: nil, screenshot: nil, dom_snapshot: nil, metadata: {}) + raise RecordingError, "No active recording" unless @recording + raise RecordingError, "Recording already completed" unless @recording.recording? + + @recording.record_action!( + action_type: action_type, + selector: selector, + value: value, + screenshot: screenshot, + dom_snapshot: dom_snapshot, + metadata: metadata + ) + end + end + end +end diff --git a/lib/active_agent/dashboard/db/migrate/20260812000001_create_active_agent_dashboard_tables.rb b/lib/active_agent/dashboard/db/migrate/20260812000001_create_active_agent_dashboard_tables.rb new file mode 100644 index 00000000..6cad0289 --- /dev/null +++ b/lib/active_agent/dashboard/db/migrate/20260812000001_create_active_agent_dashboard_tables.rb @@ -0,0 +1,311 @@ +# frozen_string_literal: true + +# Creates every table the ActiveAgent Dashboard reads and writes, apart from +# active_agent_telemetry_traces (which ships separately so an app can install +# trace ingestion alone). +# +# Table names follow ActiveAgent::Dashboard.table_name_prefix, so an app that +# has deliberately set the prefix to "" gets unprefixed tables and one that +# leaves the default gets active_agent_*. +# +# JSON columns are jsonb on PostgreSQL and json elsewhere: the dashboard runs +# on SQLite and MySQL, it just loses jsonb containment (see +# ActiveAgent::Dashboard::Agent.with_tool, which falls back to a LIKE). +class CreateActiveAgentDashboardTables < ActiveRecord::Migration[7.2] + def change + prefix = ActiveAgent::Dashboard.table_name_prefix + + create_table "#{prefix}agents" do |t| + t.string :name, null: false + t.string :slug, null: false + t.text :description + t.string :provider, default: "openai" + t.string :model, default: "gpt-4o-mini" + t.text :instructions + t.string :preset_type + t.string :agent_class_name + t.string :action_name + t.integer :status, default: 0, null: false + t.column :appearance, json_type, default: {} + t.column :instruction_sets, json_type, default: [] + t.column :tools, json_type, default: [] + t.column :mcp_servers, json_type, default: [] + t.column :model_config, json_type, default: {} + t.column :response_format, json_type, default: {} + t.column :action_prompts, json_type, default: [], null: false + + # Agents discovered from reported telemetry rather than authored here. + t.string :service_name + t.string :source + t.datetime :first_observed_at + t.datetime :last_observed_at + + t.bigint :user_id + t.bigint :account_id + t.timestamps + t.index :user_id + t.index :account_id + t.index [ :user_id, :slug ], unique: true + t.index :status + end + + create_table "#{prefix}agent_versions" do |t| + t.bigint :agent_id, null: false + t.integer :version_number, default: 1, null: false + t.column :configuration_snapshot, json_type, default: {}, null: false + t.string :change_summary + t.string :created_by + t.timestamps + t.index [ :agent_id, :version_number ], unique: true + end + + create_table "#{prefix}agent_runs" do |t| + t.bigint :agent_id, null: false + t.string :trace_id + t.string :action_name + t.integer :status, default: 0, null: false + t.text :input_prompt + t.column :input_params, json_type, default: {} + t.text :output + t.column :output_metadata, json_type, default: {} + t.column :logs, json_type, default: [] + t.text :error_message + t.text :error_backtrace + t.integer :input_tokens + t.integer :output_tokens + t.integer :total_tokens + t.integer :duration_ms + t.datetime :started_at + t.datetime :completed_at + t.timestamps + t.index :agent_id + t.index :trace_id + t.index :created_at + end + + create_table "#{prefix}agent_templates" do |t| + t.string :name, null: false + t.string :slug, null: false + t.text :description + t.string :category + t.string :icon + t.string :provider, default: "openai" + t.string :model, default: "gpt-4o-mini" + t.text :instructions + t.string :preset_type + t.column :appearance, json_type, default: {} + t.column :instruction_sets, json_type, default: [] + t.column :tools, json_type, default: [] + t.column :mcp_servers, json_type, default: {} + t.column :model_config, json_type, default: {} + t.boolean :featured, default: false + t.boolean :free_tier, default: false + t.boolean :public, default: true + t.integer :usage_count, default: 0 + t.timestamps + t.index :slug, unique: true + end + + # Conversation persistence — the solid_agent schema, owned by the engine + # so the dashboard's Interactions view works without that gem installed. + create_table "#{prefix}agent_contexts" do |t| + t.string :agent_name, null: false + t.string :action_name, null: false + t.string :contextable_type + t.bigint :contextable_id + t.text :instructions + t.column :options, json_type, default: {} + t.string :trace_id + t.integer :total_input_tokens, default: 0 + t.integer :total_output_tokens, default: 0 + t.timestamps + t.index [ :contextable_type, :contextable_id ] + t.index :trace_id + end + + create_table "#{prefix}agent_messages" do |t| + t.bigint :agent_context_id, null: false + t.string :role, null: false + t.text :content + t.string :content_checksum + t.string :tool_call_id + t.string :tool_name + t.column :tool_arguments, json_type, default: {} + t.column :tool_result, json_type + t.column :attachments, json_type, default: [] + t.column :metadata, json_type, default: {} + t.column :provenance, json_type, default: {} + t.timestamps + t.index :agent_context_id + end + + create_table "#{prefix}agent_generations" do |t| + t.bigint :agent_context_id, null: false + t.text :content + t.string :model + t.string :provider + t.string :finish_reason + t.integer :input_tokens, default: 0 + t.integer :output_tokens, default: 0 + t.integer :cached_tokens, default: 0 + t.integer :reasoning_tokens, default: 0 + t.float :duration_seconds + t.column :tool_calls, json_type, default: [] + t.column :raw_response, json_type + t.column :provenance, json_type, default: {} + t.string :trace_id + t.timestamps + t.index :agent_context_id + t.index :trace_id + end + + create_table "#{prefix}agent_memories" do |t| + t.string :memorable_type + t.bigint :memorable_id + t.string :scope, default: "default", null: false + t.timestamps + t.index [ :memorable_type, :memorable_id, :scope ], unique: true, + name: "index_active_agent_memories_on_memorable_and_scope" + end + + create_table "#{prefix}agent_memory_entries" do |t| + t.bigint :agent_memory_id, null: false + t.text :content, null: false + t.string :source_agent + t.string :category + t.timestamps + t.index :agent_memory_id + end + + create_table "#{prefix}evaluations" do |t| + t.bigint :agent_id, null: false + t.string :name, null: false + t.string :judge_kind, default: "rules", null: false + t.string :judge_model + t.integer :sample_size, default: 20, null: false + t.column :criteria, json_type, default: [], null: false + t.column :config, json_type, default: {}, null: false + t.timestamps + t.index [ :agent_id, :name ], unique: true + end + + create_table "#{prefix}evaluation_runs" do |t| + t.bigint :evaluation_id, null: false + t.integer :status, default: 0, null: false + t.column :scores, json_type, default: {} + t.integer :samples_evaluated, default: 0 + t.integer :samples_passed, default: 0 + t.text :error_message + t.datetime :completed_at + t.timestamps + t.index :evaluation_id + end + + create_table "#{prefix}sandbox_sessions" do |t| + t.string :session_id, null: false + t.bigint :agent_template_id + t.integer :status, default: 0 + t.string :sandbox_type, default: "playwright_mcp" + t.string :cloud_run_job_id + t.string :cloud_run_url + t.column :runs, json_type, default: [] + t.integer :runs_count, default: 0 + t.integer :max_runs, default: 10 + t.integer :timeout_seconds, default: 300 + t.integer :total_duration_ms, default: 0 + t.integer :total_tokens, default: 0 + t.text :error_message + t.datetime :expires_at + t.datetime :last_activity_at + t.bigint :user_id + t.bigint :account_id + t.timestamps + t.index :session_id, unique: true + t.index :user_id + end + + create_table "#{prefix}sandbox_runs" do |t| + t.bigint :sandbox_session_id + t.text :task, null: false + t.integer :status, default: 0, null: false + t.text :result + t.text :error + t.column :screenshots, json_type, default: [] + t.integer :tokens_used, default: 0 + t.integer :duration_ms + t.datetime :started_at + t.datetime :completed_at + t.timestamps + t.index :sandbox_session_id + end + + create_table "#{prefix}session_recordings" do |t| + t.string :name + t.bigint :agent_run_id + t.bigint :sandbox_session_id + t.integer :status, default: 0, null: false + t.integer :action_count, default: 0 + t.integer :duration_ms + t.column :metadata, json_type, default: {} + t.timestamps + t.index :agent_run_id + t.index :sandbox_session_id + end + + create_table "#{prefix}recording_actions" do |t| + t.bigint :session_recording_id, null: false + t.integer :sequence, null: false + t.string :action_type, null: false + t.string :selector + t.text :value + t.integer :timestamp_ms, null: false + t.string :screenshot_key + t.string :dom_snapshot_key + t.column :metadata, json_type, default: {} + t.timestamps + t.index [ :session_recording_id, :sequence ], unique: true, + name: "index_active_agent_recording_actions_on_recording_and_sequence" + end + + create_table "#{prefix}recording_snapshots" do |t| + t.bigint :session_recording_id, null: false + t.bigint :recording_action_id + t.string :snapshot_type, null: false + t.string :storage_key, null: false + t.integer :width + t.integer :height + t.integer :file_size_bytes + t.timestamps + t.index :session_recording_id + t.index :recording_action_id + end + + create_table "#{prefix}api_keys" do |t| + t.string :name, null: false + t.string :token, null: false + t.string :token_prefix, null: false + t.datetime :last_used_at + t.bigint :user_id + t.bigint :account_id + t.timestamps + t.index :token, unique: true + end + + create_table "#{prefix}provider_keys" do |t| + t.string :provider, null: false + t.string :credential, null: false + t.bigint :user_id + t.bigint :account_id + t.timestamps + t.index [ :account_id, :provider ] + t.index [ :user_id, :provider ] + end + end + + private + + # jsonb where the adapter has it, json where it doesn't. + def json_type + @json_type ||= connection.adapter_name.to_s.downcase.include?("postgres") ? :jsonb : :json + end +end From 733d0f11cb5f356df01b119eb1670d503a1b8685 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 23:41:18 +0000 Subject: [PATCH 02/12] feat(dashboard): serve the whole dashboard from the mounted engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the JSON API and the React frontend to the engine, so mounting it gives you the surface the hosted product has rather than traces and metrics alone: agents and versions, runs, interactions, evaluations, templates, sandboxes, recordings, provider and API keys, and the agents-as-MCP-server endpoint. The controllers are the platform's, rewritten onto the engine's seams. Anything they reached for directly — the account, the signed-in user's agents, plan limits, usage counters, Stripe checkout — now goes through configuration, so the same controller serves a single-user install, a per-user install and a multi-tenant platform: - `owned(relation)` scopes by whatever the model declared with owned_by, rather than assuming who the owner is - quota_denial / record_usage / upgrade_url replace can_run_agent?, increment_agent_runs! and the checkout POST - execution_enabled? leaves a read-only observability dashboard when an operator would rather the mount not call providers at all The React app drops Inertia: initial state arrives as a JSON data attribute and the bundle ships prebuilt in the gem, so a host app mounts the dashboard without adopting a frontend framework or running a build. One fetch shim rewrites /api/... onto the mount path, which is what lets the engine live at any prefix. Action Cable is optional now — without a cable mount the views poll instead of failing. /api/traces keeps its meaning on both verbs: POST is still the SDK's token-authenticated ingest, GET is the dashboard's session-authenticated read (Api::TraceReportsController). Remaining PostgreSQL-only queries got portable forms (ILIKE, DISTINCT ON, jsonb traversal), and session recordings are scoped by a real owner column instead of a JSON metadata key. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fsu5fid9JgX97zFNkkQVNz --- .gitignore | 1 + activeagent.gemspec | 3 + lib/active_agent/dashboard.rb | 57 +- .../assets/builds/active_agent_dashboard.css | 2 + .../assets/builds/active_agent_dashboard.js | 163 ++ .../dashboard/api/agent_runs_controller.rb | 130 + .../dashboard/api/agents_controller.rb | 413 +++ .../dashboard/api/analytics_controller.rb | 96 + .../dashboard/api/api_keys_controller.rb | 47 + .../dashboard/api/base_controller.rb | 110 + .../dashboard/api/evaluations_controller.rb | 150 ++ .../api/instance_tiers_controller.rb | 108 + .../dashboard/api/interactions_controller.rb | 164 ++ .../dashboard/api/mcp_controller.rb | 220 ++ .../dashboard/api/metrics_controller.rb | 176 ++ .../dashboard/api/provider_keys_controller.rb | 54 + .../api/provider_models_controller.rb | 121 + .../dashboard/api/sandboxes_controller.rb | 218 ++ .../api/session_recordings_controller.rb | 380 +++ .../dashboard/api/templates_controller.rb | 93 + .../dashboard/api/trace_reports_controller.rb | 70 + .../dashboard/application_controller.rb | 8 + .../dashboard/dashboard_controller.rb | 133 +- .../models/active_agent/dashboard/agent.rb | 1 + .../models/active_agent/dashboard/api_key.rb | 1 + .../dashboard/application_record.rb | 28 +- .../active_agent/dashboard/provider_key.rb | 1 + .../active_agent/dashboard/sandbox_session.rb | 1 + .../dashboard/session_recording.rb | 3 + .../models/active_agent/telemetry_trace.rb | 22 + .../active_agent/dashboard/ownable.rb | 79 +- .../active_agent/dashboard/agent_scorecard.rb | 24 +- .../dashboard/dashboard/index.html.erb | 5 + .../active_agent/dashboard/react.html.erb | 19 + lib/active_agent/dashboard/config/routes.rb | 115 +- ...01_create_active_agent_dashboard_tables.rb | 4 + lib/active_agent/dashboard/engine.rb | 13 + .../frontend/components/AgentAvatar.jsx | 197 ++ .../components/dashboard/AgentAnalytics.jsx | 361 +++ .../components/dashboard/AgentBuilder.jsx | 456 ++++ .../components/dashboard/AgentEditor.jsx | 896 +++++++ .../dashboard/AgentInteractions.jsx | 824 ++++++ .../components/dashboard/AgentList.jsx | 320 +++ .../components/dashboard/AgentRunner.jsx | 551 ++++ .../components/dashboard/AgentStatCard.jsx | 188 ++ .../components/dashboard/ContextMeter.jsx | 206 ++ .../dashboard/DashboardAnalytics.jsx | 291 ++ .../components/dashboard/EvaluationsView.jsx | 559 ++++ .../frontend/components/dashboard/Header.jsx | 152 ++ .../dashboard/InstanceTierSelector.jsx | 364 +++ .../dashboard/InteractionStream.jsx | 481 ++++ .../components/dashboard/InteractionsView.jsx | 647 +++++ .../components/dashboard/Markdown.jsx | 103 + .../components/dashboard/MetricsView.jsx | 327 +++ .../components/dashboard/OrganizationView.jsx | 308 +++ .../components/dashboard/SandboxRunner.jsx | 1015 +++++++ .../dashboard/SessionReplayView.jsx | 994 +++++++ .../components/dashboard/SettingsView.jsx | 525 ++++ .../frontend/components/dashboard/Sidebar.jsx | 285 ++ .../components/dashboard/TemplateLibrary.jsx | 204 ++ .../dashboard/TimeWindowSelector.jsx | 52 + .../components/dashboard/ToolRoster.jsx | 111 + .../components/dashboard/TraceSpanPills.jsx | 124 + .../components/dashboard/TracesView.jsx | 2331 +++++++++++++++++ .../frontend/components/dashboard/index.js | 10 + .../frontend/contexts/ThemeContext.jsx | 46 + .../frontend/contexts/TimeWindowContext.jsx | 84 + .../dashboard/frontend/dashboard.css | 9 + .../frontend/hooks/useActionCable.js | 85 + lib/active_agent/dashboard/frontend/index.jsx | 48 + .../dashboard/frontend/package-lock.json | 2049 +++++++++++++++ .../dashboard/frontend/package.json | 21 + .../dashboard/frontend/pages/Dashboard.jsx | 467 ++++ .../dashboard/frontend/utils/checkout.js | 19 + .../frontend/utils/dashboardTheme.js | 22 + .../dashboard/frontend/utils/designTokens.js | 290 ++ .../frontend/utils/providerModels.js | 25 + test/dashboard/engine_integration_test.rb | 19 +- ...04_create_active_agent_dashboard_tables.rb | 315 +++ test/dummy/db/schema.rb | 331 ++- 80 files changed, 19765 insertions(+), 180 deletions(-) create mode 100644 lib/active_agent/dashboard/app/assets/builds/active_agent_dashboard.css create mode 100644 lib/active_agent/dashboard/app/assets/builds/active_agent_dashboard.js create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/agent_runs_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/agents_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/analytics_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/api_keys_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/base_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/evaluations_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/instance_tiers_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/interactions_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/mcp_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/metrics_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/provider_keys_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/provider_models_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/sandboxes_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/session_recordings_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/templates_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/trace_reports_controller.rb create mode 100644 lib/active_agent/dashboard/app/views/active_agent/dashboard/dashboard/index.html.erb create mode 100644 lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/react.html.erb create mode 100644 lib/active_agent/dashboard/frontend/components/AgentAvatar.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/AgentAnalytics.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/AgentBuilder.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/AgentEditor.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/AgentInteractions.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/AgentList.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/AgentRunner.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/AgentStatCard.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/ContextMeter.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/DashboardAnalytics.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/EvaluationsView.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/Header.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/InstanceTierSelector.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/InteractionStream.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/InteractionsView.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/Markdown.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/MetricsView.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/OrganizationView.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/SandboxRunner.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/SessionReplayView.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/SettingsView.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/Sidebar.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/TemplateLibrary.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/TimeWindowSelector.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/ToolRoster.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/TraceSpanPills.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/TracesView.jsx create mode 100644 lib/active_agent/dashboard/frontend/components/dashboard/index.js create mode 100644 lib/active_agent/dashboard/frontend/contexts/ThemeContext.jsx create mode 100644 lib/active_agent/dashboard/frontend/contexts/TimeWindowContext.jsx create mode 100644 lib/active_agent/dashboard/frontend/dashboard.css create mode 100644 lib/active_agent/dashboard/frontend/hooks/useActionCable.js create mode 100644 lib/active_agent/dashboard/frontend/index.jsx create mode 100644 lib/active_agent/dashboard/frontend/package-lock.json create mode 100644 lib/active_agent/dashboard/frontend/package.json create mode 100644 lib/active_agent/dashboard/frontend/pages/Dashboard.jsx create mode 100644 lib/active_agent/dashboard/frontend/utils/checkout.js create mode 100644 lib/active_agent/dashboard/frontend/utils/dashboardTheme.js create mode 100644 lib/active_agent/dashboard/frontend/utils/designTokens.js create mode 100644 lib/active_agent/dashboard/frontend/utils/providerModels.js create mode 100644 test/dummy/db/migrate/004_create_active_agent_dashboard_tables.rb diff --git a/.gitignore b/.gitignore index 0b289be6..4bc6239b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ /doc/ /log/*.log /node_modules/ +node_modules/ /pkg/ /test/dummy/db/*.sqlite3 /test/dummy/db/*.sqlite3-* diff --git a/activeagent.gemspec b/activeagent.gemspec index d66ed543..e328dada 100644 --- a/activeagent.gemspec +++ b/activeagent.gemspec @@ -7,7 +7,10 @@ Gem::Specification.new do |spec| spec.description = "The only agent-oriented AI framework designed for Rails, where Agents are Controllers. Build AI features with less complexity using the MVC conventions you love." spec.authors = [ "Justin Bowen" ] spec.email = "jusbowen@gmail.com" + # The dashboard's JS sources build into lib/active_agent/dashboard/app/assets/builds, + # which ships; its node_modules never does. spec.files = Dir["CHANGELOG.md", "README.md", "LICENSE", "lib/**/*"] + .reject { |f| f.include?("dashboard/frontend/node_modules/") } spec.require_paths = "lib" spec.homepage = "https://activeagents.ai" spec.license = "MIT" diff --git a/lib/active_agent/dashboard.rb b/lib/active_agent/dashboard.rb index 3f813ef7..50f8e38e 100644 --- a/lib/active_agent/dashboard.rb +++ b/lib/active_agent/dashboard.rb @@ -1,5 +1,30 @@ # frozen_string_literal: true +module ActiveAgent + module Dashboard + class << self + # Table name prefix for the engine's models. The engine's own + # migrations create `active_agent_*` tables, so the default matches. + # + # A host app that already owns these tables under different names (the + # activeagents.ai platform grew them unprefixed) sets this to "" rather + # than renaming production tables. The engine's migrations read the same + # value, so the schema and the models never disagree. + # + # Defined before the engine is required on purpose: Rails' isolate_namespace + # installs its own table_name_prefix on an on_load(:active_record) hook + # unless the module already has one, and that hook would win over any + # definition made afterwards. + attr_writer :table_name_prefix + + def table_name_prefix + global = defined?(::ActiveRecord::Base) ? ::ActiveRecord::Base.table_name_prefix : "" + "#{global}#{@table_name_prefix ||= "active_agent_"}" + end + end + end +end + require "active_agent/dashboard/engine" module ActiveAgent @@ -135,6 +160,18 @@ class << self # @return [Boolean] attr_accessor :execution_enabled + # Where the dashboard's upgrade CTAs should send people. Unset in a + # self-hosted install, where there is nothing to upgrade, and the CTAs + # say so instead of linking nowhere. + # @return [String, nil] + attr_accessor :upgrade_url + + # Called after the dashboard performs a metered action, as + # (owner, kind) — the counterpart to quota_checker, for host apps that + # track usage against a plan. Unset means nothing is counted. + # @return [Proc, nil] + attr_accessor :usage_recorder + # Maps an ingested trace to the owner that its newly observed agents # belong to. Defaults to the trace's account in multi-tenant mode and to # nobody in single-tenant mode. A host app whose agents hang off a @@ -164,15 +201,6 @@ class << self # @return [String, nil] attr_accessor :agent_polymorphic_name - # Table name prefix for the engine's models. The engine's own - # migrations create `active_agent_*` tables, so the default matches. - # - # A host app that already owns these tables under different names - # (the activeagents.ai platform grew them unprefixed) sets this to "" - # rather than renaming production tables. - # @return [String] - attr_accessor :table_name_prefix - # Returns whether multi-tenant mode is enabled. # # @return [Boolean] @@ -187,6 +215,15 @@ def execution_enabled? @execution_enabled != false end + # Tells the host app that +owner+ performed +kind+. Never raises: a + # bookkeeping failure must not fail the action that was already taken. + def record_usage(owner, kind) + usage_recorder&.call(owner, kind) + rescue StandardError => e + Rails.logger.warn("[ActiveAgent::Dashboard] usage recording failed: #{e.message}") + nil + end + # Asks the host app whether +owner+ may perform +kind+. # # @return [String, nil] denial message, or nil when allowed @@ -269,6 +306,8 @@ def reset! @encrypt_credentials = true @trace_retention = nil @trace_owner_resolver = nil + @usage_recorder = nil + @upgrade_url = nil end end diff --git a/lib/active_agent/dashboard/app/assets/builds/active_agent_dashboard.css b/lib/active_agent/dashboard/app/assets/builds/active_agent_dashboard.css new file mode 100644 index 00000000..5363af7a --- /dev/null +++ b/lib/active_agent/dashboard/app/assets/builds/active_agent_dashboard.css @@ -0,0 +1,2 @@ +/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */ +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-yellow-50:oklch(98.7% .026 102.212);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-300:oklch(90.5% .182 98.111);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-yellow-800:oklch(47.6% .114 61.907);--color-yellow-900:oklch(42.1% .095 57.708);--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-green-900:oklch(39.3% .095 152.535);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-teal-400:oklch(77.7% .152 181.912);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-700:oklch(45.7% .24 277.023);--color-purple-50:oklch(97.7% .014 308.299);--color-purple-100:oklch(94.6% .033 307.174);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-600:oklch(55.8% .288 302.321);--color-purple-700:oklch(49.6% .265 301.924);--color-rose-50:oklch(96.9% .015 12.422);--color-rose-100:oklch(94.1% .03 12.58);--color-rose-200:oklch(89.2% .058 10.001);--color-rose-300:oklch(81% .117 11.638);--color-rose-500:oklch(64.5% .246 16.439);--color-rose-600:oklch(58.6% .253 17.585);--color-rose-700:oklch(51.4% .222 16.935);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-4xl:56rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.collapse{visibility:collapse}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.-top-0\.5{top:calc(var(--spacing) * -.5)}.-top-2{top:calc(var(--spacing) * -2)}.-top-3{top:calc(var(--spacing) * -3)}.-top-8{top:calc(var(--spacing) * -8)}.top-2\.5{top:calc(var(--spacing) * 2.5)}.top-4{top:calc(var(--spacing) * 4)}.top-14{top:calc(var(--spacing) * 14)}.-right-2{right:calc(var(--spacing) * -2)}.right-0{right:0}.right-2{right:calc(var(--spacing) * 2)}.right-4{right:calc(var(--spacing) * 4)}.bottom-2{bottom:calc(var(--spacing) * 2)}.bottom-4{bottom:calc(var(--spacing) * 4)}.left-1\/2{left:50%}.left-3{left:calc(var(--spacing) * 3)}.left-4{left:calc(var(--spacing) * 4)}.z-10{z-index:10}.z-50{z-index:50}.col-span-2{grid-column:span 2/span 2}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.-mx-2{margin-inline:calc(var(--spacing) * -2)}.mx-1{margin-inline:var(--spacing)}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mx-auto{margin-inline:auto}.my-0\.5{margin-block:calc(var(--spacing) * .5)}.my-1{margin-block:var(--spacing)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mr-1{margin-right:var(--spacing)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.ml-1{margin-left:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-3{margin-left:calc(var(--spacing) * 3)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-0\.5{height:calc(var(--spacing) * .5)}.h-1{height:var(--spacing)}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-12{height:calc(var(--spacing) * 12)}.h-16{height:calc(var(--spacing) * 16)}.h-24{height:calc(var(--spacing) * 24)}.h-32{height:calc(var(--spacing) * 32)}.h-36{height:calc(var(--spacing) * 36)}.h-48{height:calc(var(--spacing) * 48)}.h-64{height:calc(var(--spacing) * 64)}.h-\[500px\]{height:500px}.h-full{height:100%}.max-h-44{max-height:calc(var(--spacing) * 44)}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-96{max-height:calc(var(--spacing) * 96)}.max-h-\[90vh\]{max-height:90vh}.max-h-full{max-height:100%}.min-h-screen{min-height:100vh}.w-1{width:var(--spacing)}.w-1\/3{width:33.3333%}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-10{width:calc(var(--spacing) * 10)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-32{width:calc(var(--spacing) * 32)}.w-48{width:calc(var(--spacing) * 48)}.w-64{width:calc(var(--spacing) * 64)}.w-80{width:calc(var(--spacing) * 80)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:0}.min-w-\[4\.5rem\]{min-width:4.5rem}.min-w-full{min-width:100%}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.origin-left{transform-origin:0}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-1{--tw-translate-x:var(--spacing);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-6{--tw-translate-x:calc(var(--spacing) * 6);translate:var(--tw-translate-x) var(--tw-translate-y)}.-rotate-45{rotate:-45deg}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-none{resize:none}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-8{gap:calc(var(--spacing) * 8)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-5{column-gap:calc(var(--spacing) * 5)}:where(.space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(var(--spacing) * var(--tw-space-x-reverse));margin-inline-end:calc(var(--spacing) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 2) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-6>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 6) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing) * 8) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-x-reverse)))}.gap-y-1{row-gap:var(--spacing)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-gray-100>:not(:last-child)){border-color:var(--color-gray-100)}:where(.divide-gray-200>:not(:last-child)){border-color:var(--color-gray-200)}.self-end{align-self:flex-end}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-amber-200{border-color:var(--color-amber-200)}.border-emerald-500{border-color:var(--color-emerald-500)}.border-gray-50{border-color:var(--color-gray-50)}.border-gray-100{border-color:var(--color-gray-100)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-700{border-color:var(--color-gray-700)}.border-green-200{border-color:var(--color-green-200)}.border-green-800{border-color:var(--color-green-800)}.border-red-200{border-color:var(--color-red-200)}.border-red-500{border-color:var(--color-red-500)}.border-red-800{border-color:var(--color-red-800)}.border-rose-200{border-color:var(--color-rose-200)}.border-rose-300{border-color:var(--color-rose-300)}.border-rose-500{border-color:var(--color-rose-500)}.border-transparent{border-color:#0000}.border-yellow-200{border-color:var(--color-yellow-200)}.border-yellow-800{border-color:var(--color-yellow-800)}.border-l-amber-500{border-left-color:var(--color-amber-500)}.border-l-blue-500{border-left-color:var(--color-blue-500)}.border-l-green-500{border-left-color:var(--color-green-500)}.border-l-purple-500{border-left-color:var(--color-purple-500)}.border-l-red-500{border-left-color:var(--color-red-500)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-400{background-color:var(--color-amber-400)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-black{background-color:var(--color-black)}.bg-black\/75{background-color:#000000bf}@supports (color:color-mix(in lab, red, red)){.bg-black\/75{background-color:color-mix(in oklab, var(--color-black) 75%, transparent)}}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-400{background-color:var(--color-blue-400)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-emerald-100{background-color:var(--color-emerald-100)}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-200{background-color:var(--color-gray-200)}.bg-gray-300{background-color:var(--color-gray-300)}.bg-gray-400{background-color:var(--color-gray-400)}.bg-gray-500{background-color:var(--color-gray-500)}.bg-gray-700{background-color:var(--color-gray-700)}.bg-gray-800{background-color:var(--color-gray-800)}.bg-gray-900{background-color:var(--color-gray-900)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-green-400{background-color:var(--color-green-400)}.bg-green-500{background-color:var(--color-green-500)}.bg-green-900\/20{background-color:#0d542b33}@supports (color:color-mix(in lab, red, red)){.bg-green-900\/20{background-color:color-mix(in oklab, var(--color-green-900) 20%, transparent)}}.bg-indigo-50{background-color:var(--color-indigo-50)}.bg-orange-100{background-color:var(--color-orange-100)}.bg-orange-500{background-color:var(--color-orange-500)}.bg-purple-50{background-color:var(--color-purple-50)}.bg-purple-100{background-color:var(--color-purple-100)}.bg-purple-500{background-color:var(--color-purple-500)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-400{background-color:var(--color-red-400)}.bg-red-500{background-color:var(--color-red-500)}.bg-red-900\/20{background-color:#82181a33}@supports (color:color-mix(in lab, red, red)){.bg-red-900\/20{background-color:color-mix(in oklab, var(--color-red-900) 20%, transparent)}}.bg-red-900\/40{background-color:#82181a66}@supports (color:color-mix(in lab, red, red)){.bg-red-900\/40{background-color:color-mix(in oklab, var(--color-red-900) 40%, transparent)}}.bg-rose-50{background-color:var(--color-rose-50)}.bg-rose-500{background-color:var(--color-rose-500)}.bg-teal-400{background-color:var(--color-teal-400)}.bg-white{background-color:var(--color-white)}.bg-yellow-50{background-color:var(--color-yellow-50)}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-yellow-500{background-color:var(--color-yellow-500)}.bg-yellow-900\/20{background-color:#733e0a33}@supports (color:color-mix(in lab, red, red)){.bg-yellow-900\/20{background-color:color-mix(in oklab, var(--color-yellow-900) 20%, transparent)}}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-r{--tw-gradient-position:to right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-gray-50{--tw-gradient-from:var(--color-gray-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-red-500{--tw-gradient-from:var(--color-red-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-rose-50{--tw-gradient-from:var(--color-rose-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.from-rose-500{--tw-gradient-from:var(--color-rose-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-gray-100{--tw-gradient-to:var(--color-gray-100);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-50{--tw-gradient-to:var(--color-purple-50);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-purple-600{--tw-gradient-to:var(--color-purple-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-rose-500{--tw-gradient-to:var(--color-rose-500);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.object-contain{object-fit:contain}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-5{padding-left:calc(var(--spacing) * 5)}.pl-10{padding-left:calc(var(--spacing) * 10)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.9em\]{font-size:.9em}.text-\[0\.85em\]{font-size:.85em}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-500{color:var(--color-amber-500)}.text-amber-600{color:var(--color-amber-600)}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-amber-900{color:var(--color-amber-900)}.text-blue-500{color:var(--color-blue-500)}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-emerald-500{color:var(--color-emerald-500)}.text-emerald-600{color:var(--color-emerald-600)}.text-emerald-700{color:var(--color-emerald-700)}.text-gray-100{color:var(--color-gray-100)}.text-gray-200{color:var(--color-gray-200)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-green-300{color:var(--color-green-300)}.text-green-400{color:var(--color-green-400)}.text-green-500{color:var(--color-green-500)}.text-green-600{color:var(--color-green-600)}.text-green-700{color:var(--color-green-700)}.text-green-800{color:var(--color-green-800)}.text-indigo-700{color:var(--color-indigo-700)}.text-orange-600{color:var(--color-orange-600)}.text-purple-700{color:var(--color-purple-700)}.text-red-300{color:var(--color-red-300)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-rose-100{color:var(--color-rose-100)}.text-rose-500{color:var(--color-rose-500)}.text-rose-600{color:var(--color-rose-600)}.text-white{color:var(--color-white)}.text-yellow-300{color:var(--color-yellow-300)}.text-yellow-600{color:var(--color-yellow-600)}.text-yellow-700{color:var(--color-yellow-700)}.text-yellow-800{color:var(--color-yellow-800)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.underline{text-decoration-line:underline}.placeholder-gray-500::placeholder{color:var(--color-gray-500)}.accent-red-500{accent-color:var(--color-red-500)}.opacity-0{opacity:0}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-emerald-200{--tw-ring-color:var(--color-emerald-200)}.ring-red-100{--tw-ring-color:var(--color-red-100)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}@media (hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media (hover:hover){.hover\:border-gray-300:hover{border-color:var(--color-gray-300)}.hover\:bg-amber-600:hover{background-color:var(--color-amber-600)}.hover\:bg-black\/5:hover{background-color:#0000000d}@supports (color:color-mix(in lab, red, red)){.hover\:bg-black\/5:hover{background-color:color-mix(in oklab, var(--color-black) 5%, transparent)}}.hover\:bg-emerald-600:hover{background-color:var(--color-emerald-600)}.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-200:hover{background-color:var(--color-gray-200)}.hover\:bg-gray-300:hover{background-color:var(--color-gray-300)}.hover\:bg-gray-600:hover{background-color:var(--color-gray-600)}.hover\:bg-gray-800:hover{background-color:var(--color-gray-800)}.hover\:bg-purple-600:hover{background-color:var(--color-purple-600)}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-red-100:hover{background-color:var(--color-red-100)}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:bg-red-900\/40:hover{background-color:#82181a66}@supports (color:color-mix(in lab, red, red)){.hover\:bg-red-900\/40:hover{background-color:color-mix(in oklab, var(--color-red-900) 40%, transparent)}}.hover\:bg-red-900\/60:hover{background-color:#82181a99}@supports (color:color-mix(in lab, red, red)){.hover\:bg-red-900\/60:hover{background-color:color-mix(in oklab, var(--color-red-900) 60%, transparent)}}.hover\:bg-rose-600:hover{background-color:var(--color-rose-600)}.hover\:bg-white:hover{background-color:var(--color-white)}.hover\:from-red-600:hover{--tw-gradient-from:var(--color-red-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.hover\:to-rose-600:hover{--tw-gradient-to:var(--color-rose-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.hover\:text-amber-900:hover{color:var(--color-amber-900)}.hover\:text-gray-200:hover{color:var(--color-gray-200)}.hover\:text-gray-300:hover{color:var(--color-gray-300)}.hover\:text-gray-600:hover{color:var(--color-gray-600)}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:text-gray-900:hover{color:var(--color-gray-900)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:text-red-700:hover{color:var(--color-red-700)}.hover\:text-rose-700:hover{color:var(--color-rose-700)}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.hover\:shadow-sm:hover{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.focus\:border-transparent:focus{border-color:#0000}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.focus\:ring-rose-500:focus{--tw-ring-color:var(--color-rose-500)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-100:disabled{background-color:var(--color-gray-100)}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:40rem){.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}}@media (min-width:48rem){.md\:col-span-2{grid-column:span 2/span 2}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}@media (min-width:64rem){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}@media (min-width:80rem){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}} \ No newline at end of file diff --git a/lib/active_agent/dashboard/app/assets/builds/active_agent_dashboard.js b/lib/active_agent/dashboard/app/assets/builds/active_agent_dashboard.js new file mode 100644 index 00000000..0bebda21 --- /dev/null +++ b/lib/active_agent/dashboard/app/assets/builds/active_agent_dashboard.js @@ -0,0 +1,163 @@ +var wj=Object.create;var jg=Object.defineProperty;var Sj=Object.getOwnPropertyDescriptor;var Aj=Object.getOwnPropertyNames;var Nj=Object.getPrototypeOf,_j=Object.prototype.hasOwnProperty;var kr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Cj=(e,t)=>{for(var r in t)jg(e,r,{get:t[r],enumerable:!0})},Ej=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Aj(t))!_j.call(e,a)&&a!==r&&jg(e,a,{get:()=>t[a],enumerable:!(n=Sj(t,a))||n.enumerable});return e};var te=(e,t,r)=>(r=e!=null?wj(Nj(e)):{},Ej(t||!e||!e.__esModule?jg(r,"default",{value:e,enumerable:!0}):r,e));var SA=kr(Be=>{"use strict";var Lg=Symbol.for("react.transitional.element"),kj=Symbol.for("react.portal"),Oj=Symbol.for("react.fragment"),Tj=Symbol.for("react.strict_mode"),Mj=Symbol.for("react.profiler"),Dj=Symbol.for("react.consumer"),Pj=Symbol.for("react.context"),jj=Symbol.for("react.forward_ref"),Ij=Symbol.for("react.suspense"),zj=Symbol.for("react.memo"),hA=Symbol.for("react.lazy"),Lj=Symbol.for("react.activity"),fA=Symbol.iterator;function Bj(e){return e===null||typeof e!="object"?null:(e=fA&&e[fA]||e["@@iterator"],typeof e=="function"?e:null)}var gA={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},yA=Object.assign,xA={};function Zl(e,t,r){this.props=e,this.context=t,this.refs=xA,this.updater=r||gA}Zl.prototype.isReactComponent={};Zl.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};Zl.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function bA(){}bA.prototype=Zl.prototype;function Bg(e,t,r){this.props=e,this.context=t,this.refs=xA,this.updater=r||gA}var Ug=Bg.prototype=new bA;Ug.constructor=Bg;yA(Ug,Zl.prototype);Ug.isPureReactComponent=!0;var pA=Array.isArray;function zg(){}var bt={H:null,A:null,T:null,S:null},wA=Object.prototype.hasOwnProperty;function $g(e,t,r){var n=r.ref;return{$$typeof:Lg,type:e,key:t,ref:n!==void 0?n:null,props:r}}function Uj(e,t){return $g(e.type,t,e.props)}function Hg(e){return typeof e=="object"&&e!==null&&e.$$typeof===Lg}function $j(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(r){return t[r]})}var mA=/\/+/g;function Ig(e,t){return typeof e=="object"&&e!==null&&e.key!=null?$j(""+e.key):t.toString(36)}function Hj(e){switch(e.status){case"fulfilled":return e.value;case"rejected":throw e.reason;default:switch(typeof e.status=="string"?e.then(zg,zg):(e.status="pending",e.then(function(t){e.status==="pending"&&(e.status="fulfilled",e.value=t)},function(t){e.status==="pending"&&(e.status="rejected",e.reason=t)})),e.status){case"fulfilled":return e.value;case"rejected":throw e.reason}}throw e}function Xl(e,t,r,n,a){var i=typeof e;(i==="undefined"||i==="boolean")&&(e=null);var o=!1;if(e===null)o=!0;else switch(i){case"bigint":case"string":case"number":o=!0;break;case"object":switch(e.$$typeof){case Lg:case kj:o=!0;break;case hA:return o=e._init,Xl(o(e._payload),t,r,n,a)}}if(o)return a=a(e),o=n===""?"."+Ig(e,0):n,pA(a)?(r="",o!=null&&(r=o.replace(mA,"$&/")+"/"),Xl(a,t,r,"",function(u){return u})):a!=null&&(Hg(a)&&(a=Uj(a,r+(a.key==null||e&&e.key===a.key?"":(""+a.key).replace(mA,"$&/")+"/")+o)),t.push(a)),1;o=0;var l=n===""?".":n+":";if(pA(e))for(var s=0;s{"use strict";AA.exports=SA()});var PA=kr(kt=>{"use strict";function Yg(e,t){var r=e.length;e.push(t);e:for(;0>>1,a=e[n];if(0>>1;nep(l,r))sep(u,l)?(e[n]=u,e[s]=r,n=s):(e[n]=l,e[o]=r,n=o);else if(sep(u,r))e[n]=u,e[s]=r,n=s;else break e}}return t}function ep(e,t){var r=e.sortIndex-t.sortIndex;return r!==0?r:e.id-t.id}kt.unstable_now=void 0;typeof performance=="object"&&typeof performance.now=="function"?(NA=performance,kt.unstable_now=function(){return NA.now()}):(Vg=Date,_A=Vg.now(),kt.unstable_now=function(){return Vg.now()-_A});var NA,Vg,_A,Ha=[],ji=[],qj=1,Mn=null,Or=3,Wg=!1,dc=!1,fc=!1,Kg=!1,kA=typeof setTimeout=="function"?setTimeout:null,OA=typeof clearTimeout=="function"?clearTimeout:null,CA=typeof setImmediate<"u"?setImmediate:null;function tp(e){for(var t=ga(ji);t!==null;){if(t.callback===null)rp(ji);else if(t.startTime<=e)rp(ji),t.sortIndex=t.expirationTime,Yg(Ha,t);else break;t=ga(ji)}}function Gg(e){if(fc=!1,tp(e),!dc)if(ga(Ha)!==null)dc=!0,Jl||(Jl=!0,Ql());else{var t=ga(ji);t!==null&&Xg(Gg,t.startTime-e)}}var Jl=!1,pc=-1,TA=5,MA=-1;function DA(){return Kg?!0:!(kt.unstable_now()-MAe&&DA());){var n=Mn.callback;if(typeof n=="function"){Mn.callback=null,Or=Mn.priorityLevel;var a=n(Mn.expirationTime<=e);if(e=kt.unstable_now(),typeof a=="function"){Mn.callback=a,tp(e),t=!0;break t}Mn===ga(Ha)&&rp(Ha),tp(e)}else rp(Ha);Mn=ga(Ha)}if(Mn!==null)t=!0;else{var i=ga(ji);i!==null&&Xg(Gg,i.startTime-e),t=!1}}break e}finally{Mn=null,Or=r,Wg=!1}t=void 0}}finally{t?Ql():Jl=!1}}}var Ql;typeof CA=="function"?Ql=function(){CA(Fg)}:typeof MessageChannel<"u"?(qg=new MessageChannel,EA=qg.port2,qg.port1.onmessage=Fg,Ql=function(){EA.postMessage(null)}):Ql=function(){kA(Fg,0)};var qg,EA;function Xg(e,t){pc=kA(function(){e(kt.unstable_now())},t)}kt.unstable_IdlePriority=5;kt.unstable_ImmediatePriority=1;kt.unstable_LowPriority=4;kt.unstable_NormalPriority=3;kt.unstable_Profiling=null;kt.unstable_UserBlockingPriority=2;kt.unstable_cancelCallback=function(e){e.callback=null};kt.unstable_forceFrameRate=function(e){0>e||125n?(e.sortIndex=r,Yg(ji,e),ga(Ha)===null&&e===ga(ji)&&(fc?(OA(pc),pc=-1):fc=!0,Xg(Gg,r-n))):(e.sortIndex=a,Yg(Ha,e),dc||Wg||(dc=!0,Jl||(Jl=!0,Ql()))),e};kt.unstable_shouldYield=DA;kt.unstable_wrapCallback=function(e){var t=Or;return function(){var r=Or;Or=t;try{return e.apply(this,arguments)}finally{Or=r}}}});var IA=kr((vQ,jA)=>{"use strict";jA.exports=PA()});var LA=kr(Hr=>{"use strict";var Yj=ae();function zA(e){var t="https://react.dev/errors/"+e;if(1{"use strict";function BA(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(BA)}catch(e){console.error(e)}}BA(),UA.exports=LA()});var QC=kr(Om=>{"use strict";var ir=IA(),dN=ae(),Gj=ap();function oe(e){var t="https://react.dev/errors/"+e;if(1is||(e.current=jy[is],jy[is]=null,is--)}function yt(e,t){is++,jy[is]=e.current,e.current=t}var wa=Sa(null),$c=Sa(null),Ki=Sa(null),Bp=Sa(null);function Up(e,t){switch(yt(Ki,t),yt($c,e),yt(wa,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?G5(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=G5(t),e=LC(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}vr(wa),yt(wa,e)}function As(){vr(wa),vr($c),vr(Ki)}function Iy(e){e.memoizedState!==null&&yt(Bp,e);var t=wa.current,r=LC(t,e.type);t!==r&&(yt($c,e),yt(wa,r))}function $p(e){$c.current===e&&(vr(wa),vr($c)),Bp.current===e&&(vr(Bp),Qc._currentValue=Lo)}var Zg,VA;function Po(e){if(Zg===void 0)try{throw Error()}catch(r){var t=r.stack.trim().match(/\n( *(at )?)/);Zg=t&&t[1]||"",VA=-1)":-1a||s[n]!==u[a]){var c=` +`+s[n].replace(" at new "," at ");return e.displayName&&c.includes("")&&(c=c.replace("",e.displayName)),c}while(1<=n&&0<=a);break}}}finally{Qg=!1,Error.prepareStackTrace=r}return(r=e?e.displayName||e.name:"")?Po(r):""}function Rj(e,t){switch(e.tag){case 26:case 27:case 5:return Po(e.type);case 16:return Po("Lazy");case 13:return e.child!==t&&t!==null?Po("Suspense Fallback"):Po("Suspense");case 19:return Po("SuspenseList");case 0:case 15:return Jg(e.type,!1);case 11:return Jg(e.type.render,!1);case 1:return Jg(e.type,!0);case 31:return Po("Activity");default:return""}}function FA(e){try{var t="",r=null;do t+=Rj(e,r),r=e,e=e.return;while(e);return t}catch(n){return` +Error generating stack: `+n.message+` +`+n.stack}}var zy=Object.prototype.hasOwnProperty,Cx=ir.unstable_scheduleCallback,Rg=ir.unstable_cancelCallback,eI=ir.unstable_shouldYield,tI=ir.unstable_requestPaint,mn=ir.unstable_now,rI=ir.unstable_getCurrentPriorityLevel,yN=ir.unstable_ImmediatePriority,xN=ir.unstable_UserBlockingPriority,Hp=ir.unstable_NormalPriority,nI=ir.unstable_LowPriority,bN=ir.unstable_IdlePriority,aI=ir.log,iI=ir.unstable_setDisableYieldValue,td=null,vn=null;function Vi(e){if(typeof aI=="function"&&iI(e),vn&&typeof vn.setStrictMode=="function")try{vn.setStrictMode(td,e)}catch{}}var hn=Math.clz32?Math.clz32:sI,oI=Math.log,lI=Math.LN2;function sI(e){return e>>>=0,e===0?32:31-(oI(e)/lI|0)|0}var op=256,lp=262144,sp=4194304;function jo(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function pm(e,t,r){var n=e.pendingLanes;if(n===0)return 0;var a=0,i=e.suspendedLanes,o=e.pingedLanes;e=e.warmLanes;var l=n&134217727;return l!==0?(n=l&~i,n!==0?a=jo(n):(o&=l,o!==0?a=jo(o):r||(r=l&~e,r!==0&&(a=jo(r))))):(l=n&~i,l!==0?a=jo(l):o!==0?a=jo(o):r||(r=n&~e,r!==0&&(a=jo(r)))),a===0?0:t!==0&&t!==a&&(t&i)===0&&(i=a&-a,r=t&-t,i>=r||i===32&&(r&4194048)!==0)?t:a}function rd(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function uI(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function wN(){var e=sp;return sp<<=1,(sp&62914560)===0&&(sp=4194304),e}function ey(e){for(var t=[],r=0;31>r;r++)t.push(e);return t}function nd(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function cI(e,t,r,n,a,i){var o=e.pendingLanes;e.pendingLanes=r,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=r,e.entangledLanes&=r,e.errorRecoveryDisabledLanes&=r,e.shellSuspendCounter=0;var l=e.entanglements,s=e.expirationTimes,u=e.hiddenUpdates;for(r=o&~r;0"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var hI=/[\n"\\]/g;function zn(e){return e.replace(hI,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function Uy(e,t,r,n,a,i,o,l){e.name="",o!=null&&typeof o!="function"&&typeof o!="symbol"&&typeof o!="boolean"?e.type=o:e.removeAttribute("type"),t!=null?o==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+Pn(t)):e.value!==""+Pn(t)&&(e.value=""+Pn(t)):o!=="submit"&&o!=="reset"||e.removeAttribute("value"),t!=null?$y(e,o,Pn(t)):r!=null?$y(e,o,Pn(r)):n!=null&&e.removeAttribute("value"),a==null&&i!=null&&(e.defaultChecked=!!i),a!=null&&(e.checked=a&&typeof a!="function"&&typeof a!="symbol"),l!=null&&typeof l!="function"&&typeof l!="symbol"&&typeof l!="boolean"?e.name=""+Pn(l):e.removeAttribute("name")}function TN(e,t,r,n,a,i,o,l){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||r!=null){if(!(i!=="submit"&&i!=="reset"||t!=null)){By(e);return}r=r!=null?""+Pn(r):"",t=t!=null?""+Pn(t):r,l||t===e.value||(e.value=t),e.defaultValue=t}n=n??a,n=typeof n!="function"&&typeof n!="symbol"&&!!n,e.checked=l?e.checked:!!n,e.defaultChecked=!!n,o!=null&&typeof o!="function"&&typeof o!="symbol"&&typeof o!="boolean"&&(e.name=o),By(e)}function $y(e,t,r){t==="number"&&Vp(e.ownerDocument)===e||e.defaultValue===""+r||(e.defaultValue=""+r)}function gs(e,t,r,n){if(e=e.options,t){t={};for(var a=0;a"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Vy=!1;if(ti)try{Rl={},Object.defineProperty(Rl,"passive",{get:function(){Vy=!0}}),window.addEventListener("test",Rl,Rl),window.removeEventListener("test",Rl,Rl)}catch{Vy=!1}var Rl,Fi=null,Dx=null,_p=null;function IN(){if(_p)return _p;var e,t=Dx,r=t.length,n,a="value"in Fi?Fi.value:Fi.textContent,i=a.length;for(e=0;e=Ec),e5=" ",t5=!1;function LN(e,t){switch(e){case"keyup":return qI.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function BN(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var ss=!1;function WI(e,t){switch(e){case"compositionend":return BN(t);case"keypress":return t.which!==32?null:(t5=!0,e5);case"textInput":return e=t.data,e===e5&&t5?null:e;default:return null}}function KI(e,t){if(ss)return e==="compositionend"||!jx&&LN(e,t)?(e=IN(),_p=Dx=Fi=null,ss=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:r,offset:t-e};e=n}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=i5(r)}}function VN(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?VN(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function FN(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Vp(e.document);t instanceof e.HTMLIFrameElement;){try{var r=typeof t.contentWindow.location.href=="string"}catch{r=!1}if(r)e=t.contentWindow;else break;t=Vp(e.document)}return t}function Ix(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var t7=ti&&"documentMode"in document&&11>=document.documentMode,us=null,Fy=null,Oc=null,qy=!1;function l5(e,t,r){var n=r.window===r?r.document:r.nodeType===9?r:r.ownerDocument;qy||us==null||us!==Vp(n)||(n=us,"selectionStart"in n&&Ix(n)?n={start:n.selectionStart,end:n.selectionEnd}:(n=(n.ownerDocument&&n.ownerDocument.defaultView||window).getSelection(),n={anchorNode:n.anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset}),Oc&&Fc(Oc,n)||(Oc=n,n=om(Fy,"onSelect"),0>=o,a-=o,ya=1<<32-hn(t)+a|r<E?(L=M,M=null):L=M.sibling;var I=f(m,M,x[E],A);if(I===null){M===null&&(M=L);break}e&&M&&I.alternate===null&&t(m,M),g=i(I,g,E),_===null?k=I:_.sibling=I,_=I,M=L}if(E===x.length)return r(m,M),Xe&&Ka(m,E),k;if(M===null){for(;EE?(L=M,M=null):L=M.sibling;var j=f(m,M,I.value,A);if(j===null){M===null&&(M=L);break}e&&M&&j.alternate===null&&t(m,M),g=i(j,g,E),_===null?k=j:_.sibling=j,_=j,M=L}if(I.done)return r(m,M),Xe&&Ka(m,E),k;if(M===null){for(;!I.done;E++,I=x.next())I=d(m,I.value,A),I!==null&&(g=i(I,g,E),_===null?k=I:_.sibling=I,_=I);return Xe&&Ka(m,E),k}for(M=n(M);!I.done;E++,I=x.next())I=p(M,m,E,I.value,A),I!==null&&(e&&I.alternate!==null&&M.delete(I.key===null?E:I.key),g=i(I,g,E),_===null?k=I:_.sibling=I,_=I);return e&&M.forEach(function(z){return t(m,z)}),Xe&&Ka(m,E),k}function b(m,g,x,A){if(typeof x=="object"&&x!==null&&x.type===as&&x.key===null&&(x=x.props.children),typeof x=="object"&&x!==null){switch(x.$$typeof){case ip:e:{for(var k=x.key;g!==null;){if(g.key===k){if(k=x.type,k===as){if(g.tag===7){r(m,g.sibling),A=a(g,x.props.children),A.return=m,m=A;break e}}else if(g.elementType===k||typeof k=="object"&&k!==null&&k.$$typeof===zi&&Io(k)===g.type){r(m,g.sibling),A=a(g,x.props),gc(A,x),A.return=m,m=A;break e}r(m,g);break}else t(m,g);g=g.sibling}x.type===as?(A=Bo(x.props.children,m.mode,A,x.key),A.return=m,m=A):(A=Ep(x.type,x.key,x.props,null,m.mode,A),gc(A,x),A.return=m,m=A)}return o(m);case Sc:e:{for(k=x.key;g!==null;){if(g.key===k)if(g.tag===4&&g.stateNode.containerInfo===x.containerInfo&&g.stateNode.implementation===x.implementation){r(m,g.sibling),A=a(g,x.children||[]),A.return=m,m=A;break e}else{r(m,g);break}else t(m,g);g=g.sibling}A=sy(x,m.mode,A),A.return=m,m=A}return o(m);case zi:return x=Io(x),b(m,g,x,A)}if(Ac(x))return v(m,g,x,A);if(vc(x)){if(k=vc(x),typeof k!="function")throw Error(oe(150));return x=k.call(x),h(m,g,x,A)}if(typeof x.then=="function")return b(m,g,mp(x),A);if(x.$$typeof===Xa)return b(m,g,pp(m,x),A);vp(m,x)}return typeof x=="string"&&x!==""||typeof x=="number"||typeof x=="bigint"?(x=""+x,g!==null&&g.tag===6?(r(m,g.sibling),A=a(g,x),A.return=m,m=A):(r(m,g),A=ly(x,m.mode,A),A.return=m,m=A),o(m)):r(m,g)}return function(m,g,x,A){try{Wc=0;var k=b(m,g,x,A);return bs=null,k}catch(M){if(M===Ls||M===xm)throw M;var _=fn(29,M,null,m.mode);return _.lanes=A,_.return=m,_}}}var qo=a_(!0),i_=a_(!1),Li=!1;function qx(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Qy(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Xi(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Zi(e,t,r){var n=e.updateQueue;if(n===null)return null;if(n=n.shared,(at&2)!==0){var a=n.pending;return a===null?t.next=t:(t.next=a.next,a.next=t),n.pending=t,t=qp(e),ZN(e,null,r),t}return ym(e,n,t,r),qp(e)}function Mc(e,t,r){if(t=t.updateQueue,t!==null&&(t=t.shared,(r&4194048)!==0)){var n=t.lanes;n&=e.pendingLanes,r|=n,t.lanes=r,AN(e,r)}}function cy(e,t){var r=e.updateQueue,n=e.alternate;if(n!==null&&(n=n.updateQueue,r===n)){var a=null,i=null;if(r=r.firstBaseUpdate,r!==null){do{var o={lane:r.lane,tag:r.tag,payload:r.payload,callback:null,next:null};i===null?a=i=o:i=i.next=o,r=r.next}while(r!==null);i===null?a=i=t:i=i.next=t}else a=i=t;r={baseState:n.baseState,firstBaseUpdate:a,lastBaseUpdate:i,shared:n.shared,callbacks:n.callbacks},e.updateQueue=r;return}e=r.lastBaseUpdate,e===null?r.firstBaseUpdate=t:e.next=t,r.lastBaseUpdate=t}var Jy=!1;function Dc(){if(Jy){var e=xs;if(e!==null)throw e}}function Pc(e,t,r,n){Jy=!1;var a=e.updateQueue;Li=!1;var i=a.firstBaseUpdate,o=a.lastBaseUpdate,l=a.shared.pending;if(l!==null){a.shared.pending=null;var s=l,u=s.next;s.next=null,o===null?i=u:o.next=u,o=s;var c=e.alternate;c!==null&&(c=c.updateQueue,l=c.lastBaseUpdate,l!==o&&(l===null?c.firstBaseUpdate=u:l.next=u,c.lastBaseUpdate=s))}if(i!==null){var d=a.baseState;o=0,c=u=s=null,l=i;do{var f=l.lane&-536870913,p=f!==l.lane;if(p?(Ye&f)===f:(n&f)===f){f!==0&&f===Cs&&(Jy=!0),c!==null&&(c=c.next={lane:0,tag:l.tag,payload:l.payload,callback:null,next:null});e:{var v=e,h=l;f=t;var b=r;switch(h.tag){case 1:if(v=h.payload,typeof v=="function"){d=v.call(b,d,f);break e}d=v;break e;case 3:v.flags=v.flags&-65537|128;case 0:if(v=h.payload,f=typeof v=="function"?v.call(b,d,f):v,f==null)break e;d=At({},d,f);break e;case 2:Li=!0}}f=l.callback,f!==null&&(e.flags|=64,p&&(e.flags|=8192),p=a.callbacks,p===null?a.callbacks=[f]:p.push(f))}else p={lane:f,tag:l.tag,payload:l.payload,callback:l.callback,next:null},c===null?(u=c=p,s=d):c=c.next=p,o|=f;if(l=l.next,l===null){if(l=a.shared.pending,l===null)break;p=l,l=p.next,p.next=null,a.lastBaseUpdate=p,a.shared.pending=null}}while(!0);c===null&&(s=d),a.baseState=s,a.firstBaseUpdate=u,a.lastBaseUpdate=c,i===null&&(a.shared.lanes=0),io|=o,e.lanes=o,e.memoizedState=d}}function o_(e,t){if(typeof e!="function")throw Error(oe(191,e));e.call(t)}function l_(e,t){var r=e.callbacks;if(r!==null)for(e.callbacks=null,e=0;ei?i:8;var o=De.T,l={};De.T=l,nb(e,!1,t,r);try{var s=a(),u=De.S;if(u!==null&&u(l,s),s!==null&&typeof s=="object"&&typeof s.then=="function"){var c=c7(s,n);jc(e,t,c,gn(e))}else jc(e,t,n,gn(e))}catch(d){jc(e,t,{then:function(){},status:"rejected",reason:d},gn())}finally{it.p=i,o!==null&&l.types!==null&&(o.types=l.types),De.T=o}}function h7(){}function nx(e,t,r,n){if(e.tag!==5)throw Error(oe(476));var a=D_(e).queue;M_(e,a,t,Lo,r===null?h7:function(){return P_(e),r(n)})}function D_(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:Lo,baseState:Lo,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ni,lastRenderedState:Lo},next:null};var r={};return t.next={memoizedState:r,baseState:r,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ni,lastRenderedState:r},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function P_(e){var t=D_(e);t.next===null&&(t=e.alternate.memoizedState),jc(e,t.next.queue,{},gn())}function rb(){return Ar(Qc)}function j_(){return Yt().memoizedState}function I_(){return Yt().memoizedState}function g7(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var r=gn();e=Xi(r);var n=Zi(t,e,r);n!==null&&(Jr(n,t,r),Mc(n,t,r)),t={cache:Hx()},e.payload=t;return}t=t.return}}function y7(e,t,r){var n=gn();r={lane:n,revertLane:0,gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},Am(e)?L_(t,r):(r=Lx(e,t,r,n),r!==null&&(Jr(r,e,n),B_(r,t,n)))}function z_(e,t,r){var n=gn();jc(e,t,r,n)}function jc(e,t,r,n){var a={lane:n,revertLane:0,gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null};if(Am(e))L_(t,a);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var o=t.lastRenderedState,l=i(o,r);if(a.hasEagerState=!0,a.eagerState=l,yn(l,o))return ym(e,t,a,0),vt===null&&gm(),!1}catch{}if(r=Lx(e,t,a,n),r!==null)return Jr(r,e,n),B_(r,t,n),!0}return!1}function nb(e,t,r,n){if(n={lane:2,revertLane:fb(),gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Am(e)){if(t)throw Error(oe(479))}else t=Lx(e,r,n,2),t!==null&&Jr(t,e,2)}function Am(e){var t=e.alternate;return e===Ue||t!==null&&t===Ue}function L_(e,t){ws=Zp=!0;var r=e.pending;r===null?t.next=t:(t.next=r.next,r.next=t),e.pending=t}function B_(e,t,r){if((r&4194048)!==0){var n=t.lanes;n&=e.pendingLanes,r|=n,t.lanes=r,AN(e,r)}}var Gc={readContext:Ar,use:wm,useCallback:Bt,useContext:Bt,useEffect:Bt,useImperativeHandle:Bt,useLayoutEffect:Bt,useInsertionEffect:Bt,useMemo:Bt,useReducer:Bt,useRef:Bt,useState:Bt,useDebugValue:Bt,useDeferredValue:Bt,useTransition:Bt,useSyncExternalStore:Bt,useId:Bt,useHostTransitionStatus:Bt,useFormState:Bt,useActionState:Bt,useOptimistic:Bt,useMemoCache:Bt,useCacheRefresh:Bt};Gc.useEffectEvent=Bt;var U_={readContext:Ar,use:wm,useCallback:function(e,t){return Vr().memoizedState=[e,t===void 0?null:t],e},useContext:Ar,useEffect:S5,useImperativeHandle:function(e,t,r){r=r!=null?r.concat([e]):null,Tp(4194308,4,C_.bind(null,t,e),r)},useLayoutEffect:function(e,t){return Tp(4194308,4,e,t)},useInsertionEffect:function(e,t){Tp(4,2,e,t)},useMemo:function(e,t){var r=Vr();t=t===void 0?null:t;var n=e();if(Yo){Vi(!0);try{e()}finally{Vi(!1)}}return r.memoizedState=[n,t],n},useReducer:function(e,t,r){var n=Vr();if(r!==void 0){var a=r(t);if(Yo){Vi(!0);try{r(t)}finally{Vi(!1)}}}else a=t;return n.memoizedState=n.baseState=a,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:a},n.queue=e,e=e.dispatch=y7.bind(null,Ue,e),[n.memoizedState,e]},useRef:function(e){var t=Vr();return e={current:e},t.memoizedState=e},useState:function(e){e=tx(e);var t=e.queue,r=z_.bind(null,Ue,t);return t.dispatch=r,[e.memoizedState,r]},useDebugValue:eb,useDeferredValue:function(e,t){var r=Vr();return tb(r,e,t)},useTransition:function(){var e=tx(!1);return e=M_.bind(null,Ue,e.queue,!0,!1),Vr().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,r){var n=Ue,a=Vr();if(Xe){if(r===void 0)throw Error(oe(407));r=r()}else{if(r=t(),vt===null)throw Error(oe(349));(Ye&127)!==0||f_(n,t,r)}a.memoizedState=r;var i={value:r,getSnapshot:t};return a.queue=i,S5(m_.bind(null,n,i,e),[e]),n.flags|=2048,ks(9,{destroy:void 0},p_.bind(null,n,i,r,t),null),r},useId:function(){var e=Vr(),t=vt.identifierPrefix;if(Xe){var r=xa,n=ya;r=(n&~(1<<32-hn(n)-1)).toString(32)+r,t="_"+t+"R_"+r,r=Qp++,0<\/script>",i=i.removeChild(i.firstChild);break;case"select":i=typeof n.is=="string"?o.createElement("select",{is:n.is}):o.createElement("select"),n.multiple?i.multiple=!0:n.size&&(i.size=n.size);break;default:i=typeof n.is=="string"?o.createElement(a,{is:n.is}):o.createElement(a)}}i[wr]=t,i[Rr]=n;e:for(o=t.child;o!==null;){if(o.tag===5||o.tag===6)i.appendChild(o.stateNode);else if(o.tag!==4&&o.tag!==27&&o.child!==null){o.child.return=o,o=o.child;continue}if(o===t)break e;for(;o.sibling===null;){if(o.return===null||o.return===t)break e;o=o.return}o.sibling.return=o.return,o=o.sibling}t.stateNode=i;e:switch(Nr(i,a,n),a){case"button":case"input":case"select":case"textarea":n=!!n.autoFocus;break e;case"img":n=!0;break e;default:n=!1}n&&Fa(t)}}return wt(t),yy(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,r),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==n&&Fa(t);else{if(typeof n!="string"&&t.stateNode===null)throw Error(oe(166));if(e=Ki.current,es(t)){if(e=t.stateNode,r=t.memoizedProps,n=null,a=Sr,a!==null)switch(a.tag){case 27:case 5:n=a.memoizedProps}e[wr]=t,e=!!(e.nodeValue===r||n!==null&&n.suppressHydrationWarning===!0||zC(e.nodeValue,r)),e||no(t,!0)}else e=lm(e).createTextNode(n),e[wr]=t,t.stateNode=e}return wt(t),null;case 31:if(r=t.memoizedState,e===null||e.memoizedState!==null){if(n=es(t),r!==null){if(e===null){if(!n)throw Error(oe(318));if(e=t.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(oe(557));e[wr]=t}else Vo(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;wt(t),e=!1}else r=uy(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=r),e=!0;if(!e)return t.flags&256?(dn(t),t):(dn(t),null);if((t.flags&128)!==0)throw Error(oe(558))}return wt(t),null;case 13:if(n=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=es(t),n!==null&&n.dehydrated!==null){if(e===null){if(!a)throw Error(oe(318));if(a=t.memoizedState,a=a!==null?a.dehydrated:null,!a)throw Error(oe(317));a[wr]=t}else Vo(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;wt(t),a=!1}else a=uy(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(dn(t),t):(dn(t),null)}return dn(t),(t.flags&128)!==0?(t.lanes=r,t):(r=n!==null,e=e!==null&&e.memoizedState!==null,r&&(n=t.child,a=null,n.alternate!==null&&n.alternate.memoizedState!==null&&n.alternate.memoizedState.cachePool!==null&&(a=n.alternate.memoizedState.cachePool.pool),i=null,n.memoizedState!==null&&n.memoizedState.cachePool!==null&&(i=n.memoizedState.cachePool.pool),i!==a&&(n.flags|=2048)),r!==e&&r&&(t.child.flags|=8192),hp(t,t.updateQueue),wt(t),null);case 4:return As(),e===null&&pb(t.stateNode.containerInfo),wt(t),null;case 10:return Ra(t.type),wt(t),null;case 19:if(vr(qt),n=t.memoizedState,n===null)return wt(t),null;if(a=(t.flags&128)!==0,i=n.rendering,i===null)if(a)yc(n,!1);else{if(Ut!==0||e!==null&&(e.flags&128)!==0)for(e=t.child;e!==null;){if(i=Xp(e),i!==null){for(t.flags|=128,yc(n,!1),e=i.updateQueue,t.updateQueue=e,hp(t,e),t.subtreeFlags=0,e=r,r=t.child;r!==null;)QN(r,e),r=r.sibling;return yt(qt,qt.current&1|2),Xe&&Ka(t,n.treeForkCount),t.child}e=e.sibling}n.tail!==null&&mn()>tm&&(t.flags|=128,a=!0,yc(n,!1),t.lanes=4194304)}else{if(!a)if(e=Xp(i),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,hp(t,e),yc(n,!0),n.tail===null&&n.tailMode==="hidden"&&!i.alternate&&!Xe)return wt(t),null}else 2*mn()-n.renderingStartTime>tm&&r!==536870912&&(t.flags|=128,a=!0,yc(n,!1),t.lanes=4194304);n.isBackwards?(i.sibling=t.child,t.child=i):(e=n.last,e!==null?e.sibling=i:t.child=i,n.last=i)}return n.tail!==null?(e=n.tail,n.rendering=e,n.tail=e.sibling,n.renderingStartTime=mn(),e.sibling=null,r=qt.current,yt(qt,a?r&1|2:r&1),Xe&&Ka(t,n.treeForkCount),e):(wt(t),null);case 22:case 23:return dn(t),Yx(),n=t.memoizedState!==null,e!==null?e.memoizedState!==null!==n&&(t.flags|=8192):n&&(t.flags|=8192),n?(r&536870912)!==0&&(t.flags&128)===0&&(wt(t),t.subtreeFlags&6&&(t.flags|=8192)):wt(t),r=t.updateQueue,r!==null&&hp(t,r.retryQueue),r=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(r=e.memoizedState.cachePool.pool),n=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(n=t.memoizedState.cachePool.pool),n!==r&&(t.flags|=2048),e!==null&&vr(Uo),null;case 24:return r=null,e!==null&&(r=e.memoizedState.cache),t.memoizedState.cache!==r&&(t.flags|=2048),Ra(Rt),wt(t),null;case 25:return null;case 30:return null}throw Error(oe(156,t.tag))}function A7(e,t){switch($x(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Ra(Rt),As(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return $p(t),null;case 31:if(t.memoizedState!==null){if(dn(t),t.alternate===null)throw Error(oe(340));Vo()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(dn(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(oe(340));Vo()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return vr(qt),null;case 4:return As(),null;case 10:return Ra(t.type),null;case 22:case 23:return dn(t),Yx(),e!==null&&vr(Uo),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return Ra(Rt),null;case 25:return null;default:return null}}function Q_(e,t){switch($x(t),t.tag){case 3:Ra(Rt),As();break;case 26:case 27:case 5:$p(t);break;case 4:As();break;case 31:t.memoizedState!==null&&dn(t);break;case 13:dn(t);break;case 19:vr(qt);break;case 10:Ra(t.type);break;case 22:case 23:dn(t),Yx(),e!==null&&vr(Uo);break;case 24:Ra(Rt)}}function sd(e,t){try{var r=t.updateQueue,n=r!==null?r.lastEffect:null;if(n!==null){var a=n.next;r=a;do{if((r.tag&e)===e){n=void 0;var i=r.create,o=r.inst;n=i(),o.destroy=n}r=r.next}while(r!==a)}}catch(l){ut(t,t.return,l)}}function ao(e,t,r){try{var n=t.updateQueue,a=n!==null?n.lastEffect:null;if(a!==null){var i=a.next;n=i;do{if((n.tag&e)===e){var o=n.inst,l=o.destroy;if(l!==void 0){o.destroy=void 0,a=t;var s=r,u=l;try{u()}catch(c){ut(a,s,c)}}}n=n.next}while(n!==i)}}catch(c){ut(t,t.return,c)}}function J_(e){var t=e.updateQueue;if(t!==null){var r=e.stateNode;try{l_(t,r)}catch(n){ut(e,e.return,n)}}}function R_(e,t,r){r.props=Wo(e.type,e.memoizedProps),r.state=e.memoizedState;try{r.componentWillUnmount()}catch(n){ut(e,t,n)}}function Ic(e,t){try{var r=e.ref;if(r!==null){switch(e.tag){case 26:case 27:case 5:var n=e.stateNode;break;case 30:n=e.stateNode;break;default:n=e.stateNode}typeof r=="function"?e.refCleanup=r(n):r.current=n}}catch(a){ut(e,t,a)}}function ba(e,t){var r=e.ref,n=e.refCleanup;if(r!==null)if(typeof n=="function")try{n()}catch(a){ut(e,t,a)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof r=="function")try{r(null)}catch(a){ut(e,t,a)}else r.current=null}function eC(e){var t=e.type,r=e.memoizedProps,n=e.stateNode;try{e:switch(t){case"button":case"input":case"select":case"textarea":r.autoFocus&&n.focus();break e;case"img":r.src?n.src=r.src:r.srcSet&&(n.srcset=r.srcSet)}}catch(a){ut(e,e.return,a)}}function xy(e,t,r){try{var n=e.stateNode;F7(n,e.type,r,t),n[Rr]=t}catch(a){ut(e,e.return,a)}}function tC(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&lo(e.type)||e.tag===4}function by(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||tC(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&lo(e.type)||e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function sx(e,t,r){var n=e.tag;if(n===5||n===6)e=e.stateNode,t?(r.nodeType===9?r.body:r.nodeName==="HTML"?r.ownerDocument.body:r).insertBefore(e,t):(t=r.nodeType===9?r.body:r.nodeName==="HTML"?r.ownerDocument.body:r,t.appendChild(e),r=r._reactRootContainer,r!=null||t.onclick!==null||(t.onclick=Za));else if(n!==4&&(n===27&&lo(e.type)&&(r=e.stateNode,t=null),e=e.child,e!==null))for(sx(e,t,r),e=e.sibling;e!==null;)sx(e,t,r),e=e.sibling}function em(e,t,r){var n=e.tag;if(n===5||n===6)e=e.stateNode,t?r.insertBefore(e,t):r.appendChild(e);else if(n!==4&&(n===27&&lo(e.type)&&(r=e.stateNode),e=e.child,e!==null))for(em(e,t,r),e=e.sibling;e!==null;)em(e,t,r),e=e.sibling}function rC(e){var t=e.stateNode,r=e.memoizedProps;try{for(var n=e.type,a=t.attributes;a.length;)t.removeAttributeNode(a[0]);Nr(t,n,r),t[wr]=e,t[Rr]=r}catch(i){ut(e,e.return,i)}}var Ga=!1,Jt=!1,wy=!1,I5=typeof WeakSet=="function"?WeakSet:Set,pr=null;function N7(e,t){if(e=e.containerInfo,vx=dm,e=FN(e),Ix(e)){if("selectionStart"in e)var r={start:e.selectionStart,end:e.selectionEnd};else e:{r=(r=e.ownerDocument)&&r.defaultView||window;var n=r.getSelection&&r.getSelection();if(n&&n.rangeCount!==0){r=n.anchorNode;var a=n.anchorOffset,i=n.focusNode;n=n.focusOffset;try{r.nodeType,i.nodeType}catch{r=null;break e}var o=0,l=-1,s=-1,u=0,c=0,d=e,f=null;t:for(;;){for(var p;d!==r||a!==0&&d.nodeType!==3||(l=o+a),d!==i||n!==0&&d.nodeType!==3||(s=o+n),d.nodeType===3&&(o+=d.nodeValue.length),(p=d.firstChild)!==null;)f=d,d=p;for(;;){if(d===e)break t;if(f===r&&++u===a&&(l=o),f===i&&++c===n&&(s=o),(p=d.nextSibling)!==null)break;d=f,f=d.parentNode}d=p}r=l===-1||s===-1?null:{start:l,end:s}}else r=null}r=r||{start:0,end:0}}else r=null;for(hx={focusedElem:e,selectionRange:r},dm=!1,pr=t;pr!==null;)if(t=pr,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,pr=e;else for(;pr!==null;){switch(t=pr,i=t.alternate,e=t.flags,t.tag){case 0:if((e&4)!==0&&(e=t.updateQueue,e=e!==null?e.events:null,e!==null))for(r=0;r title"))),Nr(i,n,r),i[wr]=e,mr(i),n=i;break e;case"link":var o=nN("link","href",a).get(n+(r.href||""));if(o){for(var l=0;lb&&(o=b,b=h,h=o);var m=o5(l,h),g=o5(l,b);if(m&&g&&(p.rangeCount!==1||p.anchorNode!==m.node||p.anchorOffset!==m.offset||p.focusNode!==g.node||p.focusOffset!==g.offset)){var x=d.createRange();x.setStart(m.node,m.offset),p.removeAllRanges(),h>b?(p.addRange(x),p.extend(g.node,g.offset)):(x.setEnd(g.node,g.offset),p.addRange(x))}}}}for(d=[],p=l;p=p.parentNode;)p.nodeType===1&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for(typeof l.focus=="function"&&l.focus(),l=0;lr?32:r,De.T=null,r=dx,dx=null;var i=Ji,o=ei;if(ar=0,Ts=Ji=null,ei=0,(at&6)!==0)throw Error(oe(331));var l=at;if(at|=4,pC(i.current),cC(i,i.current,o,r),at=l,ud(0,!1),vn&&typeof vn.onPostCommitFiberRoot=="function")try{vn.onPostCommitFiberRoot(td,i)}catch{}return!0}finally{it.p=a,De.T=n,kC(e,t)}}function U5(e,t,r){t=Ln(r,t),t=ix(e.stateNode,t,2),e=Zi(e,t,2),e!==null&&(nd(e,2),Aa(e))}function ut(e,t,r){if(e.tag===3)U5(e,e,r);else for(;t!==null;){if(t.tag===3){U5(t,e,r);break}else if(t.tag===1){var n=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof n.componentDidCatch=="function"&&(Qi===null||!Qi.has(n))){e=Ln(r,e),r=q_(2),n=Zi(t,r,2),n!==null&&(Y_(r,n,t,e),nd(n,2),Aa(n));break}}t=t.return}}function Ay(e,t,r){var n=e.pingCache;if(n===null){n=e.pingCache=new E7;var a=new Set;n.set(t,a)}else a=n.get(t),a===void 0&&(a=new Set,n.set(t,a));a.has(r)||(ub=!0,a.add(r),e=D7.bind(null,e,t,r),t.then(e,e))}function D7(e,t,r){var n=e.pingCache;n!==null&&n.delete(t),e.pingedLanes|=e.suspendedLanes&r,e.warmLanes&=~r,vt===e&&(Ye&r)===r&&(Ut===4||Ut===3&&(Ye&62914560)===Ye&&300>mn()-Nm?(at&2)===0&&Ms(e,0):cb|=r,Os===Ye&&(Os=0)),Aa(e)}function TC(e,t){t===0&&(t=wN()),e=Zo(e,t),e!==null&&(nd(e,t),Aa(e))}function P7(e){var t=e.memoizedState,r=0;t!==null&&(r=t.retryLane),TC(e,r)}function j7(e,t){var r=0;switch(e.tag){case 31:case 13:var n=e.stateNode,a=e.memoizedState;a!==null&&(r=a.retryLane);break;case 19:n=e.stateNode;break;case 22:n=e.stateNode._retryCache;break;default:throw Error(oe(314))}n!==null&&n.delete(t),TC(e,r)}function I7(e,t){return Cx(e,t)}var am=null,ns=null,px=!1,im=!1,Ny=!1,Wi=0;function Aa(e){e!==ns&&e.next===null&&(ns===null?am=ns=e:ns=ns.next=e),im=!0,px||(px=!0,L7())}function ud(e,t){if(!Ny&&im){Ny=!0;do for(var r=!1,n=am;n!==null;){if(!t)if(e!==0){var a=n.pendingLanes;if(a===0)var i=0;else{var o=n.suspendedLanes,l=n.pingedLanes;i=(1<<31-hn(42|e)+1)-1,i&=a&~(o&~l),i=i&201326741?i&201326741|1:i?i|2:0}i!==0&&(r=!0,$5(n,i))}else i=Ye,i=pm(n,n===vt?i:0,n.cancelPendingCommit!==null||n.timeoutHandle!==-1),(i&3)===0||rd(n,i)||(r=!0,$5(n,i));n=n.next}while(r);Ny=!1}}function z7(){MC()}function MC(){im=px=!1;var e=0;Wi!==0&&Y7()&&(e=Wi);for(var t=mn(),r=null,n=am;n!==null;){var a=n.next,i=DC(n,t);i===0?(n.next=null,r===null?am=a:r.next=a,a===null&&(ns=r)):(r=n,(e!==0||(i&3)!==0)&&(im=!0)),n=a}ar!==0&&ar!==5||ud(e,!1),Wi!==0&&(Wi=0)}function DC(e,t){for(var r=e.suspendedLanes,n=e.pingedLanes,a=e.expirationTimes,i=e.pendingLanes&-62914561;0l)break;var c=s.transferSize,d=s.initiatorType;c&&K5(d)&&(s=s.responseEnd,o+=c*(s"u"?null:document;function HC(e,t,r){var n=Us;if(n&&typeof t=="string"&&t){var a=zn(t);a='link[rel="'+e+'"][href="'+a+'"]',typeof r=="string"&&(a+='[crossorigin="'+r+'"]'),eN.has(a)||(eN.add(a),e={rel:e,crossOrigin:r,href:t},n.querySelector(a)===null&&(t=n.createElement("link"),Nr(t,"link",e),mr(t),n.head.appendChild(t)))}}function ez(e){oi.D(e),HC("dns-prefetch",e,null)}function tz(e,t){oi.C(e,t),HC("preconnect",e,t)}function rz(e,t,r){oi.L(e,t,r);var n=Us;if(n&&e&&t){var a='link[rel="preload"][as="'+zn(t)+'"]';t==="image"&&r&&r.imageSrcSet?(a+='[imagesrcset="'+zn(r.imageSrcSet)+'"]',typeof r.imageSizes=="string"&&(a+='[imagesizes="'+zn(r.imageSizes)+'"]')):a+='[href="'+zn(e)+'"]';var i=a;switch(t){case"style":i=Ds(e);break;case"script":i=$s(e)}Hn.has(i)||(e=At({rel:"preload",href:t==="image"&&r&&r.imageSrcSet?void 0:e,as:t},r),Hn.set(i,e),n.querySelector(a)!==null||t==="style"&&n.querySelector(cd(i))||t==="script"&&n.querySelector(dd(i))||(t=n.createElement("link"),Nr(t,"link",e),mr(t),n.head.appendChild(t)))}}function nz(e,t){oi.m(e,t);var r=Us;if(r&&e){var n=t&&typeof t.as=="string"?t.as:"script",a='link[rel="modulepreload"][as="'+zn(n)+'"][href="'+zn(e)+'"]',i=a;switch(n){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":i=$s(e)}if(!Hn.has(i)&&(e=At({rel:"modulepreload",href:e},t),Hn.set(i,e),r.querySelector(a)===null)){switch(n){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(r.querySelector(dd(i)))return}n=r.createElement("link"),Nr(n,"link",e),mr(n),r.head.appendChild(n)}}}function az(e,t,r){oi.S(e,t,r);var n=Us;if(n&&e){var a=hs(n).hoistableStyles,i=Ds(e);t=t||"default";var o=a.get(i);if(!o){var l={loading:0,preload:null};if(o=n.querySelector(cd(i)))l.loading=5;else{e=At({rel:"stylesheet",href:e,"data-precedence":t},r),(r=Hn.get(i))&&mb(e,r);var s=o=n.createElement("link");mr(s),Nr(s,"link",e),s._p=new Promise(function(u,c){s.onload=u,s.onerror=c}),s.addEventListener("load",function(){l.loading|=1}),s.addEventListener("error",function(){l.loading|=2}),l.loading|=4,jp(o,t,n)}o={type:"stylesheet",instance:o,count:1,state:l},a.set(i,o)}}}function iz(e,t){oi.X(e,t);var r=Us;if(r&&e){var n=hs(r).hoistableScripts,a=$s(e),i=n.get(a);i||(i=r.querySelector(dd(a)),i||(e=At({src:e,async:!0},t),(t=Hn.get(a))&&vb(e,t),i=r.createElement("script"),mr(i),Nr(i,"link",e),r.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},n.set(a,i))}}function oz(e,t){oi.M(e,t);var r=Us;if(r&&e){var n=hs(r).hoistableScripts,a=$s(e),i=n.get(a);i||(i=r.querySelector(dd(a)),i||(e=At({src:e,async:!0,type:"module"},t),(t=Hn.get(a))&&vb(e,t),i=r.createElement("script"),mr(i),Nr(i,"link",e),r.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},n.set(a,i))}}function tN(e,t,r,n){var a=(a=Ki.current)?sm(a):null;if(!a)throw Error(oe(446));switch(e){case"meta":case"title":return null;case"style":return typeof r.precedence=="string"&&typeof r.href=="string"?(t=Ds(r.href),r=hs(a).hoistableStyles,n=r.get(t),n||(n={type:"style",instance:null,count:0,state:null},r.set(t,n)),n):{type:"void",instance:null,count:0,state:null};case"link":if(r.rel==="stylesheet"&&typeof r.href=="string"&&typeof r.precedence=="string"){e=Ds(r.href);var i=hs(a).hoistableStyles,o=i.get(e);if(o||(a=a.ownerDocument||a,o={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},i.set(e,o),(i=a.querySelector(cd(e)))&&!i._p&&(o.instance=i,o.state.loading=5),Hn.has(e)||(r={rel:"preload",as:"style",href:r.href,crossOrigin:r.crossOrigin,integrity:r.integrity,media:r.media,hrefLang:r.hrefLang,referrerPolicy:r.referrerPolicy},Hn.set(e,r),i||lz(a,e,r,o.state))),t&&n===null)throw Error(oe(528,""));return o}if(t&&n!==null)throw Error(oe(529,""));return null;case"script":return t=r.async,r=r.src,typeof r=="string"&&t&&typeof t!="function"&&typeof t!="symbol"?(t=$s(r),r=hs(a).hoistableScripts,n=r.get(t),n||(n={type:"script",instance:null,count:0,state:null},r.set(t,n)),n):{type:"void",instance:null,count:0,state:null};default:throw Error(oe(444,e))}}function Ds(e){return'href="'+zn(e)+'"'}function cd(e){return'link[rel="stylesheet"]['+e+"]"}function VC(e){return At({},e,{"data-precedence":e.precedence,precedence:null})}function lz(e,t,r,n){e.querySelector('link[rel="preload"][as="style"]['+t+"]")?n.loading=1:(t=e.createElement("link"),n.preload=t,t.addEventListener("load",function(){return n.loading|=1}),t.addEventListener("error",function(){return n.loading|=2}),Nr(t,"link",r),mr(t),e.head.appendChild(t))}function $s(e){return'[src="'+zn(e)+'"]'}function dd(e){return"script[async]"+e}function rN(e,t,r){if(t.count++,t.instance===null)switch(t.type){case"style":var n=e.querySelector('style[data-href~="'+zn(r.href)+'"]');if(n)return t.instance=n,mr(n),n;var a=At({},r,{"data-href":r.href,"data-precedence":r.precedence,href:null,precedence:null});return n=(e.ownerDocument||e).createElement("style"),mr(n),Nr(n,"style",a),jp(n,r.precedence,e),t.instance=n;case"stylesheet":a=Ds(r.href);var i=e.querySelector(cd(a));if(i)return t.state.loading|=4,t.instance=i,mr(i),i;n=VC(r),(a=Hn.get(a))&&mb(n,a),i=(e.ownerDocument||e).createElement("link"),mr(i);var o=i;return o._p=new Promise(function(l,s){o.onload=l,o.onerror=s}),Nr(i,"link",n),t.state.loading|=4,jp(i,r.precedence,e),t.instance=i;case"script":return i=$s(r.src),(a=e.querySelector(dd(i)))?(t.instance=a,mr(a),a):(n=r,(a=Hn.get(i))&&(n=At({},r),vb(n,a)),e=e.ownerDocument||e,a=e.createElement("script"),mr(a),Nr(a,"link",n),e.head.appendChild(a),t.instance=a);case"void":return null;default:throw Error(oe(443,t.type))}else t.type==="stylesheet"&&(t.state.loading&4)===0&&(n=t.instance,t.state.loading|=4,jp(n,r.precedence,e));return t.instance}function jp(e,t,r){for(var n=r.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),a=n.length?n[n.length-1]:null,i=a,o=0;o title"):null)}function sz(e,t,r){if(r===1||t.itemProp!=null)return!1;switch(e){case"meta":case"title":return!0;case"style":if(typeof t.precedence!="string"||typeof t.href!="string"||t.href==="")break;return!0;case"link":if(typeof t.rel!="string"||typeof t.href!="string"||t.href===""||t.onLoad||t.onError)break;return t.rel==="stylesheet"?(e=t.disabled,typeof t.precedence=="string"&&e==null):!0;case"script":if(t.async&&typeof t.async!="function"&&typeof t.async!="symbol"&&!t.onLoad&&!t.onError&&t.src&&typeof t.src=="string")return!0}return!1}function FC(e){return!(e.type==="stylesheet"&&(e.state.loading&3)===0)}function uz(e,t,r,n){if(r.type==="stylesheet"&&(typeof n.media!="string"||matchMedia(n.media).matches!==!1)&&(r.state.loading&4)===0){if(r.instance===null){var a=Ds(n.href),i=t.querySelector(cd(a));if(i){t=i._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(e.count++,e=um.bind(e),t.then(e,e)),r.state.loading|=4,r.instance=i,mr(i);return}i=t.ownerDocument||t,n=VC(n),(a=Hn.get(a))&&mb(n,a),i=i.createElement("link"),mr(i);var o=i;o._p=new Promise(function(l,s){o.onload=l,o.onerror=s}),Nr(i,"link",n),r.instance=i}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(r,t),(t=r.state.preload)&&(r.state.loading&3)===0&&(e.count++,r=um.bind(e),t.addEventListener("load",r),t.addEventListener("error",r))}}var ky=0;function cz(e,t){return e.stylesheets&&e.count===0&&zp(e,e.stylesheets),0ky?50:800)+t);return e.unsuspend=r,function(){e.unsuspend=null,clearTimeout(n),clearTimeout(a)}}:null}function um(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)zp(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var cm=null;function zp(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,cm=new Map,t.forEach(dz,e),cm=null,um.call(e))}function dz(e,t){if(!(t.state.loading&4)){var r=cm.get(e);if(r)var n=r.get(null);else{r=new Map,cm.set(e,r);for(var a=e.querySelectorAll("link[data-precedence],style[data-precedence]"),i=0;i{"use strict";function JC(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(JC)}catch(e){console.error(e)}}JC(),RC.exports=QC()});var JE=kr(QE=>{"use strict";var au=ae();function t9(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var r9=typeof Object.is=="function"?Object.is:t9,n9=au.useState,a9=au.useEffect,i9=au.useLayoutEffect,o9=au.useDebugValue;function l9(e,t){var r=t(),n=n9({inst:{value:r,getSnapshot:t}}),a=n[0].inst,i=n[1];return i9(function(){a.value=r,a.getSnapshot=t,Rb(a)&&i({inst:a})},[e,r,t]),a9(function(){return Rb(a)&&i({inst:a}),e(function(){Rb(a)&&i({inst:a})})},[e]),o9(r),r}function Rb(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!r9(e,r)}catch{return!0}}function s9(e,t){return t()}var u9=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?s9:l9;QE.useSyncExternalStore=au.useSyncExternalStore!==void 0?au.useSyncExternalStore:u9});var ek=kr((nte,RE)=>{"use strict";RE.exports=JE()});var rk=kr(tk=>{"use strict";var mv=ae(),c9=ek();function d9(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var f9=typeof Object.is=="function"?Object.is:d9,p9=c9.useSyncExternalStore,m9=mv.useRef,v9=mv.useEffect,h9=mv.useMemo,g9=mv.useDebugValue;tk.useSyncExternalStoreWithSelector=function(e,t,r,n,a){var i=m9(null);if(i.current===null){var o={hasValue:!1,value:null};i.current=o}else o=i.current;i=h9(function(){function s(p){if(!u){if(u=!0,c=p,p=n(p),a!==void 0&&o.hasValue){var v=o.value;if(a(v,p))return d=v}return d=p}if(v=d,f9(c,p))return v;var h=n(p);return a!==void 0&&a(v,h)?(c=p,v):(c=p,d=h)}var u=!1,c,d,f=r===void 0?null:r;return[function(){return s(t())},f===null?void 0:function(){return s(f())}]},[t,r,n,a]);var l=p9(e,i[0],i[1]);return v9(function(){o.hasValue=!0,o.value=l},[l]),g9(l),l}});var ak=kr((ite,nk)=>{"use strict";nk.exports=rk()});var WO=kr(YO=>{"use strict";var Td=ae();function wB(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var SB=typeof Object.is=="function"?Object.is:wB,AB=Td.useSyncExternalStore,NB=Td.useRef,_B=Td.useEffect,CB=Td.useMemo,EB=Td.useDebugValue;YO.useSyncExternalStoreWithSelector=function(e,t,r,n,a){var i=NB(null);if(i.current===null){var o={hasValue:!1,value:null};i.current=o}else o=i.current;i=CB(function(){function s(p){if(!u){if(u=!0,c=p,p=n(p),a!==void 0&&o.hasValue){var v=o.value;if(a(v,p))return d=v}return d=p}if(v=d,SB(c,p))return v;var h=n(p);return a!==void 0&&a(v,h)?(c=p,v):(c=p,d=h)}var u=!1,c,d,f=r===void 0?null:r;return[function(){return s(t())},f===null?void 0:function(){return s(f())}]},[t,r,n,a]);var l=AB(e,i[0],i[1]);return _B(function(){o.hasValue=!0,o.value=l},[l]),EB(l),l}});var GO=kr((ane,KO)=>{"use strict";KO.exports=WO()});var W1=kr((g3,hh)=>{(function(e){"use strict";var t=1e9,r={precision:20,rounding:4,toExpNeg:-7,toExpPos:21,LN10:"2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298341967784042286"},n=!0,a="[DecimalError] ",i=a+"Invalid argument: ",o=a+"Exponent out of range: ",l=Math.floor,s=Math.pow,u=/^(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,c,d=1e7,f=7,p=9007199254740991,v=l(p/f),h={};h.absoluteValue=h.abs=function(){var y=new this.constructor(this);return y.s&&(y.s=1),y},h.comparedTo=h.cmp=function(y){var C,O,D,N,T=this;if(y=new T.constructor(y),T.s!==y.s)return T.s||-y.s;if(T.e!==y.e)return T.e>y.e^T.s<0?1:-1;for(D=T.d.length,N=y.d.length,C=0,O=Dy.d[C]^T.s<0?1:-1;return D===N?0:D>N^T.s<0?1:-1},h.decimalPlaces=h.dp=function(){var y=this,C=y.d.length-1,O=(C-y.e)*f;if(C=y.d[C],C)for(;C%10==0;C/=10)O--;return O<0?0:O},h.dividedBy=h.div=function(y){return x(this,new this.constructor(y))},h.dividedToIntegerBy=h.idiv=function(y){var C=this,O=C.constructor;return I(x(C,new O(y),0,1),O.precision)},h.equals=h.eq=function(y){return!this.cmp(y)},h.exponent=function(){return k(this)},h.greaterThan=h.gt=function(y){return this.cmp(y)>0},h.greaterThanOrEqualTo=h.gte=function(y){return this.cmp(y)>=0},h.isInteger=h.isint=function(){return this.e>this.d.length-2},h.isNegative=h.isneg=function(){return this.s<0},h.isPositive=h.ispos=function(){return this.s>0},h.isZero=function(){return this.s===0},h.lessThan=h.lt=function(y){return this.cmp(y)<0},h.lessThanOrEqualTo=h.lte=function(y){return this.cmp(y)<1},h.logarithm=h.log=function(y){var C,O=this,D=O.constructor,N=D.precision,T=N+5;if(y===void 0)y=new D(10);else if(y=new D(y),y.s<1||y.eq(c))throw Error(a+"NaN");if(O.s<1)throw Error(a+(O.s?"NaN":"-Infinity"));return O.eq(c)?new D(0):(n=!1,C=x(E(O,T),E(y,T),T),n=!0,I(C,N))},h.minus=h.sub=function(y){var C=this;return y=new C.constructor(y),C.s==y.s?j(C,y):b(C,(y.s=-y.s,y))},h.modulo=h.mod=function(y){var C,O=this,D=O.constructor,N=D.precision;if(y=new D(y),!y.s)throw Error(a+"NaN");return O.s?(n=!1,C=x(O,y,0,1).times(y),n=!0,O.minus(C)):I(new D(O),N)},h.naturalExponential=h.exp=function(){return A(this)},h.naturalLogarithm=h.ln=function(){return E(this)},h.negated=h.neg=function(){var y=new this.constructor(this);return y.s=-y.s||0,y},h.plus=h.add=function(y){var C=this;return y=new C.constructor(y),C.s==y.s?b(C,y):j(C,(y.s=-y.s,y))},h.precision=h.sd=function(y){var C,O,D,N=this;if(y!==void 0&&y!==!!y&&y!==1&&y!==0)throw Error(i+y);if(C=k(N)+1,D=N.d.length-1,O=D*f+1,D=N.d[D],D){for(;D%10==0;D/=10)O--;for(D=N.d[0];D>=10;D/=10)O++}return y&&C>O?C:O},h.squareRoot=h.sqrt=function(){var y,C,O,D,N,T,$,Y=this,J=Y.constructor;if(Y.s<1){if(!Y.s)return new J(0);throw Error(a+"NaN")}for(y=k(Y),n=!1,N=Math.sqrt(+Y),N==0||N==1/0?(C=g(Y.d),(C.length+y)%2==0&&(C+="0"),N=Math.sqrt(C),y=l((y+1)/2)-(y<0||y%2),N==1/0?C="5e"+y:(C=N.toExponential(),C=C.slice(0,C.indexOf("e")+1)+y),D=new J(C)):D=new J(N.toString()),O=J.precision,N=$=O+3;;)if(T=D,D=T.plus(x(Y,T,$+2)).times(.5),g(T.d).slice(0,$)===(C=g(D.d)).slice(0,$)){if(C=C.slice($-3,$+1),N==$&&C=="4999"){if(I(T,O+1,0),T.times(T).eq(Y)){D=T;break}}else if(C!="9999")break;$+=4}return n=!0,I(D,O)},h.times=h.mul=function(y){var C,O,D,N,T,$,Y,J,fe,pe=this,ge=pe.constructor,ze=pe.d,se=(y=new ge(y)).d;if(!pe.s||!y.s)return new ge(0);for(y.s*=pe.s,O=pe.e+y.e,J=ze.length,fe=se.length,J=0;){for(C=0,N=J+D;N>D;)Y=T[N]+se[D]*ze[N-D-1]+C,T[N--]=Y%d|0,C=Y/d|0;T[N]=(T[N]+C)%d|0}for(;!T[--$];)T.pop();return C?++O:T.shift(),y.d=T,y.e=O,n?I(y,ge.precision):y},h.toDecimalPlaces=h.todp=function(y,C){var O=this,D=O.constructor;return O=new D(O),y===void 0?O:(m(y,0,t),C===void 0?C=D.rounding:m(C,0,8),I(O,y+k(O)+1,C))},h.toExponential=function(y,C){var O,D=this,N=D.constructor;return y===void 0?O=z(D,!0):(m(y,0,t),C===void 0?C=N.rounding:m(C,0,8),D=I(new N(D),y+1,C),O=z(D,!0,y+1)),O},h.toFixed=function(y,C){var O,D,N=this,T=N.constructor;return y===void 0?z(N):(m(y,0,t),C===void 0?C=T.rounding:m(C,0,8),D=I(new T(N),y+k(N)+1,C),O=z(D.abs(),!1,y+k(D)+1),N.isneg()&&!N.isZero()?"-"+O:O)},h.toInteger=h.toint=function(){var y=this,C=y.constructor;return I(new C(y),k(y)+1,C.rounding)},h.toNumber=function(){return+this},h.toPower=h.pow=function(y){var C,O,D,N,T,$,Y=this,J=Y.constructor,fe=12,pe=+(y=new J(y));if(!y.s)return new J(c);if(Y=new J(Y),!Y.s){if(y.s<1)throw Error(a+"Infinity");return Y}if(Y.eq(c))return Y;if(D=J.precision,y.eq(c))return I(Y,D);if(C=y.e,O=y.d.length-1,$=C>=O,T=Y.s,$){if((O=pe<0?-pe:pe)<=p){for(N=new J(c),C=Math.ceil(D/f+4),n=!1;O%2&&(N=N.times(Y),X(N.d,C)),O=l(O/2),O!==0;)Y=Y.times(Y),X(Y.d,C);return n=!0,y.s<0?new J(c).div(N):I(N,D)}}else if(T<0)throw Error(a+"NaN");return T=T<0&&y.d[Math.max(C,O)]&1?-1:1,Y.s=1,n=!1,N=y.times(E(Y,D+fe)),n=!0,N=A(N),N.s=T,N},h.toPrecision=function(y,C){var O,D,N=this,T=N.constructor;return y===void 0?(O=k(N),D=z(N,O<=T.toExpNeg||O>=T.toExpPos)):(m(y,1,t),C===void 0?C=T.rounding:m(C,0,8),N=I(new T(N),y,C),O=k(N),D=z(N,y<=O||O<=T.toExpNeg,y)),D},h.toSignificantDigits=h.tosd=function(y,C){var O=this,D=O.constructor;return y===void 0?(y=D.precision,C=D.rounding):(m(y,1,t),C===void 0?C=D.rounding:m(C,0,8)),I(new D(O),y,C)},h.toString=h.valueOf=h.val=h.toJSON=function(){var y=this,C=k(y),O=y.constructor;return z(y,C<=O.toExpNeg||C>=O.toExpPos)};function b(y,C){var O,D,N,T,$,Y,J,fe,pe=y.constructor,ge=pe.precision;if(!y.s||!C.s)return C.s||(C=new pe(y)),n?I(C,ge):C;if(J=y.d,fe=C.d,$=y.e,N=C.e,J=J.slice(),T=$-N,T){for(T<0?(D=J,T=-T,Y=fe.length):(D=fe,N=$,Y=J.length),$=Math.ceil(ge/f),Y=$>Y?$+1:Y+1,T>Y&&(T=Y,D.length=1),D.reverse();T--;)D.push(0);D.reverse()}for(Y=J.length,T=fe.length,Y-T<0&&(T=Y,D=fe,fe=J,J=D),O=0;T;)O=(J[--T]=J[T]+fe[T]+O)/d|0,J[T]%=d;for(O&&(J.unshift(O),++N),Y=J.length;J[--Y]==0;)J.pop();return C.d=J,C.e=N,n?I(C,ge):C}function m(y,C,O){if(y!==~~y||yO)throw Error(i+y)}function g(y){var C,O,D,N=y.length-1,T="",$=y[0];if(N>0){for(T+=$,C=1;C$?1:-1;else for(Y=J=0;YN[Y]?1:-1;break}return J}function O(D,N,T){for(var $=0;T--;)D[T]-=$,$=D[T]1;)D.shift()}return function(D,N,T,$){var Y,J,fe,pe,ge,ze,se,Ne,F,H,q,ee,ie,K,xe,Ae,we,Ke,xr=D.constructor,To=D.s==N.s?1:-1,Kr=D.d,It=N.d;if(!D.s)return new xr(D);if(!N.s)throw Error(a+"Division by zero");for(J=D.e-N.e,we=It.length,xe=Kr.length,se=new xr(To),Ne=se.d=[],fe=0;It[fe]==(Kr[fe]||0);)++fe;if(It[fe]>(Kr[fe]||0)&&--J,T==null?ee=T=xr.precision:$?ee=T+(k(D)-k(N))+1:ee=T,ee<0)return new xr(0);if(ee=ee/f+2|0,fe=0,we==1)for(pe=0,It=It[0],ee++;(fe1&&(It=y(It,pe),Kr=y(Kr,pe),we=It.length,xe=Kr.length),K=we,F=Kr.slice(0,we),H=F.length;H=d/2&&++Ae;do pe=0,Y=C(It,F,we,H),Y<0?(q=F[0],we!=H&&(q=q*d+(F[1]||0)),pe=q/Ae|0,pe>1?(pe>=d&&(pe=d-1),ge=y(It,pe),ze=ge.length,H=F.length,Y=C(ge,F,ze,H),Y==1&&(pe--,O(ge,we16)throw Error(o+k(y));if(!y.s)return new pe(c);for(C==null?(n=!1,Y=ge):Y=C,$=new pe(.03125);y.abs().gte(.1);)y=y.times($),fe+=5;for(D=Math.log(s(2,fe))/Math.LN10*2+5|0,Y+=D,O=N=T=new pe(c),pe.precision=Y;;){if(N=I(N.times(y),Y),O=O.times(++J),$=T.plus(x(N,O,Y)),g($.d).slice(0,Y)===g(T.d).slice(0,Y)){for(;fe--;)T=I(T.times(T),Y);return pe.precision=ge,C==null?(n=!0,I(T,ge)):T}T=$}}function k(y){for(var C=y.e*f,O=y.d[0];O>=10;O/=10)C++;return C}function _(y,C,O){if(C>y.LN10.sd())throw n=!0,O&&(y.precision=O),Error(a+"LN10 precision limit exceeded");return I(new y(y.LN10),C)}function M(y){for(var C="";y--;)C+="0";return C}function E(y,C){var O,D,N,T,$,Y,J,fe,pe,ge=1,ze=10,se=y,Ne=se.d,F=se.constructor,H=F.precision;if(se.s<1)throw Error(a+(se.s?"NaN":"-Infinity"));if(se.eq(c))return new F(0);if(C==null?(n=!1,fe=H):fe=C,se.eq(10))return C==null&&(n=!0),_(F,fe);if(fe+=ze,F.precision=fe,O=g(Ne),D=O.charAt(0),T=k(se),Math.abs(T)<15e14){for(;D<7&&D!=1||D==1&&O.charAt(1)>3;)se=se.times(y),O=g(se.d),D=O.charAt(0),ge++;T=k(se),D>1?(se=new F("0."+O),T++):se=new F(D+"."+O.slice(1))}else return J=_(F,fe+2,H).times(T+""),se=E(new F(D+"."+O.slice(1)),fe-ze).plus(J),F.precision=H,C==null?(n=!0,I(se,H)):se;for(Y=$=se=x(se.minus(c),se.plus(c),fe),pe=I(se.times(se),fe),N=3;;){if($=I($.times(pe),fe),J=Y.plus(x($,new F(N),fe)),g(J.d).slice(0,fe)===g(Y.d).slice(0,fe))return Y=Y.times(2),T!==0&&(Y=Y.plus(_(F,fe+2,H).times(T+""))),Y=x(Y,new F(ge),fe),F.precision=H,C==null?(n=!0,I(Y,H)):Y;Y=J,N+=2}}function L(y,C){var O,D,N;for((O=C.indexOf("."))>-1&&(C=C.replace(".","")),(D=C.search(/e/i))>0?(O<0&&(O=D),O+=+C.slice(D+1),C=C.substring(0,D)):O<0&&(O=C.length),D=0;C.charCodeAt(D)===48;)++D;for(N=C.length;C.charCodeAt(N-1)===48;)--N;if(C=C.slice(D,N),C){if(N-=D,O=O-D-1,y.e=l(O/f),y.d=[],D=(O+1)%f,O<0&&(D+=f),Dv||y.e<-v))throw Error(o+O)}else y.s=0,y.e=0,y.d=[0];return y}function I(y,C,O){var D,N,T,$,Y,J,fe,pe,ge=y.d;for($=1,T=ge[0];T>=10;T/=10)$++;if(D=C-$,D<0)D+=f,N=C,fe=ge[pe=0];else{if(pe=Math.ceil((D+1)/f),T=ge.length,pe>=T)return y;for(fe=T=ge[pe],$=1;T>=10;T/=10)$++;D%=f,N=D-f+$}if(O!==void 0&&(T=s(10,$-N-1),Y=fe/T%10|0,J=C<0||ge[pe+1]!==void 0||fe%T,J=O<4?(Y||J)&&(O==0||O==(y.s<0?3:2)):Y>5||Y==5&&(O==4||J||O==6&&(D>0?N>0?fe/s(10,$-N):0:ge[pe-1])%10&1||O==(y.s<0?8:7))),C<1||!ge[0])return J?(T=k(y),ge.length=1,C=C-T-1,ge[0]=s(10,(f-C%f)%f),y.e=l(-C/f)||0):(ge.length=1,ge[0]=y.e=y.s=0),y;if(D==0?(ge.length=pe,T=1,pe--):(ge.length=pe+1,T=s(10,f-D),ge[pe]=N>0?(fe/s(10,$-N)%s(10,N)|0)*T:0),J)for(;;)if(pe==0){(ge[0]+=T)==d&&(ge[0]=1,++y.e);break}else{if(ge[pe]+=T,ge[pe]!=d)break;ge[pe--]=0,T=1}for(D=ge.length;ge[--D]===0;)ge.pop();if(n&&(y.e>v||y.e<-v))throw Error(o+k(y));return y}function j(y,C){var O,D,N,T,$,Y,J,fe,pe,ge,ze=y.constructor,se=ze.precision;if(!y.s||!C.s)return C.s?C.s=-C.s:C=new ze(y),n?I(C,se):C;if(J=y.d,ge=C.d,D=C.e,fe=y.e,J=J.slice(),$=fe-D,$){for(pe=$<0,pe?(O=J,$=-$,Y=ge.length):(O=ge,D=fe,Y=J.length),N=Math.max(Math.ceil(se/f),Y)+2,$>N&&($=N,O.length=1),O.reverse(),N=$;N--;)O.push(0);O.reverse()}else{for(N=J.length,Y=ge.length,pe=N0;--N)J[Y++]=0;for(N=ge.length;N>$;){if(J[--N]0?T=T.charAt(0)+"."+T.slice(1)+M(D):$>1&&(T=T.charAt(0)+"."+T.slice(1)),T=T+(N<0?"e":"e+")+N):N<0?(T="0."+M(-N-1)+T,O&&(D=O-$)>0&&(T+=M(D))):N>=$?(T+=M(N+1-$),O&&(D=O-N-1)>0&&(T=T+"."+M(D))):((D=N+1)<$&&(T=T.slice(0,D)+"."+T.slice(D)),O&&(D=O-$)>0&&(N+1===$&&(T+="."),T+=M(D))),y.s<0?"-"+T:T}function X(y,C){if(y.length>C)return y.length=C,!0}function R(y){var C,O,D;function N(T){var $=this;if(!($ instanceof N))return new N(T);if($.constructor=N,T instanceof N){$.s=T.s,$.e=T.e,$.d=(T=T.d)?T.slice():T;return}if(typeof T=="number"){if(T*0!==0)throw Error(i+T);if(T>0)$.s=1;else if(T<0)T=-T,$.s=-1;else{$.s=0,$.e=0,$.d=[0];return}if(T===~~T&&T<1e7){$.e=0,$.d=[T];return}return L($,T.toString())}else if(typeof T!="string")throw Error(i+T);if(T.charCodeAt(0)===45?(T=T.slice(1),$.s=-1):$.s=1,u.test(T))L($,T);else throw Error(i+T)}if(N.prototype=h,N.ROUND_UP=0,N.ROUND_DOWN=1,N.ROUND_CEIL=2,N.ROUND_FLOOR=3,N.ROUND_HALF_UP=4,N.ROUND_HALF_DOWN=5,N.ROUND_HALF_EVEN=6,N.ROUND_HALF_CEIL=7,N.ROUND_HALF_FLOOR=8,N.clone=R,N.config=N.set=Q,y===void 0&&(y={}),y)for(D=["precision","rounding","toExpNeg","toExpPos","LN10"],C=0;C=N[C+1]&&D<=N[C+2])this[O]=D;else throw Error(i+O+": "+D);if((D=y[O="LN10"])!==void 0)if(D==Math.LN10)this[O]=new this(D);else throw Error(i+O+": "+D);return this}r=R(r),r.default=r.Decimal=r,c=new r(1),typeof define=="function"&&define.amd?define(function(){return r}):typeof hh<"u"&&hh.exports?hh.exports=r:(e||(e=typeof self<"u"&&self&&self.self==self?self:Function("return this")()),e.Decimal=r)})(g3)});var g8=kr((Tpe,Uw)=>{"use strict";var uY=Object.prototype.hasOwnProperty,Wr="~";function gf(){}Object.create&&(gf.prototype=Object.create(null),new gf().__proto__||(Wr=!1));function cY(e,t,r){this.fn=e,this.context=t,this.once=r||!1}function h8(e,t,r,n,a){if(typeof r!="function")throw new TypeError("The listener must be a function");var i=new cY(r,n||e,a),o=Wr?Wr+t:t;return e._events[o]?e._events[o].fn?e._events[o]=[e._events[o],i]:e._events[o].push(i):(e._events[o]=i,e._eventsCount++),e}function I0(e,t){--e._eventsCount===0?e._events=new gf:delete e._events[t]}function Ir(){this._events=new gf,this._eventsCount=0}Ir.prototype.eventNames=function(){var t=[],r,n;if(this._eventsCount===0)return t;for(n in r=this._events)uY.call(r,n)&&t.push(Wr?n.slice(1):n);return Object.getOwnPropertySymbols?t.concat(Object.getOwnPropertySymbols(r)):t};Ir.prototype.listeners=function(t){var r=Wr?Wr+t:t,n=this._events[r];if(!n)return[];if(n.fn)return[n.fn];for(var a=0,i=n.length,o=new Array(i);att.default.createElement("g",{stroke:"none",fill:"none",fillRule:"evenodd",strokeWidth:"1"},tt.default.createElement("g",{transform:"translate(25, 475) scale(-1, 1) rotate(-180) translate(-25, -475) translate(0, 450)"},tt.default.createElement("g",{fillRule:"nonzero"},tt.default.createElement("path",{d:"M421,271.3 C422.733,265.1 423.667,244.633 423.8,209.9 C423.9,173.4 423.8,142.1 423.5,140.4 C423.1,137.9 412.5,126.8 373.1,87.2 C345.6,59.7 321.7,36.3 319.8,35.3 C316.7,33.6 312.5,33.5 250,33.5 C186.5,33.5 183.3,33.6 178.2,35.4 C173.5,37.2 168.3,42.1 125.2,85.3 C69.3,141.4 75.6,134.9 73.7,138.7 C72.3,141.3 72,147.2 71.8,178.6 C71.3,243.6 71.8,276 73.4,276 C74.067,276 99.6,304.854 150,362.563 L350.015,362.563 L421,271.3 Z",fill:"#EB5555",stroke:"#000000",strokeWidth:"12"})),tt.default.createElement("path",{d:"M216.5,202.5 C213.4,199.5 213.4,198.7 216.5,193.4 C223.2,182 248.2,176 264.5,181.9 C273,185 280.3,190.1 282.4,194.5 C284,197.9 284,198.3 282.5,200.6 C281.6,202 279.8,203.6 278.5,204.2 C276.4,205.1 275.4,204.7 270.1,200.8 C263,195.4 254.7,193.3 244.4,194.4 C236.1,195.3 234.3,196 228.6,200.9 C223.1,205.6 220,206.1 216.5,202.5 Z",fill:"#000000",fillRule:"nonzero"}),tt.default.createElement("path",{d:"M177.8,484.5 C169.1,482.1 162,475.2 157.7,465 C156.2,461.4 151.9,446.6 148.1,432 C144.3,417.4 140.7,404.3 140.1,402.8 L139,400 L94.8,400 C70.4,400 49.5,399.7 48.2,399.4 C45.1,398.5 42.7,393.8 43.9,390.8 C45.1,387.9 49.4,383.3 54,379.9 C68.7,369.2 86.3,364.8 120,363.4 C126.067,363.2 134.065,362.921 143.995,362.563 L350.015,362.563 C367.433,363.767 377.5,364.1 377.5,364.1 C411,365.2 433.1,371.5 446.2,383.8 C453.8,390.9 455.1,394.7 451.3,399 C451,399.3 429.6,399.7 403.7,400 L356.6,400.5 L353.3,412.5 C351.5,419.1 348.2,431.3 346.1,439.5 C339.9,463 336.7,470.5 329.4,477.5 C322.9,483.8 315.7,486.3 305.5,485.8 C298.1,485.4 296.1,484.7 280.5,477.8 C271.2,473.7 261.7,469.7 259.5,468.9 C250.9,466.1 238.1,467.9 224.9,473.8 C197.3,486.1 189.8,487.8 177.8,484.5 Z",stroke:"#000000",strokeWidth:"11",fill:"#000000",fillRule:"nonzero"}),tt.default.createElement("g",{transform:"translate(75.105, 196.182)"},tt.default.createElement("ellipse",{fill:"#F5E6D3",cx:"87",cy:"55",rx:"35",ry:"29"}),tt.default.createElement("ellipse",{fill:"#5D4037",cx:"87",cy:"57",rx:"20",ry:"20"}),tt.default.createElement("ellipse",{fill:"#2D1F1F",cx:"87",cy:"57",rx:"12",ry:"12"}),tt.default.createElement("ellipse",{fill:"#FFFFFF",cx:"80",cy:"51",rx:"5",ry:"5"}),tt.default.createElement("ellipse",{fill:"#F5E6D3",cx:"259",cy:"55",rx:"35",ry:"29"}),tt.default.createElement("ellipse",{fill:"#5D4037",cx:"259",cy:"57",rx:"20",ry:"20"}),tt.default.createElement("ellipse",{fill:"#2D1F1F",cx:"259",cy:"57",rx:"12",ry:"12"}),tt.default.createElement("ellipse",{fill:"#FFFFFF",cx:"252",cy:"51",rx:"5",ry:"5"})),tt.default.createElement("g",{transform:"translate(75.105, 196.182)",fillRule:"nonzero"},tt.default.createElement("g",{transform:"translate(267.395, 54.835) scale(-1, 1) translate(-267.395, -54.835)"},tt.default.createElement("path",{d:"M346.895,99.881 L335.287,108.67 L292.495,108.67 L211.805,108.67 C194.861,92.394 186.922,84.257 187.99,84.257 C188.99,84.257 187.689,77.518 187.99,71.118 C188.99,54.518 196.996,38.618 211.805,23.818 C219.711,15.918 222.813,13.518 230.118,10.018 C242.025,4.118 250.431,1.818 262.839,1.118 C274.147,0.518 285.354,2.218 295.561,6.018 C308.67,10.918 327.482,26.818 335.287,39.618 C342.092,50.918 346.895,65.818 346.895,76.018 C346.895,82.818 346.895,90.772 346.895,99.881 Z",stroke:"#000000",strokeWidth:"8",fill:"#000000",fillOpacity:"0.92"})),tt.default.createElement("path",{d:"M158.895,99.881 L147.295,108.67 L104.531,108.67 L23.895,108.67 C6.961,92.394 -0.972,84.257 0.095,84.257 C1.095,84.257 -0.205,77.518 0.095,71.118 C1.095,54.518 9.095,38.618 23.895,23.818 C31.795,15.918 34.895,13.518 42.195,10.018 C54.095,4.118 62.495,1.818 74.895,1.118 C86.195,0.518 97.395,2.218 107.595,6.018 C120.695,10.918 139.495,26.818 147.295,39.618 C154.095,50.918 158.895,65.818 158.895,76.018 C158.895,82.818 158.895,90.772 158.895,99.881 Z",stroke:"#000000",strokeWidth:"8",fill:"#000000",fillOpacity:"0.92"})),tt.default.createElement("g",{transform:"translate(75.105, 196.182)",fillRule:"nonzero"},tt.default.createElement("path",{d:"M187.895,73.418 C187.895,60.818 195.095,43.418 205.495,30.818 C213.379,21.259 220.29,15.351 230.066,10.615 C233.275,8.66 236.386,7.08 239.229,6.018 C246.892,3.165 255.119,1.496 263.529,1.095 C267.063,0.859 270.58,0.912 274.131,1.261 C285.374,2.105 293.475,4.47 304.672,10.018 C311.734,13.402 314.868,15.757 322.213,23.049 L322.795,23.618 C326.306,27.085 329.397,30.586 332.082,34.15 L332.148,34.235 C341.116,45.994 346.03,58.349 346.8,71.118 C346.957,74.467 346.676,77.909 346.515,80.429 C346.693,80.646 346.917,80.701 347.195,80.618 C347.366,80.542 348.039,80.501 349.213,80.495 L349.775,80.495 C352.888,80.507 358.769,80.715 367.418,81.118 C374.646,86.469 378.26,91.703 378.26,96.818 C378.26,101.933 374.646,107.042 367.418,112.146 L15.403,112.146 L-20.38,113.348 C-27.807,110.322 -31.52,105.629 -31.52,99.27 C-31.52,92.911 -27.807,88.087 -20.38,84.798 C-13.63,84.605 -8.537,84.463 -5.101,84.373 C-5.115,84.296 -5.084,84.257 -5.01,84.257 C-4.01,84.257 -5.311,77.518 -5.01,71.118 C-4.01,54.518 3.996,38.618 18.805,23.818 C26.711,15.918 29.813,13.518 37.118,10.018 C49.025,4.118 57.431,1.818 69.839,1.118 C72.166,0.994 74.489,0.968 76.8,1.038 C76.165,1.058 75.53,1.084 74.895,1.118 C62.495,1.818 54.095,4.118 42.195,10.018 C34.895,13.518 31.795,15.918 23.895,23.818 C9.095,38.618 1.095,54.518 0.095,71.118 C-0.205,77.518 1.095,84.257 0.095,84.257 C-0.4,84.257 -2.131,84.296 -5.101,84.373 C-4.938,85.42 3.031,93.518 18.805,108.67 L142.287,108.67 L153.895,99.881 L153.895,76.018 C153.895,65.818 149.092,50.918 142.287,39.618 C134.482,26.818 115.67,10.918 102.561,6.018 C94.455,3 85.717,1.307 76.8,1.038 C87.451,0.716 97.968,2.432 107.595,6.018 C120.695,10.918 139.495,26.818 147.295,39.618 C154.095,50.918 158.895,65.818 158.895,76.018 L158.895,80.418 L164.995,80.418 C168.395,80.118 174.995,79.818 179.495,79.818 L187.895,79.818 L187.895,99.881 L199.502,108.67 L322.984,108.67 C339.929,92.394 347.867,84.257 346.8,84.257 C346.323,84.257 346.369,82.725 346.515,80.429 C346.051,79.868 345.895,78.223 345.895,75.118 C345.895,59.873 341.479,46.625 332.082,34.15 L331.696,33.647 C329.117,30.321 326.212,27.043 322.984,23.818 C322.722,23.556 322.465,23.299 322.213,23.049 C316.441,17.437 312.579,14.708 306.095,11.418 C294.424,5.583 284.144,2.245 274.131,1.261 C273.417,1.207 272.691,1.16 271.95,1.118 C269.132,0.968 266.321,0.962 263.529,1.095 C257.389,1.506 251.198,2.792 244.695,4.918 C239.073,6.731 234.301,8.563 230.066,10.615 C218.478,17.668 205.615,29.594 199.502,39.618 C192.698,50.917 187.895,65.817 187.895,76.017 L187.895,73.418 Z",stroke:"#000000",strokeWidth:"8",fill:"#000000"})),tt.default.createElement("g",{transform:"translate(83.563, 252.212)",fill:"#FFFFFF",fillRule:"nonzero"},tt.default.createElement("path",{d:"M12.837,35.288 C13.137,33.988 14.437,31.888 15.637,30.788 L17.837,28.788 L15.637,26.788 C14.437,25.688 13.137,23.588 12.837,22.288 C12.037,19.088 10.637,19.088 9.437,22.188 C8.937,23.488 7.537,25.588 6.237,26.788 C4.037,28.788 4.037,28.988 5.737,29.988 C6.837,30.488 8.237,32.488 9.037,34.388 C10.737,38.388 11.937,38.688 12.837,35.288 Z M29.237,33.088 C30.137,29.688 31.837,27.088 34.937,24.088 L39.337,19.888 L36.337,17.788 C33.437,15.588 28.437,7.288 28.437,4.288 C28.437,2.188 26.537,2.388 25.837,4.588 C23.937,10.388 21.637,14.388 18.337,17.188 L14.537,20.388 L17.337,22.088 C19.837,23.588 24.337,30.988 25.837,36.088 C26.737,39.088 27.937,37.988 29.237,33.088 Z M10.937,12.488 L13.037,9.288 L10.937,6.088 C8.837,2.788 7.437,1.888 7.437,3.988 C7.437,4.588 6.537,6.088 5.437,7.288 L3.437,9.488 L5.437,11.288 C6.537,12.288 7.437,13.688 7.437,14.488 C7.437,16.688 8.837,15.888 10.937,12.488 Z"}),tt.default.createElement("path",{d:"M203.437,36.388 C203.437,35.588 204.637,33.588 206.037,31.888 L208.637,28.788 L206.037,25.688 C204.637,23.988 203.437,21.988 203.437,21.188 C203.437,18.588 201.437,19.788 200.037,23.188 C199.237,25.088 197.837,27.088 196.737,27.688 C195.037,28.688 195.037,28.788 197.037,30.188 C198.237,30.988 199.737,33.088 200.437,34.788 C201.737,37.888 203.437,38.788 203.437,36.388 Z M220.837,32.588 C221.937,29.288 224.237,25.788 226.737,23.388 C229.737,20.488 230.537,19.288 229.437,18.988 C226.637,17.988 222.437,12.588 220.737,7.688 C219.737,4.988 218.737,2.788 218.337,2.788 C218.037,2.788 216.737,5.288 215.537,8.288 C214.337,11.288 211.737,15.088 209.837,16.688 C205.937,20.188 205.637,21.388 208.437,22.288 C210.937,23.088 214.737,28.788 216.437,34.088 C217.037,36.088 217.937,37.788 218.237,37.788 C218.637,37.788 219.837,35.388 220.837,32.588 Z M201.137,14.088 C201.437,13.088 202.437,11.488 203.337,10.488 C204.337,9.288 204.537,8.388 203.837,7.988 C203.237,7.588 202.237,6.288 201.537,5.088 C200.137,2.388 198.837,2.188 198.037,4.588 C197.737,5.488 196.937,6.988 196.337,7.888 C195.337,9.288 195.437,9.888 196.737,11.288 C197.637,12.188 198.437,13.588 198.437,14.388 C198.437,16.388 200.337,16.188 201.137,14.088 Z"}))));function Tt({size:e=200,instructions:t=[],tools:r=[],showDecorations:n=!1,className:a="",hat:i,hatAccessory:o,heldItem:l,theme:s,customColors:u}){return tt.default.createElement("div",{className:`inline-flex flex-col items-center ${a}`},n&&t.length>0&&tt.default.createElement("div",{className:"flex gap-1 mb-1"},t.map(c=>{let d=Jo[c];return d?tt.default.createElement("span",{key:c,className:"text-sm",title:d.label},d.emoji):null})),tt.default.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 500 500",width:e,height:e,role:"img","aria-label":"Active Agent mascot"},tt.default.createElement("title",null,"ActiveAgent"),tt.default.createElement(xz,null)),n&&r.length>0&&tt.default.createElement("div",{className:"flex gap-1 mt-1"},r.map(c=>{let d=Ro[c];return d?tt.default.createElement("span",{key:c,className:"text-sm",title:d.label},d.emoji):null})))}var tn=te(ae());var oa=te(ae()),tE=(0,oa.createContext)();function rE({children:e}){let[t,r]=(0,oa.useState)(()=>{let a=localStorage.getItem("dashboard-theme");return a?a==="dark":window.matchMedia("(prefers-color-scheme: dark)").matches});(0,oa.useEffect)(()=>{localStorage.setItem("dashboard-theme",t?"dark":"light")},[t]),(0,oa.useEffect)(()=>{let a=window.matchMedia("(prefers-color-scheme: dark)"),i=o=>{localStorage.getItem("dashboard-theme")||r(o.matches)};return a.addEventListener("change",i),()=>a.removeEventListener("change",i)},[]);let n=()=>r(!t);return oa.default.createElement(tE.Provider,{value:{darkMode:t,toggleDarkMode:n}},e)}function pt(){let e=(0,oa.useContext)(tE);if(!e)throw new Error("useTheme must be used within a ThemeProvider");return e}function Vs({name:e,subtitle:t,badge:r,accentColor:n,stats:a=[],footer:i,onClick:o,actions:l}){let{darkMode:s}=pt(),[u,c]=tn.default.useState(!1),d={cardBg:s?"rgba(255,255,255,0.05)":"#ffffff",border:s?"rgba(255,255,255,0.1)":"#e5e7eb",borderHover:s?"rgba(255,255,255,0.2)":"#d1d5db",tileBg:s?"rgba(255,255,255,0.05)":"#f9fafb",textPrimary:s?"#f9fafb":"#111827",textSecondary:s?"#9ca3af":"#6b7280",textMuted:s?"#6b7280":"#9ca3af"},f=p=>p==="good"?s?"#4ade80":"#16a34a":p==="warn"?s?"#facc15":"#ca8a04":p==="bad"?s?"#f87171":"#dc2626":p==="muted"?d.textMuted:d.textPrimary;return tn.default.createElement("div",{"data-testid":"agent-card","data-agent-name":typeof e=="string"?e:void 0,onClick:o,onMouseEnter:()=>c(!0),onMouseLeave:()=>c(!1),role:o?"button":void 0,tabIndex:o?0:void 0,onKeyDown:p=>{o&&(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),o(p))},style:{background:d.cardBg,borderTop:`1px solid ${u?d.borderHover:d.border}`,borderRight:`1px solid ${u?d.borderHover:d.border}`,borderBottom:`1px solid ${u?d.borderHover:d.border}`,borderLeft:n?`4px solid ${n}`:`1px solid ${u?d.borderHover:d.border}`,borderRadius:"12px",padding:"16px",cursor:o?"pointer":"default",transition:"border-color 0.15s ease, box-shadow 0.15s ease",boxShadow:u&&o?"0 4px 16px rgba(0,0,0,0.08)":"none",display:"flex",flexDirection:"column",gap:"12px"}},tn.default.createElement("div",{style:{display:"flex",alignItems:"flex-start",justifyContent:"space-between",gap:"8px"}},tn.default.createElement("div",{style:{minWidth:0}},tn.default.createElement("div",{style:{fontWeight:600,fontSize:"15px",color:u&&o?"#ef4444":d.textPrimary,transition:"color 0.15s ease",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},title:e},e),t&&tn.default.createElement("div",{style:{fontSize:"13px",color:d.textSecondary,marginTop:"2px",display:"-webkit-box",WebkitLineClamp:2,WebkitBoxOrient:"vertical",overflow:"hidden"}},t)),r&&tn.default.createElement("div",{style:{flexShrink:0}},r)),a.length>0&&tn.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"repeat(3, 1fr)",gap:"6px"}},a.map(p=>tn.default.createElement("div",{key:p.label,title:p.title,style:{background:d.tileBg,borderRadius:"8px",padding:"6px 8px",textAlign:"center"}},tn.default.createElement("div",{style:{fontSize:"14px",fontWeight:600,lineHeight:1.2,color:f(p.tone)}},p.value),tn.default.createElement("div",{style:{fontSize:"10px",textTransform:"uppercase",letterSpacing:"0.04em",color:d.textMuted,whiteSpace:"nowrap"}},p.label)))),i&&tn.default.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",gap:"8px",fontSize:"12px",color:d.textMuted}},i),l&&tn.default.createElement("div",{style:{opacity:u?1:0,transition:"opacity 0.15s ease"}},l))}function bb(e){return e==null?"muted":e>=.85?"good":e>=.7?"warn":"bad"}var bz=[{value:"recent",label:"Recently updated"},{value:"popular",label:"Most runs"},{value:"longest",label:"Longest average"},{value:"cost",label:"Highest cost"},{value:"tokens",label:"Most tokens"}];function wb({agents:e,meta:t,onSelect:r,onNew:n,onBrowseTemplates:a,onDuplicate:i,onDelete:o,onRefresh:l,sort:s="recent",onSortChange:u,isLoading:c}){let[d,f]=(0,Ee.useState)(""),[p,v]=(0,Ee.useState)(""),[h,b]=(0,Ee.useState)(""),m=e.filter(E=>{let L=!d||E.name.toLowerCase().includes(d.toLowerCase())||E.description?.toLowerCase().includes(d.toLowerCase()),I=!p||E.provider===p,j=!h||E.status===h;return L&&I&&j}),g=E=>{let L=new Date(E),j=new Date-L,z=Math.floor(j/(1e3*60*60*24));return z===0?"Today":z===1?"Yesterday":z<7?`${z} days ago`:L.toLocaleDateString()},x=E=>{switch(E){case"active":return"bg-green-100 text-green-700";case"draft":return"bg-yellow-100 text-yellow-700";case"archived":return"bg-gray-100 text-gray-500";default:return"bg-gray-100 text-gray-500"}},A=E=>E==null?"\u2014":E<1e3?`${Math.round(E)}ms`:`${(E/1e3).toFixed(1)}s`,k=E=>E==null?"\u2014":E>=1e6?`${(E/1e6).toFixed(1)}M`:E>=1e3?`${(E/1e3).toFixed(1)}k`:`${E}`,_=E=>E==null?"\u2014":E>0&&E<.01?"<$0.01":`$${E.toFixed(2)}`,M=E=>{if(!E)return"\u2014";let L=Math.floor((new Date-new Date(E))/(1e3*60*60*24));return L<=0?"Today":L<30?`${L}d ago`:new Date(E).toLocaleDateString()};return Ee.default.createElement("div",{className:"space-y-6"},Ee.default.createElement("div",{className:"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4"},Ee.default.createElement("div",{className:"flex-1 flex items-center space-x-4"},Ee.default.createElement("div",{className:"relative flex-1 max-w-md"},Ee.default.createElement("input",{type:"text",placeholder:"Search agents...",value:d,onChange:E=>f(E.target.value),className:"w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"}),Ee.default.createElement("svg",{className:"absolute left-3 top-2.5 h-5 w-5 text-gray-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},Ee.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"}))),Ee.default.createElement("select",{value:p,onChange:E=>v(E.target.value),className:"px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500"},Ee.default.createElement("option",{value:""},"All Providers"),t.providers?.map(E=>Ee.default.createElement("option",{key:E,value:E},E.charAt(0).toUpperCase()+E.slice(1)))),Ee.default.createElement("select",{value:h,onChange:E=>b(E.target.value),className:"px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500"},Ee.default.createElement("option",{value:""},"All Status"),Ee.default.createElement("option",{value:"active"},"Active"),Ee.default.createElement("option",{value:"draft"},"Draft"),Ee.default.createElement("option",{value:"archived"},"Archived")),Ee.default.createElement("select",{value:s,onChange:E=>u?.(E.target.value),className:"px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500",title:"Rank agents by their scorecard"},bz.map(E=>Ee.default.createElement("option",{key:E.value,value:E.value},E.label)))),Ee.default.createElement("div",{className:"flex items-center space-x-3"},Ee.default.createElement("button",{onClick:l,disabled:c,className:"p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-lg transition-colors"},Ee.default.createElement("svg",{className:`h-5 w-5 ${c?"animate-spin":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},Ee.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"}))),Ee.default.createElement("button",{onClick:a,className:"flex items-center space-x-2 px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"},Ee.default.createElement("svg",{className:"h-5 w-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},Ee.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"})),Ee.default.createElement("span",null,"Browse Templates")),Ee.default.createElement("button",{onClick:n,className:"flex items-center space-x-2 px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors"},Ee.default.createElement("svg",{className:"h-5 w-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},Ee.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 4v16m8-8H4"})),Ee.default.createElement("span",null,"New Agent")))),m.length>0?Ee.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"},m.map(E=>{let L=E.stats||{},I=L.run_sources||[],j=I.length?`Counted from ${I.map(z=>z==="platform"?"dashboard runs":"reported telemetry").join(" + ")}`:void 0;return Ee.default.createElement(Vs,{key:E.id,name:E.name,subtitle:E.description||"No description",badge:Ee.default.createElement("span",{className:`px-2 py-0.5 text-xs font-medium rounded-full ${x(E.status)}`},E.status),onClick:()=>r(E),stats:[{label:`Runs ${L.window_days||30}d`,value:L.runs??0,title:j},{label:"Success",value:L.success_rate!=null?`${Math.round(L.success_rate)}%`:"\u2014",tone:bb(L.success_rate!=null?L.success_rate/100:null)},{label:"Avg time",value:A(L.avg_duration_ms)},{label:L.eval_samples_evaluated?`Eval ${L.eval_samples_passed}/${L.eval_samples_evaluated}`:"Eval",title:L.eval_samples_evaluated?`Latest evaluation: ${L.eval_samples_passed} of ${L.eval_samples_evaluated} samples passed`:void 0,value:L.eval_score!=null?`${Math.round(L.eval_score*100)}%`:"\u2014",tone:bb(L.eval_score)},{label:"Tokens",value:k(L.tokens)},{label:"Cost",value:_(L.cost),title:"Estimated from token counts at each model's published rates",tone:L.cost==null?"muted":void 0}],footer:Ee.default.createElement(Ee.default.Fragment,null,Ee.default.createElement("span",{style:{display:"flex",alignItems:"center",gap:"6px",minWidth:0}},Ee.default.createElement("span",{style:{padding:"2px 6px",borderRadius:"4px",background:"rgba(128,128,128,0.15)",whiteSpace:"nowrap",flexShrink:0}},E.provider),Ee.default.createElement("span",{title:E.model,style:{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}},E.model)),Ee.default.createElement("span",{style:{whiteSpace:"nowrap"},title:`Updated ${g(E.updatedAt||E.updated_at)}`},L.last_run_at?`Last run ${M(L.last_run_at)}`:`Updated ${g(E.updatedAt||E.updated_at)}`)),actions:Ee.default.createElement("div",{style:{display:"flex",justifyContent:"space-between",fontSize:"13px"}},Ee.default.createElement("button",{onClick:z=>{z.stopPropagation(),i(E.id)},className:"text-gray-500 hover:text-red-600 transition-colors"},"Duplicate"),Ee.default.createElement("button",{onClick:z=>{z.stopPropagation(),o(E.id)},className:"text-red-500 hover:text-red-700 transition-colors"},"Delete"))})})):Ee.default.createElement("div",{className:"text-center py-16"},Ee.default.createElement("div",{className:"inline-block mb-4"},Ee.default.createElement(Tt,{size:100})),Ee.default.createElement("h3",{className:"text-lg font-medium text-gray-900 mb-2"},e.length===0?"No agents yet":"No matching agents"),Ee.default.createElement("p",{className:"text-gray-500 mb-6"},e.length===0?"Create your first AI agent to get started":"Try adjusting your search or filters"),e.length===0&&Ee.default.createElement("div",{className:"flex items-center justify-center space-x-4"},Ee.default.createElement("button",{onClick:a,className:"inline-flex items-center space-x-2 px-6 py-3 border-2 border-red-500 text-red-600 rounded-lg hover:bg-red-50 transition-colors"},Ee.default.createElement("svg",{className:"h-5 w-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},Ee.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"})),Ee.default.createElement("span",null,"Browse Templates")),Ee.default.createElement("span",{className:"text-gray-400"},"or"),Ee.default.createElement("button",{onClick:n,className:"inline-flex items-center space-x-2 px-6 py-3 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors"},Ee.default.createElement("svg",{className:"h-5 w-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},Ee.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 4v16m8-8H4"})),Ee.default.createElement("span",null,"Create From Scratch")))))}var ue=te(ae());var or={chevronRight:">",chevronDown:"v",chevronUp:"^",arrow:"->",arrowLeft:"<-",pipe:"|",corner:"+",branch:"|-",success:"[+]",error:"[!]",warning:"[?]",info:"[i]",pending:"[ ]",active:"[*]",add:"+",remove:"-",edit:"*",view:"@",run:">",stop:"x",refresh:"~",trace:"->",span:"|",metric:"#",log:">",agent:"@",action:"#",time:":",cost:"$",tokens:"T",memory:"M",nav:{agents:"@",newAgent:"+",demo:">",traces:"->",metrics:"#",evaluations:"=",interactions:"<>",benchmarks:"%%",replay:"[>]",docs:"?",github:"*"},spans:{root:">",prompt:"*",generate:"|>",llm:">>",thinking:"..",tool:"[]",response:"<|"}};var Oe={mono:"'SF Mono', 'Fira Code', 'Consolas', monospace",sizes:{xs:"10px",sm:"11px",base:"13px",md:"14px",lg:"16px",xl:"20px",xxl:"24px"},weights:{normal:400,medium:500,semibold:600,bold:700}};var Fs={openai:["gpt-5.1","gpt-5","gpt-5-mini","gpt-5-nano"],anthropic:["claude-opus-5","claude-fable-5","claude-sonnet-5","claude-opus-4-8","claude-haiku-4-5"],ollama:["qwen3:8b","llama3.2","mistral","gemma3"],openrouter:["anthropic/claude-sonnet-4.5","openai/gpt-5.1","meta-llama/llama-3.3-70b-instruct"]};async function Tm(e){try{let t=await fetch(`/api/provider_models?provider=${encodeURIComponent(e)}`);if(t.ok){let r=await t.json();if(Array.isArray(r.models)&&r.models.length>0)return r.models}}catch{}return Fs[e]||[]}var Mm=[{id:"basics",label:"Basics",icon:"1"},{id:"configure",label:"Configure",icon:"2"},{id:"review",label:"Review",icon:"3"}];function Sb({meta:e,onSave:t,onCancel:r,isLoading:n}){let[a,i]=(0,ue.useState)(0),[o,l]=(0,ue.useState)(Fs),[s,u]=(0,ue.useState)({name:"",description:"",provider:"openai",model:Fs.openai[0],instructions:"",preset_type:"terminal",instruction_sets:["github"],tools:["terminal","code"],model_config:{temperature:.7}});(0,ue.useEffect)(()=>{let h=!1;return Tm(s.provider).then(b=>{h||b.length===0||(l(m=>({...m,[s.provider]:b})),u(m=>b.includes(m.model)?m:{...m,model:b[0]}))}),()=>{h=!0}},[s.provider]);let c=(h,b)=>{u(m=>({...m,[h]:b}))},d=(h,b)=>{u(m=>{let g=m[h]||[],x=g.includes(b)?g.filter(A=>A!==b):[...g,b];return{...m,[h]:x}})},f=h=>{if(c("preset_type",h),Hs[h]){let b=Hs[h];c("instruction_sets",[...b.instructions]),c("tools",[...b.tools])}},p=()=>{switch(a){case 0:return s.name.trim().length>=2;case 1:return!0;case 2:return!0;default:return!1}},v=()=>{t(s)};return ue.default.createElement("div",{className:"max-w-4xl mx-auto"},ue.default.createElement("div",{className:"mb-8"},ue.default.createElement("div",{className:"flex items-center justify-between"},Mm.map((h,b)=>ue.default.createElement("div",{key:h.id,className:"flex items-center"},ue.default.createElement("div",{className:`flex items-center justify-center w-10 h-10 rounded-full border-2 transition-colors ${bi(a-1),className:"px-6 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors"},a===0?"Cancel":"Back"),ue.default.createElement("button",{onClick:a===Mm.length-1?v:()=>i(a+1),disabled:!p()||n,className:`px-6 py-2 rounded-lg transition-colors ${p()&&!n?"bg-red-500 text-white hover:bg-red-600":"bg-gray-200 text-gray-400 cursor-not-allowed"}`},a===Mm.length-1?"Create Agent":"Continue")))}function wz({formData:e,updateField:t,providerModels:r}){return ue.default.createElement("div",{className:"space-y-6"},ue.default.createElement("h2",{className:"text-xl font-semibold text-gray-900"},"Basic Information"),ue.default.createElement("p",{className:"text-gray-500"},"Give your agent a name and configure its AI provider."),ue.default.createElement("div",{className:"space-y-4"},ue.default.createElement("div",null,ue.default.createElement("label",{className:"block text-sm font-medium text-gray-700 mb-1"},"Agent Name *"),ue.default.createElement("input",{type:"text",value:e.name,onChange:n=>t("name",n.target.value),placeholder:"My Awesome Agent",className:"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"})),ue.default.createElement("div",null,ue.default.createElement("label",{className:"block text-sm font-medium text-gray-700 mb-1"},"Description"),ue.default.createElement("textarea",{value:e.description,onChange:n=>t("description",n.target.value),placeholder:"What does this agent do?",rows:3,className:"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"})),ue.default.createElement("div",{className:"grid grid-cols-2 gap-4"},ue.default.createElement("div",null,ue.default.createElement("label",{className:"block text-sm font-medium text-gray-700 mb-1"},"Provider"),ue.default.createElement("select",{value:e.provider,onChange:n=>{t("provider",n.target.value),t("model",r[n.target.value][0])},className:"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500"},Object.keys(r).map(n=>ue.default.createElement("option",{key:n,value:n},n.charAt(0).toUpperCase()+n.slice(1))))),ue.default.createElement("div",null,ue.default.createElement("label",{className:"block text-sm font-medium text-gray-700 mb-1"},"Model"),ue.default.createElement("select",{value:e.model,onChange:n=>t("model",n.target.value),className:"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500"},r[e.provider].map(n=>ue.default.createElement("option",{key:n,value:n},n))))),ue.default.createElement("div",null,ue.default.createElement("label",{className:"block text-sm font-medium text-gray-700 mb-1"},"Temperature: ",e.model_config.temperature),ue.default.createElement("input",{type:"range",min:"0",max:"1",step:"0.1",value:e.model_config.temperature,onChange:n=>t("model_config",{...e.model_config,temperature:parseFloat(n.target.value)}),className:"w-full accent-red-500"}),ue.default.createElement("div",{className:"flex justify-between text-xs text-gray-400"},ue.default.createElement("span",null,"Precise"),ue.default.createElement("span",null,"Creative")))))}function Sz({formData:e,meta:t,toggleArrayItem:r,updateField:n,selectPreset:a}){let i=Object.keys(Hs),o=Object.keys(Jo),l=Object.keys(Ro);return ue.default.createElement("div",{className:"space-y-6"},ue.default.createElement("h2",{className:"text-xl font-semibold text-gray-900"},"Configure Agent"),ue.default.createElement("p",{className:"text-gray-500"},"Choose a preset or customize instructions and tools."),ue.default.createElement("div",{className:"flex justify-center py-6 bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl"},ue.default.createElement("div",{className:"flex flex-col items-center"},ue.default.createElement("div",{className:"flex gap-2 mb-2"},e.instruction_sets.map(s=>{let u=Jo[s];return u?ue.default.createElement("span",{key:s,className:"text-lg",title:u.label},u.emoji):null})),ue.default.createElement(Tt,{size:120}),ue.default.createElement("div",{className:"flex gap-2 mt-2"},e.tools.map(s=>{let u=Ro[s];return u?ue.default.createElement("span",{key:s,className:"text-lg",title:u.label},u.emoji):null})))),ue.default.createElement("div",null,ue.default.createElement("label",{className:"block text-sm font-medium text-gray-700 mb-3"},"Quick Presets"),ue.default.createElement("div",{className:"flex flex-wrap gap-2"},i.map(s=>ue.default.createElement("button",{key:s,onClick:()=>a(s),className:`px-4 py-2 rounded-lg border-2 text-sm transition-all ${e.preset_type===s?"border-red-500 bg-red-50 text-red-700":"border-gray-200 hover:border-gray-300 text-gray-600"}`},ue.default.createElement("span",{className:"capitalize"},s.replace(/([A-Z])/g," $1").trim()))))),ue.default.createElement("div",null,ue.default.createElement("label",{className:"block text-sm font-medium text-gray-700 mb-3"},ue.default.createElement("strong",null,"Instructions"),ue.default.createElement("span",{className:"text-gray-400 font-normal ml-2"},"System/developer messages")),ue.default.createElement("div",{className:"flex flex-wrap gap-2"},o.map(s=>{let u=Jo[s];return ue.default.createElement("button",{key:s,onClick:()=>r("instruction_sets",s),className:`flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm transition-colors ${e.instruction_sets.includes(s)?"bg-red-500 text-white":"bg-gray-100 text-gray-700 hover:bg-gray-200"}`},ue.default.createElement("span",null,u.emoji),ue.default.createElement("span",null,u.label))}))),ue.default.createElement("div",null,ue.default.createElement("label",{className:"block text-sm font-medium text-gray-700 mb-3"},ue.default.createElement("strong",null,"Tools"),ue.default.createElement("span",{className:"text-gray-400 font-normal ml-2"},"MCPs and integrations")),ue.default.createElement("div",{className:"flex flex-wrap gap-2"},l.map(s=>{let u=Ro[s];return ue.default.createElement("button",{key:s,onClick:()=>r("tools",s),className:`flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm transition-colors ${e.tools.includes(s)?"bg-red-500 text-white":"bg-gray-100 text-gray-700 hover:bg-gray-200"}`},ue.default.createElement("span",null,u.emoji),ue.default.createElement("span",null,u.label))}))),ue.default.createElement("div",null,ue.default.createElement("label",{className:"block text-sm font-medium text-gray-700 mb-2"},"System Instructions"),ue.default.createElement("textarea",{value:e.instructions,onChange:s=>n("instructions",s.target.value),placeholder:"You are a helpful AI assistant that...",rows:4,className:"w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent font-mono text-sm"})))}function Az({formData:e}){return ue.default.createElement("div",{className:"space-y-6"},ue.default.createElement("h2",{className:"text-xl font-semibold text-gray-900"},"Review Your Agent"),ue.default.createElement("p",{className:"text-gray-500"},"Confirm the configuration before creating your agent."),ue.default.createElement("div",{className:"grid grid-cols-2 gap-8"},ue.default.createElement("div",{className:"flex flex-col items-center py-6 bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl"},ue.default.createElement("div",{className:"flex gap-2 mb-2"},e.instruction_sets.map(t=>{let r=Jo[t];return r?ue.default.createElement("span",{key:t,className:"text-lg",title:r.label},r.emoji):null})),ue.default.createElement(Tt,{size:100}),ue.default.createElement("div",{className:"flex gap-2 mt-2"},e.tools.map(t=>{let r=Ro[t];return r?ue.default.createElement("span",{key:t,className:"text-lg",title:r.label},r.emoji):null})),ue.default.createElement("h3",{className:"mt-4 text-lg font-semibold text-gray-900"},e.name),ue.default.createElement("p",{className:"text-sm text-gray-500"},e.description||"No description")),ue.default.createElement("div",{className:"space-y-4"},ue.default.createElement("div",null,ue.default.createElement("span",{className:"text-sm font-medium text-gray-500"},"Provider / Model"),ue.default.createElement("p",{className:"text-gray-900"},e.provider," / ",e.model)),ue.default.createElement("div",null,ue.default.createElement("span",{className:"text-sm font-medium text-gray-500"},"Temperature"),ue.default.createElement("p",{className:"text-gray-900"},e.model_config.temperature)),ue.default.createElement("div",null,ue.default.createElement("span",{className:"text-sm font-medium text-gray-500"},"Instructions"),ue.default.createElement("div",{className:"flex flex-wrap gap-1 mt-1"},e.instruction_sets.length>0?e.instruction_sets.map(t=>{let r=Jo[t];return r?ue.default.createElement("span",{key:t,className:"px-2 py-0.5 bg-gray-100 rounded text-xs"},r.emoji," ",r.label):null}):ue.default.createElement("span",{className:"text-gray-400 text-sm"},"None selected"))),ue.default.createElement("div",null,ue.default.createElement("span",{className:"text-sm font-medium text-gray-500"},"Tools"),ue.default.createElement("div",{className:"flex flex-wrap gap-1 mt-1"},e.tools.length>0?e.tools.map(t=>{let r=Ro[t];return r?ue.default.createElement("span",{key:t,className:"px-2 py-0.5 bg-red-100 text-red-700 rounded text-xs"},r.emoji," ",r.label):null}):ue.default.createElement("span",{className:"text-gray-400 text-sm"},"None selected"))))),e.instructions&&ue.default.createElement("div",null,ue.default.createElement("span",{className:"text-sm font-medium text-gray-500"},"System Instructions"),ue.default.createElement("pre",{className:"mt-2 p-4 bg-gray-50 rounded-lg text-sm text-gray-700 whitespace-pre-wrap font-mono"},e.instructions)))}var G=te(ae());var qs=e=>({cardBg:e?"#1f1f1f":"#ffffff",cardBorder:e?"#2a2a2a":"#e5e7eb",borderStrong:e?"rgba(255,255,255,0.2)":"#d1d5db",mutedBg:e?"rgba(255,255,255,0.06)":"#f3f4f6",innerBg:e?"rgba(255,255,255,0.04)":"#f9fafb",textPrimary:e?"#ffffff":"#111827",textCell:e?"rgba(255,255,255,0.75)":"#4b5563",textSecondary:e?"rgba(255,255,255,0.6)":"#6b7280",textMuted:e?"rgba(255,255,255,0.4)":"#9ca3af",inputBg:e?"rgba(255,255,255,0.06)":"#ffffff",inputBorder:e?"rgba(255,255,255,0.2)":"#d1d5db",trackBg:e?"rgba(255,255,255,0.1)":"#f3f4f6"}),la="#ef4444";var w=te(ae());var Dm=te(ae()),lE=te(ae());function nE(e){var t,r,n="";if(typeof e=="string"||typeof e=="number")n+=e;else if(typeof e=="object")if(Array.isArray(e)){var a=e.length;for(t=0;t{var r=e.children,n=e.width,a=e.height,i=e.viewBox,o=e.className,l=e.style,s=e.title,u=e.desc,c=kz(e,Ez),d=i||{width:n,height:a,x:0,y:0},f=Le("recharts-surface",o);return Dm.createElement("svg",_b({},Wt(c),{className:f,width:n,height:a,style:l,viewBox:"".concat(d.x," ").concat(d.y," ").concat(d.width," ").concat(d.height),ref:t}),Dm.createElement("title",null,s),Dm.createElement("desc",null,u),r)});var Pm=te(ae());var Tz=["children","className"];function Eb(){return Eb=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var r=e.children,n=e.className,a=Mz(e,Tz),i=Le("recharts-layer",n);return Pm.createElement("g",Eb({className:i},Wt(a),{ref:t}),r)});function jm(e){return e==="__proto__"}var Pz=/\.|(\[(?:[^[\]]*|(["'])(?:(?!\2)[^\\]|\\.)*?\2)\])/;function Im(e){switch(typeof e){case"number":case"symbol":return!1;case"string":return e===""||e.startsWith(".")||e.endsWith(".")?!1:Pz.test(e);default:return!1}}function Ys(e){return typeof e=="string"||typeof e=="symbol"?e:Object.is(e?.valueOf?.(),-0)?"-0":String(e)}function kb(e){if(e==null)return"";if(typeof e=="string")return e;if(Array.isArray(e))return e.map(kb).join(",");let t=String(e);return t==="0"&&Object.is(Number(e),-0)?"-0":t}function Ws(e){if(Array.isArray(e))return e.map(Ys);if(typeof e=="symbol")return[e];e=kb(e);let t=[],r=e.length;if(r===0)return t;let n=0,a="",i="",o=!1;for(e.charCodeAt(0)===46&&t.push("");n1&&arguments[1]!==void 0?arguments[1]:Iz,r=10**t,n=Math.round(e*r)/r;return Object.is(n,-0)?0:n}function sr(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),n=1;n{var l=r[o-1];return typeof l=="string"?a+l+i:l!==void 0?a+Na(l)+i:a+i},"")}var ur=e=>e===0?0:e>0?1:-1,Tr=e=>typeof e=="number"&&e!=+e,li=e=>typeof e=="string"&&e.length>1&&e.indexOf("%")===e.length-1,ye=e=>(typeof e=="number"||e instanceof Number)&&!Tr(e),Fr=e=>ye(e)||typeof e=="string",zz=0,si=e=>{var t=++zz;return"".concat(e||"").concat(t)},Vn=function(t,r){var n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:0,a=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!1;if(!ye(t)&&typeof t!="string")return n;var i;if(li(t)){if(r==null)return n;var o=t.indexOf("%");i=r*parseFloat(t.slice(0,o))/100}else i=+t;return Tr(i)&&(i=n),a&&r!=null&&i>r&&(i=r),i},Ob=e=>{if(!Array.isArray(e))return!1;for(var t=e.length,r={},n=0;nn&&(typeof t=="function"?t(n):bn(n,t))===r)}var Mt=e=>e===null||typeof e>"u",Ks=e=>Mt(e)?e:"".concat(e.charAt(0).toUpperCase()).concat(e.slice(1));function cr(e){return e!=null}function sa(){}function pd(e){if(e)return{x:e.x,y:e.y,upperWidth:"upperWidth"in e?e.upperWidth:e.width,lowerWidth:"lowerWidth"in e?e.lowerWidth:e.width,width:e.width,height:e.height}}function sE(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function _a(e){for(var t=1;t{var t=e.viewBox,r=e.position,n=e.offset,a=n===void 0?0:n,i=e.parentViewBox,o=e.clamp,l=pd(t),s=l.x,u=l.y,c=l.height,d=l.upperWidth,f=l.lowerWidth,p=s,v=s+(d-f)/2,h=(p+v)/2,b=(d+f)/2,m=p+d/2,g=c>=0?1:-1,x=g*a,A=g>0?"end":"start",k=g>0?"start":"end",_=d>=0?1:-1,M=_*a,E=_>0?"end":"start",L=_>0?"start":"end",I=i;if(r==="top"){var j={x:p+d/2,y:u-x,horizontalAnchor:"middle",verticalAnchor:A};return o&&I&&(j.height=Math.max(u-I.y,0),j.width=d),j}if(r==="bottom"){var z={x:v+f/2,y:u+c+x,horizontalAnchor:"middle",verticalAnchor:k};return o&&I&&(z.height=Math.max(I.y+I.height-(u+c),0),z.width=f),z}if(r==="left"){var X={x:h-M,y:u+c/2,horizontalAnchor:E,verticalAnchor:"middle"};return o&&I&&(X.width=Math.max(X.x-I.x,0),X.height=c),X}if(r==="right"){var R={x:h+b+M,y:u+c/2,horizontalAnchor:L,verticalAnchor:"middle"};return o&&I&&(R.width=Math.max(I.x+I.width-R.x,0),R.height=c),R}var Q=o&&I?{width:b,height:c}:{};return r==="insideLeft"?_a({x:h+M,y:u+c/2,horizontalAnchor:L,verticalAnchor:"middle"},Q):r==="insideRight"?_a({x:h+b-M,y:u+c/2,horizontalAnchor:E,verticalAnchor:"middle"},Q):r==="insideTop"?_a({x:p+d/2,y:u+x,horizontalAnchor:"middle",verticalAnchor:k},Q):r==="insideBottom"?_a({x:v+f/2,y:u+c-x,horizontalAnchor:"middle",verticalAnchor:A},Q):r==="insideTopLeft"?_a({x:p+M,y:u+x,horizontalAnchor:L,verticalAnchor:k},Q):r==="insideTopRight"?_a({x:p+d-M,y:u+x,horizontalAnchor:E,verticalAnchor:k},Q):r==="insideBottomLeft"?_a({x:v+M,y:u+c-x,horizontalAnchor:L,verticalAnchor:A},Q):r==="insideBottomRight"?_a({x:v+f-M,y:u+c-x,horizontalAnchor:E,verticalAnchor:A},Q):r&&typeof r=="object"&&(ye(r.x)||li(r.x))&&(ye(r.y)||li(r.y))?_a({x:s+Vn(r.x,b),y:u+Vn(r.y,c),horizontalAnchor:"end",verticalAnchor:"end"},Q):_a({x:m,y:u+c/2,horizontalAnchor:"middle",verticalAnchor:"middle"},Q)},$z=["top","left","right","bottom"];function cE(e){return e==null?!1:typeof e=="object"?!0:$z.includes(e)}var Tb=te(ae()),dE=(0,Tb.createContext)(null);function Nt(e){return function(){return e}}var Mb=Math.PI,Db=2*Mb,el=1e-6,Hz=Db-el;function fE(e){this._+=e[0];for(let t=1,r=e.length;t=0))throw new Error(`invalid digits: ${e}`);if(t>15)return fE;let r=10**t;return function(n){this._+=n[0];for(let a=1,i=n.length;ael)if(!(Math.abs(d*s-u*c)>el)||!i)this._append`L${this._x1=t},${this._y1=r}`;else{let p=n-o,v=a-l,h=s*s+u*u,b=p*p+v*v,m=Math.sqrt(h),g=Math.sqrt(f),x=i*Math.tan((Mb-Math.acos((h+f-b)/(2*m*g)))/2),A=x/g,k=x/m;Math.abs(A-1)>el&&this._append`L${t+A*c},${r+A*d}`,this._append`A${i},${i},0,0,${+(d*p>c*v)},${this._x1=t+k*s},${this._y1=r+k*u}`}}arc(t,r,n,a,i,o){if(t=+t,r=+r,n=+n,o=!!o,n<0)throw new Error(`negative radius: ${n}`);let l=n*Math.cos(a),s=n*Math.sin(a),u=t+l,c=r+s,d=1^o,f=o?a-i:i-a;this._x1===null?this._append`M${u},${c}`:(Math.abs(this._x1-u)>el||Math.abs(this._y1-c)>el)&&this._append`L${u},${c}`,n&&(f<0&&(f=f%Db+Db),f>Hz?this._append`A${n},${n},0,1,${d},${t-l},${r-s}A${n},${n},0,1,${d},${this._x1=u},${this._y1=c}`:f>el&&this._append`A${n},${n},0,${+(f>=Mb)},${d},${this._x1=t+n*Math.cos(i)},${this._y1=r+n*Math.sin(i)}`)}rect(t,r,n,a){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+r}h${n=+n}v${+a}h${-n}Z`}toString(){return this._}};function pE(){return new tl}pE.prototype=tl.prototype;function Lm(e){let t=3;return e.digits=function(r){if(!arguments.length)return t;if(r==null)t=null;else{let n=Math.floor(r);if(!(n>=0))throw new RangeError(`invalid digits: ${r}`);t=n}return e},()=>new tl(t)}var AJ=Array.prototype.slice;function Gs(e){return typeof e=="object"&&"length"in e?e:Array.from(e)}function mE(e){this._context=e}mE.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:this._context.lineTo(e,t);break}}};function uo(e){return new mE(e)}function Bm(e){return e[0]}function Um(e){return e[1]}function md(e,t){var r=Nt(!0),n=null,a=uo,i=null,o=Lm(l);e=typeof e=="function"?e:e===void 0?Bm:Nt(e),t=typeof t=="function"?t:t===void 0?Um:Nt(t);function l(s){var u,c=(s=Gs(s)).length,d,f=!1,p;for(n==null&&(i=a(p=o())),u=0;u<=c;++u)!(u=p;--v)l.point(x[v],A[v]);l.lineEnd(),l.areaEnd()}m&&(x[f]=+e(b,f,d),A[f]=+t(b,f,d),l.point(n?+n(b,f,d):x[f],r?+r(b,f,d):A[f]))}if(g)return l=null,g+""||null}function c(){return md().defined(a).curve(o).context(i)}return u.x=function(d){return arguments.length?(e=typeof d=="function"?d:Nt(+d),n=null,u):e},u.x0=function(d){return arguments.length?(e=typeof d=="function"?d:Nt(+d),u):e},u.x1=function(d){return arguments.length?(n=d==null?null:typeof d=="function"?d:Nt(+d),u):n},u.y=function(d){return arguments.length?(t=typeof d=="function"?d:Nt(+d),r=null,u):t},u.y0=function(d){return arguments.length?(t=typeof d=="function"?d:Nt(+d),u):t},u.y1=function(d){return arguments.length?(r=d==null?null:typeof d=="function"?d:Nt(+d),u):r},u.lineX0=u.lineY0=function(){return c().x(e).y(t)},u.lineY1=function(){return c().x(e).y(r)},u.lineX1=function(){return c().x(n).y(t)},u.defined=function(d){return arguments.length?(a=typeof d=="function"?d:Nt(!!d),u):a},u.curve=function(d){return arguments.length?(o=d,i!=null&&(l=o(i)),u):o},u.context=function(d){return arguments.length?(d==null?i=l=null:l=o(i=d),u):i},u}var $m=class{constructor(t,r){this._context=t,this._x=r}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line}point(t,r){switch(t=+t,r=+r,this._point){case 0:{this._point=1,this._line?this._context.lineTo(t,r):this._context.moveTo(t,r);break}case 1:this._point=2;default:{this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,r,t,r):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+r)/2,t,this._y0,t,r);break}}this._x0=t,this._y0=r}};function Pb(e){return new $m(e,!0)}function jb(e){return new $m(e,!1)}function Zs(){}function Qs(e,t,r){e._context.bezierCurveTo((2*e._x0+e._x1)/3,(2*e._y0+e._y1)/3,(e._x0+2*e._x1)/3,(e._y0+2*e._y1)/3,(e._x0+4*e._x1+t)/6,(e._y0+4*e._y1+r)/6)}function vE(e){this._context=e}vE.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Qs(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Qs(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function Ib(e){return new vE(e)}function hE(e){this._context=e}hE.prototype={areaStart:Zs,areaEnd:Zs,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x2,this._y2),this._context.closePath();break}case 2:{this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break}case 3:{this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4);break}}},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._x2=e,this._y2=t;break;case 1:this._point=2,this._x3=e,this._y3=t;break;case 2:this._point=3,this._x4=e,this._y4=t,this._context.moveTo((this._x0+4*this._x1+e)/6,(this._y0+4*this._y1+t)/6);break;default:Qs(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function zb(e){return new hE(e)}function gE(e){this._context=e}gE.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var r=(this._x0+4*this._x1+e)/6,n=(this._y0+4*this._y1+t)/6;this._line?this._context.lineTo(r,n):this._context.moveTo(r,n);break;case 3:this._point=4;default:Qs(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function Lb(e){return new gE(e)}function yE(e){this._context=e}yE.prototype={areaStart:Zs,areaEnd:Zs,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(e,t){e=+e,t=+t,this._point?this._context.lineTo(e,t):(this._point=1,this._context.moveTo(e,t))}};function Bb(e){return new yE(e)}function xE(e){return e<0?-1:1}function bE(e,t,r){var n=e._x1-e._x0,a=t-e._x1,i=(e._y1-e._y0)/(n||a<0&&-0),o=(r-e._y1)/(a||n<0&&-0),l=(i*a+o*n)/(n+a);return(xE(i)+xE(o))*Math.min(Math.abs(i),Math.abs(o),.5*Math.abs(l))||0}function wE(e,t){var r=e._x1-e._x0;return r?(3*(e._y1-e._y0)/r-t)/2:t}function Ub(e,t,r){var n=e._x0,a=e._y0,i=e._x1,o=e._y1,l=(i-n)/3;e._context.bezierCurveTo(n+l,a+l*t,i-l,o-l*r,i,o)}function Hm(e){this._context=e}Hm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:Ub(this,this._t0,wE(this,this._t0));break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){var r=NaN;if(e=+e,t=+t,!(e===this._x1&&t===this._y1)){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,Ub(this,wE(this,r=bE(this,e,t)),r);break;default:Ub(this,this._t0,r=bE(this,e,t));break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t,this._t0=r}}};function SE(e){this._context=new AE(e)}(SE.prototype=Object.create(Hm.prototype)).point=function(e,t){Hm.prototype.point.call(this,t,e)};function AE(e){this._context=e}AE.prototype={moveTo:function(e,t){this._context.moveTo(t,e)},closePath:function(){this._context.closePath()},lineTo:function(e,t){this._context.lineTo(t,e)},bezierCurveTo:function(e,t,r,n,a,i){this._context.bezierCurveTo(t,e,n,r,i,a)}};function $b(e){return new Hm(e)}function Hb(e){return new SE(e)}function _E(e){this._context=e}_E.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var e=this._x,t=this._y,r=e.length;if(r)if(this._line?this._context.lineTo(e[0],t[0]):this._context.moveTo(e[0],t[0]),r===2)this._context.lineTo(e[1],t[1]);else for(var n=NE(e),a=NE(t),i=0,o=1;o=0;--t)a[t]=(o[t]-a[t+1])/i[t];for(i[r-1]=(e[r]+a[r-1])/2,t=0;t=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:{if(this._t<=0)this._context.lineTo(this._x,t),this._context.lineTo(e,t);else{var r=this._x*(1-this._t)+e*this._t;this._context.lineTo(r,this._y),this._context.lineTo(r,t)}break}}this._x=e,this._y=t}};function Fb(e){return new Vm(e,.5)}function qb(e){return new Vm(e,0)}function Yb(e){return new Vm(e,1)}function wn(e,t){if((o=e.length)>1)for(var r=1,n,a,i=e[t[0]],o,l=i.length;r=0;)r[t]=t;return r}function Fz(e,t){return e[t]}function qz(e){let t=[];return t.key=e,t}function Wb(){var e=Nt([]),t=Js,r=wn,n=Fz;function a(i){var o=Array.from(e.apply(this,arguments),qz),l,s=o.length,u=-1,c;for(let d of i)for(l=0,++u;l0){for(var r,n,a=0,i=e[0].length,o;a0){for(var r=0,n=e[t[0]],a,i=n.length;r0)||!((i=(a=e[t[0]]).length)>0))){for(var r=0,n=1,a,i,o;n"radius"in e&&"startAngle"in e&&"endAngle"in e,Rs=(e,t)=>{if(!e||typeof e=="function"||typeof e=="boolean")return null;var r=e;if((0,CE.isValidElement)(e)&&(r=e.props),typeof r!="object"&&typeof r!="function")return null;var n={};return Object.keys(r).forEach(a=>{fd(a)&&typeof r[a]=="function"&&(n[a]=t||(i=>r[a](r,i)))}),n},Yz=(e,t,r)=>n=>(e(t,r,n),null),EE=(e,t,r)=>{if(e===null||typeof e!="object"&&typeof e!="function")return null;var n=null;return Object.keys(e).forEach(a=>{var i=e[a];fd(a)&&typeof i=="function"&&(n||(n={}),n[a]=Yz(i,t,r))}),n};function kE(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function Wz(e){for(var t=1;t(o[l]===void 0&&n[l]!==void 0&&(o[l]=n[l]),o),r);return i}function OE(e,t){let r=new Map;for(let n=0;nObject.prototype.propertyIsEnumerable.call(e,t))}function ui(e){return e==null?e===void 0?"[object Undefined]":"[object Null]":Object.prototype.toString.call(e)}var Ym="[object RegExp]",eu="[object String]",tu="[object Number]",ru="[object Boolean]",Wm="[object Arguments]",Km="[object Symbol]",Gm="[object Date]",Xm="[object Map]",Zm="[object Set]",Qm="[object Array]",PE="[object Function]",Jm="[object ArrayBuffer]",nu="[object Object]",jE="[object Error]",Rm="[object DataView]",ev="[object Uint8Array]",tv="[object Uint8ClampedArray]",rv="[object Uint16Array]",nv="[object Uint32Array]",IE="[object BigUint64Array]",av="[object Int8Array]",iv="[object Int16Array]",ov="[object Int32Array]",zE="[object BigInt64Array]",lv="[object Float32Array]",sv="[object Float64Array]";var Zb=typeof globalThis=="object"&&globalThis||typeof window=="object"&&window||typeof self=="object"&&self||typeof global=="object"&&global||(function(){return this})();function gd(e){return typeof Zb.Buffer<"u"&&Zb.Buffer.isBuffer(e)}function LE(e,t){return co(e,void 0,e,new Map,t)}function co(e,t,r,n=new Map,a=void 0){let i=a?.(e,t,r,n);if(i!==void 0)return i;if(vd(e))return e;if(n.has(e))return n.get(e);if(Array.isArray(e)){let o=new Array(e.length);n.set(e,o);for(let l=0;l{}):Qb(e,t,function n(a,i,o,l,s,u){let c=r(a,i,o,l,s,u);return c!==void 0?!!c:Qb(a,i,n,u,!1)},new Map,!0)}function Qb(e,t,r,n,a=!1){if(t===e)return!0;switch(typeof t){case"object":return Qz(e,t,r,n);case"function":return Object.keys(t).length>0?Qb(e,{...t},r,n,a):fo(e,t);default:return uv(e)?a?typeof t=="string"?t==="":!0:fo(e,t):fo(e,t)}}function Qz(e,t,r,n){if(t==null)return!0;if(Array.isArray(t))return UE(e,t,r,n);if(t instanceof Map)return Jz(e,t,r,n);if(t instanceof Set)return Rz(e,t,r,n);let a=Object.keys(t);if(e==null||vd(e))return a.length===0;if(a.length===0)return!0;if(n?.has(t))return n.get(t)===e;n?.set(t,e);try{for(let i=0;i{})}function $E(e){return e=BE(e),t=>cv(t,e)}function HE(e,t){return LE(e,(r,n,a,i)=>{let o=t?.(r,n,a,i);if(o!==void 0)return o;if(typeof e=="object"){if(ui(e)==="[object Object]"&&typeof e.constructor!="function"){let l={};return i.set(e,l),qn(l,e,a,i),l}switch(Object.prototype.toString.call(e)){case tu:case eu:case ru:{let l=new e.constructor(e?.valueOf());return qn(l,e),l}case Wm:{let l={};return qn(l,e),l.length=e.length,l[Symbol.iterator]=e[Symbol.iterator],l}default:return}}})}function VE(e){return HE(e)}var e9=/^(?:0|[1-9]\d*)$/;function dv(e,t=Number.MAX_SAFE_INTEGER){switch(typeof e){case"number":return Number.isInteger(e)&&e>=0&&e=0}function fv(e){return e!=null&&typeof e!="function"&&KE(e.length)}function GE(e){return typeof e=="object"&&e!==null}function XE(e){return GE(e)&&fv(e)}function pv(e,t=qm){return XE(e)?OE(Array.from(e),TE(WE(t),1)):[]}function ZE(e,t,r){return t===!0?pv(e,r):typeof t=="function"?pv(e,t):e}var ok=te(ak()),xd=te(ae());var ik=te(ae()),yd=(0,ik.createContext)(null);var y9=e=>e,Ve=()=>{var e=(0,xd.useContext)(yd);return e?e.store.dispatch:y9},vv=()=>{},x9=()=>vv,b9=(e,t)=>e===t;function Se(e){var t=(0,xd.useContext)(yd),r=(0,xd.useMemo)(()=>t?n=>{if(n!=null)return e(n)}:vv,[t,e]);return(0,ok.useSyncExternalStoreWithSelector)(t?t.subscription.addNestedSub:x9,t?t.store.getState:vv,t?t.store.getState:vv,r,b9)}function w9(e,t=`expected a function, instead received ${typeof e}`){if(typeof e!="function")throw new TypeError(t)}function S9(e,t="expected all items to be functions, instead received the following types: "){if(!e.every(r=>typeof r=="function")){let r=e.map(n=>typeof n=="function"?`function ${n.name||"unnamed"}()`:typeof n).join(", ");throw new TypeError(`${t}[${r}]`)}}var lk=e=>Array.isArray(e)?e:[e];function A9(e){let t=Array.isArray(e[0])?e[0]:e;return S9(t,"createSelector expects all input-selectors to be functions, but received the following types: "),t}function N9(e,t){let r=[],{length:n}=e;for(let a=0;atypeof WeakRef>"u"?_9:WeakRef,uk=C9(),E9=0,sk=1;function hv(){return{s:E9,v:void 0,o:null,p:null}}function k9(e){return e instanceof uk?e.deref():e}function ck(e,t={}){let r=hv(),{resultEqualityCheck:n}=t,a,i=0;function o(){let l=r,{length:s}=arguments;for(let d=0,f=s;d{r=hv(),o.resetResultsCount()},o.resultsCount=()=>i,o.resetResultsCount=()=>{i=0},o}function O9(e,...t){let r=typeof e=="function"?{memoize:e,memoizeOptions:t}:e,n=(...a)=>{let i=0,o=0,l,s={},u=a.pop();typeof u=="object"&&(s=u,u=a.pop()),w9(u,`createSelector expects an output function after the inputs, but received: [${typeof u}]`);let c={...r,...s},{memoize:d,memoizeOptions:f=[],argsMemoize:p=ck,argsMemoizeOptions:v=[]}=c,h=lk(f),b=lk(v),m=A9(a),g=d(function(){return i++,u.apply(null,arguments)},...h),x=!0,A=p(function(){o++;let _=N9(m,arguments);return l=g.apply(null,_),l},...b);return Object.assign(A,{resultFunc:u,memoizedResultFunc:g,dependencies:m,dependencyRecomputations:()=>o,resetDependencyRecomputations:()=>{o=0},lastResult:()=>l,recomputations:()=>i,resetRecomputations:()=>{i=0},memoize:d,argsMemoize:p})};return Object.assign(n,{withTypes:()=>n}),n}var V=O9(ck);function dk(e,t=1){let r=[],n=Math.floor(t),a=(i,o)=>{for(let l=0;l{if(e!==t){let n=fk(e),a=fk(t);if(n===a&&n===0){if(et)return r==="desc"?-1:1}return r==="desc"?a-n:n-a}return 0};function gv(e){return typeof e=="symbol"||e instanceof Symbol}var T9=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,M9=/^\w*$/;function mk(e,t){return Array.isArray(e)?!1:typeof e=="number"||typeof e=="boolean"||e==null||gv(e)?!0:typeof e=="string"&&(M9.test(e)||!T9.test(e))||t!=null&&Object.hasOwn(t,e)}function vk(e,t,r,n){if(e==null)return[];r=n?void 0:r,Array.isArray(e)||(e=Object.values(e)),Array.isArray(t)||(t=t==null?[null]:[t]),t.length===0&&(t=[null]),Array.isArray(r)||(r=r==null?[]:[r]),r=r.map(l=>String(l));let a=(l,s)=>{let u=l;for(let c=0;cs==null||l==null?s:typeof l=="object"&&"key"in l?Object.hasOwn(s,l.key)?s[l.key]:a(s,l.path):typeof l=="function"?l(s):Array.isArray(l)?a(s,l):typeof s=="object"?s[l]:s,o=t.map(l=>(Array.isArray(l)&&l.length===1&&(l=l[0]),l==null||typeof l=="function"||Array.isArray(l)||mk(l)?l:{key:l,path:Ws(l)}));return e.map(l=>({original:l,criteria:o.map(s=>i(s,l))})).slice().sort((l,s)=>{for(let u=0;ul.original)}function ci(e,...t){let r=t.length;return r>1&&bd(e,t[0],t[1])?t=[]:r>2&&bd(t[0],t[1],t[2])&&(t=[t[0]]),vk(e,dk(t),["asc"])}var e1=e=>e.legend.settings,hk=e=>e.legend.size,D9=e=>e.legend.payload,Pte=V([D9,e1],(e,t)=>{var r=t.itemSorter,n=e.flat(1);return r?ci(n,r):n});var di=te(ae());function P9(e,t){return L9(e)||z9(e,t)||I9(e,t)||j9()}function j9(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function I9(e,t){if(e){if(typeof e=="string")return gk(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?gk(e,t):void 0}}function gk(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);ryv||Math.abs(e.left-t.left)>yv||Math.abs(e.top-t.top)>yv||Math.abs(e.width-t.width)>yv}function xk(e){var t=e.getBoundingClientRect();return{height:t.height,left:t.left,top:t.top,width:t.width}}function bk(){var e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:[],t=(0,di.useState)({height:0,left:0,top:0,width:0}),r=P9(t,2),n=r[0],a=r[1],i=(0,di.useRef)(null),o=(0,di.useRef)(n);o.current=n;var l=(0,di.useCallback)(s=>{if(i.current!=null&&(i.current.disconnect(),i.current=null),s!=null){var u=xk(s);if(yk(u,o.current)&&a(u),typeof ResizeObserver<"u"){var c=new ResizeObserver(()=>{var d=xk(s);yk(d,o.current)&&a(d)});c.observe(s),i.current=c}}},[...e]);return(0,di.useEffect)(()=>()=>{var s;(s=i.current)===null||s===void 0||s.disconnect()},[]),[n,l]}var BO=te(ae());function _r(e){return`Minified Redux error #${e}; visit https://redux.js.org/Errors?code=${e} for the full message or use the non-minified dev environment for full errors. `}var B9=typeof Symbol=="function"&&Symbol.observable||"@@observable",wk=B9,t1=()=>Math.random().toString(36).substring(7).split("").join("."),U9={INIT:`@@redux/INIT${t1()}`,REPLACE:`@@redux/REPLACE${t1()}`,PROBE_UNKNOWN_ACTION:()=>`@@redux/PROBE_UNKNOWN_ACTION${t1()}`},xv=U9;function bv(e){if(typeof e!="object"||e===null)return!1;let t=e;for(;Object.getPrototypeOf(t)!==null;)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t||Object.getPrototypeOf(e)===null}function r1(e,t,r){if(typeof e!="function")throw new Error(_r(2));if(typeof t=="function"&&typeof r=="function"||typeof r=="function"&&typeof arguments[3]=="function")throw new Error(_r(0));if(typeof t=="function"&&typeof r>"u"&&(r=t,t=void 0),typeof r<"u"){if(typeof r!="function")throw new Error(_r(1));return r(r1)(e,t)}let n=e,a=t,i=new Map,o=i,l=0,s=!1;function u(){o===i&&(o=new Map,i.forEach((b,m)=>{o.set(m,b)}))}function c(){if(s)throw new Error(_r(3));return a}function d(b){if(typeof b!="function")throw new Error(_r(4));if(s)throw new Error(_r(5));let m=!0;u();let g=l++;return o.set(g,b),function(){if(m){if(s)throw new Error(_r(6));m=!1,u(),o.delete(g),i=null}}}function f(b){if(!bv(b))throw new Error(_r(7));if(typeof b.type>"u")throw new Error(_r(8));if(typeof b.type!="string")throw new Error(_r(17));if(s)throw new Error(_r(9));try{s=!0,a=n(a,b)}finally{s=!1}return(i=o).forEach(g=>{g()}),b}function p(b){if(typeof b!="function")throw new Error(_r(10));n=b,f({type:xv.REPLACE})}function v(){let b=d;return{subscribe(m){if(typeof m!="object"||m===null)throw new Error(_r(11));function g(){let A=m;A.next&&A.next(c())}return g(),{unsubscribe:b(g)}},[wk](){return this}}}return f({type:xv.INIT}),{dispatch:f,subscribe:d,getState:c,replaceReducer:p,[wk]:v}}function $9(e){Object.keys(e).forEach(t=>{let r=e[t];if(typeof r(void 0,{type:xv.INIT})>"u")throw new Error(_r(12));if(typeof r(void 0,{type:xv.PROBE_UNKNOWN_ACTION()})>"u")throw new Error(_r(13))})}function wv(e){let t=Object.keys(e),r={};for(let o=0;o"u"){let b=s&&s.type;throw new Error(_r(14))}c[f]=h,u=u||h!==v}return u=u||n.length!==Object.keys(l).length,u?c:l}}function wd(...e){return e.length===0?t=>t:e.length===1?e[0]:e.reduce((t,r)=>(...n)=>t(r(...n)))}function Sk(...e){return t=>(r,n)=>{let a=t(r,n),i=()=>{throw new Error(_r(15))},o={getState:a.getState,dispatch:(s,...u)=>i(s,...u)},l=e.map(s=>s(o));return i=wd(...l)(a.dispatch),{...a,dispatch:i}}}function n1(e){return bv(e)&&"type"in e&&typeof e.type=="string"}var Mk=Symbol.for("immer-nothing"),Ak=Symbol.for("immer-draftable"),Mr=Symbol.for("immer-state");function ua(e,...t){throw new Error(`[Immer] minified error nr: ${e}. Full error at: https://bit.ly/3cXEKWf`)}var Sn=Object,ou=Sn.getPrototypeOf,_v="constructor",Mv="prototype",l1="configurable",Cv="enumerable",Av="writable",Sd="value",Ca=e=>!!e&&!!e[Mr];function An(e){return e?Dk(e)||Pv(e)||!!e[Ak]||!!e[_v]?.[Ak]||jv(e)||Iv(e):!1}var H9=Sn[Mv][_v].toString(),Nk=new WeakMap;function Dk(e){if(!e||!v1(e))return!1;let t=ou(e);if(t===null||t===Sn[Mv])return!0;let r=Sn.hasOwnProperty.call(t,_v)&&t[_v];if(r===Object)return!0;if(!iu(r))return!1;let n=Nk.get(r);return n===void 0&&(n=Function.toString.call(r),Nk.set(r,n)),n===H9}function Dv(e,t,r=!0){_d(e)===0?(r?Reflect.ownKeys(e):Sn.keys(e)).forEach(a=>{t(a,e[a],e)}):e.forEach((n,a)=>t(a,n,e))}function _d(e){let t=e[Mr];return t?t.type_:Pv(e)?1:jv(e)?2:Iv(e)?3:0}var a1=(e,t,r=_d(e))=>r===2?e.has(t):Sn[Mv].hasOwnProperty.call(e,t),s1=(e,t,r=_d(e))=>r===2?e.get(t):e[t],Ev=(e,t,r,n=_d(e))=>{n===2?e.set(t,r):n===3?e.add(r):e[t]=r};function V9(e,t){return e===t?e!==0||1/e===1/t:e!==e&&t!==t}var Pv=Array.isArray,jv=e=>e instanceof Map,Iv=e=>e instanceof Set,v1=e=>typeof e=="object",iu=e=>typeof e=="function",i1=e=>typeof e=="boolean";function F9(e){let t=+e;return Number.isInteger(t)&&String(t)===e}var fi=e=>e.copy_||e.base_;var h1=e=>e.modified_?e.copy_:e.base_;function u1(e,t){if(jv(e))return new Map(e);if(Iv(e))return new Set(e);if(Pv(e))return Array[Mv].slice.call(e);let r=Dk(e);if(t===!0||t==="class_only"&&!r){let n=Sn.getOwnPropertyDescriptors(e);delete n[Mr];let a=Reflect.ownKeys(n);for(let i=0;i1&&Sn.defineProperties(e,{set:Sv,add:Sv,clear:Sv,delete:Sv}),Sn.freeze(e),t&&Dv(e,(r,n)=>{g1(n,!0)},!1)),e}function q9(){ua(2)}var Sv={[Sd]:q9};function zv(e){return e===null||!v1(e)?!0:Sn.isFrozen(e)}var kv="MapSet",c1="Patches",_k="ArrayMethods",Pk={};function rl(e){let t=Pk[e];return t||ua(0,e),t}var Ck=e=>!!Pk[e];var Ad,jk=()=>Ad,Y9=(e,t)=>({drafts_:[],parent_:e,immer_:t,canAutoFreeze_:!0,unfinalizedDrafts_:0,handledSet_:new Set,processedForPatches_:new Set,mapSetPlugin_:Ck(kv)?rl(kv):void 0,arrayMethodsPlugin_:Ck(_k)?rl(_k):void 0});function Ek(e,t){t&&(e.patchPlugin_=rl(c1),e.patches_=[],e.inversePatches_=[],e.patchListener_=t)}function d1(e){f1(e),e.drafts_.forEach(W9),e.drafts_=null}function f1(e){e===Ad&&(Ad=e.parent_)}var kk=e=>Ad=Y9(Ad,e);function W9(e){let t=e[Mr];t.type_===0||t.type_===1?t.revoke_():t.revoked_=!0}function Ok(e,t){t.unfinalizedDrafts_=t.drafts_.length;let r=t.drafts_[0];if(e!==void 0&&e!==r){r[Mr].modified_&&(d1(t),ua(4)),An(e)&&(e=Tk(t,e));let{patchPlugin_:a}=t;a&&a.generateReplacementPatches_(r[Mr].base_,e,t)}else e=Tk(t,r);return K9(t,e,!0),d1(t),t.patches_&&t.patchListener_(t.patches_,t.inversePatches_),e!==Mk?e:void 0}function Tk(e,t){if(zv(t))return t;let r=t[Mr];if(!r)return Ov(t,e.handledSet_,e);if(!Lv(r,e))return t;if(!r.modified_)return r.base_;if(!r.finalized_){let{callbacks_:n}=r;if(n)for(;n.length>0;)n.pop()(e);Lk(r,e)}return r.copy_}function K9(e,t,r=!1){!e.parent_&&e.immer_.autoFreeze_&&e.canAutoFreeze_&&g1(t,r)}function Ik(e){e.finalized_=!0,e.scope_.unfinalizedDrafts_--}var Lv=(e,t)=>e.scope_===t,G9=[];function zk(e,t,r,n){let a=fi(e),i=e.type_;if(n!==void 0&&s1(a,n,i)===t){Ev(a,n,r,i);return}if(!e.draftLocations_){let l=e.draftLocations_=new Map;Dv(a,(s,u)=>{if(Ca(u)){let c=l.get(u)||[];c.push(s),l.set(u,c)}})}let o=e.draftLocations_.get(t)??G9;for(let l of o)Ev(a,l,r,i)}function X9(e,t,r){e.callbacks_.push(function(a){let i=t;if(!i||!Lv(i,a))return;a.mapSetPlugin_?.fixSetContents(i);let o=h1(i);zk(e,i.draft_??i,o,r),Lk(i,a)})}function Lk(e,t){if(e.modified_&&!e.finalized_&&(e.type_===3||e.type_===1&&e.allIndicesReassigned_||(e.assigned_?.size??0)>0)){let{patchPlugin_:n}=t;if(n){let a=n.getPath(e);a&&n.generatePatches_(e,a,t)}Ik(e)}}function Z9(e,t,r){let{scope_:n}=e;if(Ca(r)){let a=r[Mr];Lv(a,n)&&a.callbacks_.push(function(){Nv(e);let o=h1(a);zk(e,r,o,t)})}else An(r)&&e.callbacks_.push(function(){let i=fi(e);e.type_===3?i.has(r)&&Ov(r,n.handledSet_,n):s1(i,t,e.type_)===r&&n.drafts_.length>1&&(e.assigned_.get(t)??!1)===!0&&e.copy_&&Ov(s1(e.copy_,t,e.type_),n.handledSet_,n)})}function Ov(e,t,r){return!r.immer_.autoFreeze_&&r.unfinalizedDrafts_<1||Ca(e)||t.has(e)||!An(e)||zv(e)||(t.add(e),Dv(e,(n,a)=>{if(Ca(a)){let i=a[Mr];if(Lv(i,r)){let o=h1(i);Ev(e,n,o,e.type_),Ik(i)}}else An(a)&&Ov(a,t,r)})),e}function Q9(e,t){let r=Pv(e),n={type_:r?1:0,scope_:t?t.scope_:jk(),modified_:!1,finalized_:!1,assigned_:void 0,parent_:t,base_:e,draft_:null,copy_:null,revoke_:null,isManual_:!1,callbacks_:void 0},a=n,i=Tv;r&&(a=[n],i=Nd);let{revoke:o,proxy:l}=Proxy.revocable(a,i);return n.draft_=l,n.revoke_=o,[l,n]}var Tv={get(e,t){if(t===Mr)return e;let r=e.scope_.arrayMethodsPlugin_,n=e.type_===1&&typeof t=="string";if(n&&r?.isArrayOperationMethod(t))return r.createMethodInterceptor(e,t);let a=fi(e);if(!a1(a,t,e.type_))return R9(e,a,t);let i=a[t];if(e.finalized_||!An(i)||n&&e.operationMethod&&r?.isMutatingArrayMethod(e.operationMethod)&&F9(t))return i;if(i===o1(e.base_,t)||J9(e,t,i)){Nv(e);let o=e.type_===1?+t:t,l=m1(e.scope_,i,e,o);return e.copy_[o]=l}return i},has(e,t){return t in fi(e)},ownKeys(e){return Reflect.ownKeys(fi(e))},set(e,t,r){let n=Bk(fi(e),t);if(n?.set)return n.set.call(e.draft_,r),!0;if(!e.modified_){let a=o1(fi(e),t),i=a?.[Mr];if(i&&i.base_===r)return e.copy_[t]=r,e.assigned_.set(t,!1),!0;if(V9(r,a)&&(r!==void 0||a1(e.base_,t,e.type_)))return!0;Nv(e),p1(e)}return e.copy_[t]===r&&(r!==void 0||a1(e.copy_,t,e.type_))||Number.isNaN(r)&&Number.isNaN(e.copy_[t])||(e.copy_[t]=r,e.assigned_.set(t,!0),Z9(e,t,r)),!0},deleteProperty(e,t){return Nv(e),o1(e.base_,t)!==void 0||t in e.base_?(e.assigned_.set(t,!1),p1(e)):e.assigned_.delete(t),e.copy_&&delete e.copy_[t],!0},getOwnPropertyDescriptor(e,t){let r=fi(e),n=Reflect.getOwnPropertyDescriptor(r,t);return n&&{[Av]:!0,[l1]:e.type_!==1||t!=="length",[Cv]:n[Cv],[Sd]:r[t]}},defineProperty(){ua(11)},getPrototypeOf(e){return ou(e.base_)},setPrototypeOf(){ua(12)}},Nd={};for(let e in Tv){let t=Tv[e];Nd[e]=function(){let r=arguments;return r[0]=r[0][0],t.apply(this,r)}}Nd.deleteProperty=function(e,t){return Nd.set.call(this,e,t,void 0)};Nd.set=function(e,t,r){return Tv.set.call(this,e[0],t,r,e[0])};function o1(e,t){let r=e[Mr];return(r?fi(r):e)[t]}function J9(e,t,r){return e.type_!==1||!e.allIndicesReassigned_||e.assigned_?.get(t)||!An(r)||r[Mr]?!1:e.baseRefs_.has(r)}function R9(e,t,r){let n=Bk(t,r);return n?Sd in n?n[Sd]:n.get?.call(e.draft_):void 0}function Bk(e,t){if(!(t in e))return;let r=ou(e);for(;r;){let n=Object.getOwnPropertyDescriptor(r,t);if(n)return n;r=ou(r)}}function p1(e){e.modified_||(e.modified_=!0,e.parent_&&p1(e.parent_))}function Nv(e){e.copy_||(e.assigned_=new Map,e.copy_=u1(e.base_,e.scope_.immer_.useStrictShallowCopy_))}var eL=class{constructor(e){this.autoFreeze_=!0,this.useStrictShallowCopy_=!1,this.useStrictIteration_=!1,this.produce=(t,r,n)=>{if(iu(t)&&!iu(r)){let i=r;r=t;let o=this;return function(s=i,...u){return o.produce(s,c=>r.call(this,c,...u))}}iu(r)||ua(6),n!==void 0&&!iu(n)&&ua(7);let a;if(An(t)){let i=kk(this),o=m1(i,t,void 0),l=!0;try{a=r(o),l=!1}finally{l?d1(i):f1(i)}return Ek(i,n),Ok(a,i)}else if(!t||!v1(t)){if(a=r(t),a===void 0&&(a=t),a===Mk&&(a=void 0),this.autoFreeze_&&g1(a,!0),n){let i=[],o=[];rl(c1).generateReplacementPatches_(t,a,{patches_:i,inversePatches_:o}),n(i,o)}return a}else ua(1,t)},this.produceWithPatches=(t,r)=>{if(iu(t))return(o,...l)=>this.produceWithPatches(o,s=>t(s,...l));let n,a;return[this.produce(t,r,(o,l)=>{n=o,a=l}),n,a]},i1(e?.autoFreeze)&&this.setAutoFreeze(e.autoFreeze),i1(e?.useStrictShallowCopy)&&this.setUseStrictShallowCopy(e.useStrictShallowCopy),i1(e?.useStrictIteration)&&this.setUseStrictIteration(e.useStrictIteration)}createDraft(e){An(e)||ua(8),Ca(e)&&(e=Cr(e));let t=kk(this),r=m1(t,e,void 0);return r[Mr].isManual_=!0,f1(t),r}finishDraft(e,t){let r=e&&e[Mr];(!r||!r.isManual_)&&ua(9);let{scope_:n}=r;return Ek(n,t),Ok(void 0,n)}setAutoFreeze(e){this.autoFreeze_=e}setUseStrictShallowCopy(e){this.useStrictShallowCopy_=e}setUseStrictIteration(e){this.useStrictIteration_=e}shouldUseStrictIteration(){return this.useStrictIteration_}applyPatches(e,t){let r;for(r=t.length-1;r>=0;r--){let a=t[r];if(a.path.length===0&&a.op==="replace"){e=a.value;break}}r>-1&&(t=t.slice(r+1));let n=rl(c1).applyPatches_;return Ca(e)?n(e,t):this.produce(e,a=>n(a,t))}};function m1(e,t,r,n){let[a,i]=jv(t)?rl(kv).proxyMap_(t,r):Iv(t)?rl(kv).proxySet_(t,r):Q9(t,r);return(r?.scope_??jk()).drafts_.push(a),i.callbacks_=r?.callbacks_??[],i.key_=n,r&&n!==void 0?X9(r,i,n):i.callbacks_.push(function(s){s.mapSetPlugin_?.fixSetContents(i);let{patchPlugin_:u}=s;i.modified_&&u&&u.generatePatches_(i,[],s)}),a}function Cr(e){return Ca(e)||ua(10,e),Uk(e)}function Uk(e){if(!An(e)||zv(e))return e;let t=e[Mr],r,n=!0;if(t){if(!t.modified_)return t.base_;t.finalized_=!0,r=u1(e,t.scope_.immer_.useStrictShallowCopy_),n=t.scope_.immer_.shouldUseStrictIteration()}else r=u1(e,!0);return Dv(r,(a,i)=>{Ev(r,a,Uk(i))},n),t&&(t.finalized_=!1),r}var tL=new eL,y1=tL.produce;var $e=e=>e;function $k(e){return({dispatch:r,getState:n})=>a=>i=>typeof i=="function"?i(r,n,e):a(i)}var Hk=$k(),Vk=$k;var rL=typeof window<"u"&&window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__:function(){if(arguments.length!==0)return typeof arguments[0]=="object"?wd:wd.apply(null,arguments)},Fte=typeof window<"u"&&window.__REDUX_DEVTOOLS_EXTENSION__?window.__REDUX_DEVTOOLS_EXTENSION__:function(){return function(e){return e}},nL=e=>e&&typeof e.match=="function";function tr(e,t){function r(...n){if(t){let a=t(...n);if(!a)throw new Error(Nn(0));return{type:e,payload:a.payload,..."meta"in a&&{meta:a.meta},..."error"in a&&{error:a.error}}}return{type:e,payload:n[0]}}return r.toString=()=>`${e}`,r.type=e,r.match=n=>n1(n)&&n.type===e,r}var Zk=class Cd extends Array{constructor(...t){super(...t),Object.setPrototypeOf(this,Cd.prototype)}static get[Symbol.species](){return Cd}concat(...t){return super.concat.apply(this,t)}prepend(...t){return t.length===1&&Array.isArray(t[0])?new Cd(...t[0].concat(this)):new Cd(...t.concat(this))}};function Fk(e){return An(e)?y1(e,()=>{}):e}function Bv(e,t,r){return e.has(t)?e.get(t):e.set(t,r(t)).get(t)}function aL(e){return typeof e=="boolean"}var iL=()=>function(t){let{thunk:r=!0,immutableCheck:n=!0,serializableCheck:a=!0,actionCreatorCheck:i=!0}=t??{},o=new Zk;return r&&(aL(r)?o.push(Hk):o.push(Vk(r.extraArgument))),o},Qk="RTK_autoBatch",lt=()=>e=>({payload:e,meta:{[Qk]:!0}}),qk=e=>t=>{setTimeout(t,e)},oL=(e,t)=>r=>{let n=!1,a=()=>{n||(n=!0,cancelAnimationFrame(i),clearTimeout(o),r())},i=e(a),o=setTimeout(a,t)},S1=(e={type:"raf"})=>t=>(...r)=>{let n=t(...r),a=!0,i=!1,o=!1,l=new Set,s=e.type==="tick"?queueMicrotask:e.type==="raf"?typeof window<"u"&&window.requestAnimationFrame?oL(window.requestAnimationFrame,100):qk(10):e.type==="callback"?e.queueNotification:qk(e.timeout),u=()=>{o=!1,i&&(i=!1,l.forEach(c=>c()))};return Object.assign({},n,{subscribe(c){let d=()=>a&&c(),f=n.subscribe(d);return l.add(c),()=>{f(),l.delete(c)}},dispatch(c){try{return a=!c?.meta?.[Qk],i=!a,i&&(o||(o=!0,s(u))),n.dispatch(c)}finally{a=!0}}})},lL=e=>function(r){let{autoBatch:n=!0}=r??{},a=new Zk(e);return n&&a.push(S1(typeof n=="object"?n:void 0)),a};function Jk(e){let t=iL(),{reducer:r=void 0,middleware:n,devTools:a=!0,duplicateMiddlewareCheck:i=!0,preloadedState:o=void 0,enhancers:l=void 0}=e||{},s;if(typeof r=="function")s=r;else if(bv(r))s=wv(r);else throw new Error(Nn(1));let u;typeof n=="function"?u=n(t):u=t();let c=wd;a&&(c=rL({trace:!1,...typeof a=="object"&&a}));let d=Sk(...u),f=lL(d),p=typeof l=="function"?l(f):f(),v=c(...p);return r1(s,o,v)}function Rk(e){let t={},r=[],n,a={addCase(i,o){let l=typeof i=="string"?i:i.type;if(!l)throw new Error(Nn(28));if(l in t)throw new Error(Nn(29));return t[l]=o,a},addAsyncThunk(i,o){return o.pending&&(t[i.pending.type]=o.pending),o.rejected&&(t[i.rejected.type]=o.rejected),o.fulfilled&&(t[i.fulfilled.type]=o.fulfilled),o.settled&&r.push({matcher:i.settled,reducer:o.settled}),a},addMatcher(i,o){return r.push({matcher:i,reducer:o}),a},addDefaultCase(i){return n=i,a}};return e(a),[t,r,n]}function sL(e){return typeof e=="function"}function uL(e,t){let[r,n,a]=Rk(t),i;if(sL(e))i=()=>Fk(e());else{let l=Fk(e);i=()=>l}function o(l=i(),s){let u=[r[s.type],...n.filter(({matcher:c})=>c(s)).map(({reducer:c})=>c)];return u.filter(c=>!!c).length===0&&(u=[a]),u.reduce((c,d)=>{if(d)if(Ca(c)){let p=d(c,s);return p===void 0?c:p}else{if(An(c))return y1(c,f=>d(f,s));{let f=d(c,s);if(f===void 0){if(c===null)return c;throw Error("A case reducer on a non-draftable value must not return undefined")}return f}}return c},l)}return o.getInitialState=i,o}var cL=(e,t)=>nL(e)?e.match(t):e(t);function dL(...e){return t=>e.some(r=>cL(r,t))}var fL="ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW",eO=(e=21)=>{let t="",r=e;for(;r--;)t+=fL[Math.random()*64|0];return t},pL=["name","message","stack","code"],x1=class{constructor(e,t){this.payload=e,this.meta=t}payload;meta;_type},Yk=class{constructor(e,t){this.payload=e,this.meta=t}payload;meta;_type},mL=e=>{if(typeof e=="object"&&e!==null){let t={};for(let r of pL)typeof e[r]=="string"&&(t[r]=e[r]);return t}return{message:String(e)}},Wk="External signal was aborted",vL=(()=>{function e(t,r,n){let a=tr(t+"/fulfilled",(s,u,c,d)=>({payload:s,meta:{...d||{},arg:c,requestId:u,requestStatus:"fulfilled"}})),i=tr(t+"/pending",(s,u,c)=>({payload:void 0,meta:{...c||{},arg:u,requestId:s,requestStatus:"pending"}})),o=tr(t+"/rejected",(s,u,c,d,f)=>({payload:d,error:(n&&n.serializeError||mL)(s||"Rejected"),meta:{...f||{},arg:c,requestId:u,rejectedWithValue:!!d,requestStatus:"rejected",aborted:s?.name==="AbortError",condition:s?.name==="ConditionError"}}));function l(s,{signal:u}={}){return(c,d,f)=>{let p=n?.idGenerator?n.idGenerator(s):eO(),v=new AbortController,h,b;function m(x){b=x,v.abort()}u&&(u.aborted?m(Wk):u.addEventListener("abort",()=>m(Wk),{once:!0}));let g=(async function(){let x;try{let k=n?.condition?.(s,{getState:d,extra:f});if(gL(k)&&(k=await k),k===!1||v.signal.aborted)throw{name:"ConditionError",message:"Aborted due to condition callback returning false."};let _=new Promise((M,E)=>{h=()=>{E({name:"AbortError",message:b||"Aborted"})},v.signal.addEventListener("abort",h,{once:!0})});c(i(p,s,n?.getPendingMeta?.({requestId:p,arg:s},{getState:d,extra:f}))),x=await Promise.race([_,Promise.resolve(r(s,{dispatch:c,getState:d,extra:f,requestId:p,signal:v.signal,abort:m,rejectWithValue:((M,E)=>new x1(M,E)),fulfillWithValue:((M,E)=>new Yk(M,E))})).then(M=>{if(M instanceof x1)throw M;return M instanceof Yk?a(M.payload,p,s,M.meta):a(M,p,s)})])}catch(k){x=k instanceof x1?o(null,p,s,k.payload,k.meta):o(k,p,s)}finally{h&&v.signal.removeEventListener("abort",h)}return n&&!n.dispatchConditionRejection&&o.match(x)&&x.meta.condition||c(x),x})();return Object.assign(g,{abort:m,requestId:p,arg:s,unwrap(){return g.then(hL)}})}}return Object.assign(l,{pending:i,rejected:o,fulfilled:a,settled:dL(o,a),typePrefix:t})}return e.withTypes=()=>e,e})();function hL(e){if(e.meta&&e.meta.rejectedWithValue)throw e.payload;if(e.error)throw e.error;return e.payload}function gL(e){return e!==null&&typeof e=="object"&&typeof e.then=="function"}var tO=Symbol.for("rtk-slice-createasyncthunk"),Yte={[tO]:vL};function yL(e,t){return`${e}/${t}`}function xL({creators:e}={}){let t=e?.asyncThunk?.[tO];return function(n){let{name:a,reducerPath:i=a}=n;if(!a)throw new Error(Nn(11));typeof process<"u";let o=(typeof n.reducers=="function"?n.reducers(wL()):n.reducers)||{},l=Object.keys(o),s={sliceCaseReducersByName:{},sliceCaseReducersByType:{},actionCreators:{},sliceMatchers:[]},u={addCase(x,A){let k=typeof x=="string"?x:x.type;if(!k)throw new Error(Nn(12));if(k in s.sliceCaseReducersByType)throw new Error(Nn(13));return s.sliceCaseReducersByType[k]=A,u},addMatcher(x,A){return s.sliceMatchers.push({matcher:x,reducer:A}),u},exposeAction(x,A){return s.actionCreators[x]=A,u},exposeCaseReducer(x,A){return s.sliceCaseReducersByName[x]=A,u}};l.forEach(x=>{let A=o[x],k={reducerName:x,type:yL(a,x),createNotation:typeof n.reducers=="function"};AL(A)?_L(k,A,u,t):SL(k,A,u)});function c(){let[x={},A=[],k=void 0]=typeof n.extraReducers=="function"?Rk(n.extraReducers):[n.extraReducers],_={...x,...s.sliceCaseReducersByType};return uL(n.initialState,M=>{for(let E in _)M.addCase(E,_[E]);for(let E of s.sliceMatchers)M.addMatcher(E.matcher,E.reducer);for(let E of A)M.addMatcher(E.matcher,E.reducer);k&&M.addDefaultCase(k)})}let d=x=>x,f=new Map,p=new WeakMap,v;function h(x,A){return v||(v=c()),v(x,A)}function b(){return v||(v=c()),v.getInitialState()}function m(x,A=!1){function k(M){let E=M[x];return typeof E>"u"&&A&&(E=Bv(p,k,b)),E}function _(M=d){let E=Bv(f,A,()=>new WeakMap);return Bv(E,M,()=>{let L={};for(let[I,j]of Object.entries(n.selectors??{}))L[I]=bL(j,M,()=>Bv(p,M,b),A);return L})}return{reducerPath:x,getSelectors:_,get selectors(){return _(k)},selectSlice:k}}let g={name:a,reducer:h,actions:s.actionCreators,caseReducers:s.sliceCaseReducersByName,getInitialState:b,...m(i),injectInto(x,{reducerPath:A,...k}={}){let _=A??i;return x.inject({reducerPath:_,reducer:h},k),{...g,...m(_,!0)}}};return g}}function bL(e,t,r,n){function a(i,...o){let l=t(i);return typeof l>"u"&&n&&(l=r()),e(l,...o)}return a.unwrapped=e,a}var rt=xL();function wL(){function e(t,r){return{_reducerDefinitionType:"asyncThunk",payloadCreator:t,...r}}return e.withTypes=()=>e,{reducer(t){return Object.assign({[t.name](...r){return t(...r)}}[t.name],{_reducerDefinitionType:"reducer"})},preparedReducer(t,r){return{_reducerDefinitionType:"reducerWithPrepare",prepare:t,reducer:r}},asyncThunk:e}}function SL({type:e,reducerName:t,createNotation:r},n,a){let i,o;if("reducer"in n){if(r&&!NL(n))throw new Error(Nn(17));i=n.reducer,o=n.prepare}else i=n;a.addCase(e,i).exposeCaseReducer(t,i).exposeAction(t,o?tr(e,o):tr(e))}function AL(e){return e._reducerDefinitionType==="asyncThunk"}function NL(e){return e._reducerDefinitionType==="reducerWithPrepare"}function _L({type:e,reducerName:t},r,n,a){if(!a)throw new Error(Nn(18));let{payloadCreator:i,fulfilled:o,pending:l,rejected:s,settled:u,options:c}=r,d=a(e,i,c);n.exposeAction(t,d),o&&n.addCase(d.fulfilled,o),l&&n.addCase(d.pending,l),s&&n.addCase(d.rejected,s),u&&n.addMatcher(d.settled,u),n.exposeCaseReducer(t,{fulfilled:o||Uv,pending:l||Uv,rejected:s||Uv,settled:u||Uv})}function Uv(){}var CL="task",rO="listener",nO="completed",A1="cancelled",EL=`task-${A1}`,kL=`task-${nO}`,b1=`${rO}-${A1}`,OL=`${rO}-${nO}`,Vv=class{constructor(e){this.code=e,this.message=`${CL} ${A1} (reason: ${e})`}code;name="TaskAbortError";message},N1=(e,t)=>{if(typeof e!="function")throw new TypeError(Nn(32))},$v=()=>{},aO=(e,t=$v)=>(e.catch(t),e),iO=(e,t)=>(e.addEventListener("abort",t,{once:!0}),()=>e.removeEventListener("abort",t)),nl=e=>{if(e.aborted)throw new Vv(e.reason)};function oO(e,t){let r=$v;return new Promise((n,a)=>{let i=()=>a(new Vv(e.reason));if(e.aborted){i();return}r=iO(e,i),t.finally(()=>r()).then(n,a)}).finally(()=>{r=$v})}var TL=async(e,t)=>{try{return await Promise.resolve(),{status:"ok",value:await e()}}catch(r){return{status:r instanceof Vv?"cancelled":"rejected",error:r}}finally{t?.()}},Hv=e=>t=>aO(oO(e,t).then(r=>(nl(e),r))),lO=e=>{let t=Hv(e);return r=>t(new Promise(n=>setTimeout(n,r)))},{assign:lu}=Object,Kk={},Fv="listenerMiddleware",ML=(e,t)=>{let r=n=>iO(e,()=>n.abort(e.reason));return(n,a)=>{N1(n,"taskExecutor");let i=new AbortController;r(i);let o=TL(async()=>{nl(e),nl(i.signal);let l=await n({pause:Hv(i.signal),delay:lO(i.signal),signal:i.signal});return nl(i.signal),l},()=>i.abort(kL));return a?.autoJoin&&t.push(o.catch($v)),{result:Hv(e)(o),cancel(){i.abort(EL)}}}},DL=(e,t)=>{let r=async(n,a)=>{nl(t);let i=()=>{},l=[new Promise((s,u)=>{let c=e({predicate:n,effect:(d,f)=>{f.unsubscribe(),s([d,f.getState(),f.getOriginalState()])}});i=()=>{c(),u()}})];a!=null&&l.push(new Promise(s=>setTimeout(s,a,null)));try{let s=await oO(t,Promise.race(l));return nl(t),s}finally{i()}};return((n,a)=>aO(r(n,a)))},sO=e=>{let{type:t,actionCreator:r,matcher:n,predicate:a,effect:i}=e;if(t)a=tr(t).match;else if(r)t=r.type,a=r.match;else if(n)a=n;else if(!a)throw new Error(Nn(21));return N1(i,"options.listener"),{predicate:a,type:t,effect:i}},uO=lu(e=>{let{type:t,predicate:r,effect:n}=sO(e);return{id:eO(),effect:n,type:t,predicate:r,pending:new Set,unsubscribe:()=>{throw new Error(Nn(22))}}},{withTypes:()=>uO}),Gk=(e,t)=>{let{type:r,effect:n,predicate:a}=sO(t);return Array.from(e.values()).find(i=>(typeof r=="string"?i.type===r:i.predicate===a)&&i.effect===n)},w1=e=>{e.pending.forEach(t=>{t.abort(b1)})},PL=(e,t)=>()=>{for(let r of t.keys())w1(r);e.clear()},Xk=(e,t,r)=>{try{e(t,r)}catch(n){setTimeout(()=>{throw n},0)}},cO=lu(tr(`${Fv}/add`),{withTypes:()=>cO}),jL=tr(`${Fv}/removeAll`),dO=lu(tr(`${Fv}/remove`),{withTypes:()=>dO}),IL=(...e)=>{console.error(`${Fv}/error`,...e)},pi=(e={})=>{let t=new Map,r=new Map,n=p=>{let v=r.get(p)??0;r.set(p,v+1)},a=p=>{let v=r.get(p)??1;v===1?r.delete(p):r.set(p,v-1)},{extra:i,onError:o=IL}=e;N1(o,"onError");let l=p=>(p.unsubscribe=()=>t.delete(p.id),t.set(p.id,p),v=>{p.unsubscribe(),v?.cancelActive&&w1(p)}),s=(p=>{let v=Gk(t,p)??uO(p);return l(v)});lu(s,{withTypes:()=>s});let u=p=>{let v=Gk(t,p);return v&&(v.unsubscribe(),p.cancelActive&&w1(v)),!!v};lu(u,{withTypes:()=>u});let c=async(p,v,h,b)=>{let m=new AbortController,g=DL(s,m.signal),x=[];try{p.pending.add(m),n(p),await Promise.resolve(p.effect(v,lu({},h,{getOriginalState:b,condition:(A,k)=>g(A,k).then(Boolean),take:g,delay:lO(m.signal),pause:Hv(m.signal),extra:i,signal:m.signal,fork:ML(m.signal,x),unsubscribe:p.unsubscribe,subscribe:()=>{t.set(p.id,p)},cancelActiveListeners:()=>{p.pending.forEach((A,k,_)=>{A!==m&&(A.abort(b1),_.delete(A))})},cancel:()=>{m.abort(b1),p.pending.delete(m)},throwIfCancelled:()=>{nl(m.signal)}})))}catch(A){A instanceof Vv||Xk(o,A,{raisedBy:"effect"})}finally{await Promise.all(x),m.abort(OL),a(p),p.pending.delete(m)}},d=PL(t,r);return{middleware:p=>v=>h=>{if(!n1(h))return v(h);if(cO.match(h))return s(h.payload);if(jL.match(h)){d();return}if(dO.match(h))return u(h.payload);let b=p.getState(),m=()=>{if(b===Kk)throw new Error(Nn(23));return b},g;try{if(g=v(h),t.size>0){let x=p.getState(),A=Array.from(t.values());for(let k of A){let _=!1;try{_=k.predicate(h,x,b)}catch(M){_=!1,Xk(o,M,{raisedBy:"predicate"})}_&&c(k,h,p,m)}}}finally{b=Kk}return g},startListening:s,stopListening:u,clearListeners:d}};function Nn(e){return`Minified Redux Toolkit error #${e}; visit https://redux-toolkit.js.org/Errors?code=${e} for the full message or use the non-minified dev environment for full errors. `}var zL={layoutType:"horizontal",width:0,height:0,margin:{top:5,right:5,bottom:5,left:5},scale:1},fO=rt({name:"chartLayout",initialState:zL,reducers:{setLayout(e,t){e.layoutType=t.payload},setChartSize(e,t){e.width=t.payload.width,e.height=t.payload.height},setMargin(e,t){var r,n,a,i;e.margin.top=(r=t.payload.top)!==null&&r!==void 0?r:0,e.margin.right=(n=t.payload.right)!==null&&n!==void 0?n:0,e.margin.bottom=(a=t.payload.bottom)!==null&&a!==void 0?a:0,e.margin.left=(i=t.payload.left)!==null&&i!==void 0?i:0},setScale(e,t){e.scale=t.payload}}}),qv=fO.actions,_1=qv.setMargin,pO=qv.setLayout,mO=qv.setChartSize,vO=qv.setScale;var hO=fO.reducer;function Yv(e,t,r){return Array.isArray(e)&&e&&t+r!==0?e.slice(t,r+1):e}function Ie(e){return Number.isFinite(e)}function rn(e){return typeof e=="number"&&e>0&&Number.isFinite(e)}function gO(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function nn(e){for(var t=1;t{if(t&&r){var n=r.width,a=r.height,i=t.align,o=t.verticalAlign,l=t.layout,s=t.position,u=t.offset,c=u===void 0?0:u;if(s!=null){if(cE(s)){if(s==="top"&&ye(e.top))return nn(nn({},e),{},{top:e.top+(a||0)+c});if(s==="bottom"&&ye(e.bottom))return nn(nn({},e),{},{bottom:e.bottom+(a||0)+c});if(s==="left"&&ye(e.left))return nn(nn({},e),{},{left:e.left+(n||0)+c});if(s==="right"&&ye(e.right))return nn(nn({},e),{},{right:e.right+(n||0)+c})}return e}if((l==="vertical"||l==="horizontal"&&o==="middle")&&i!=="center"&&ye(e[i]))return nn(nn({},e),{},{[i]:e[i]+(n||0)});if((l==="horizontal"||l==="vertical"&&i==="center")&&o!=="middle"&&ye(e[o]))return nn(nn({},e),{},{[o]:e[o]+(a||0)})}return e},an=(e,t)=>e==="horizontal"&&t==="xAxis"||e==="vertical"&&t==="yAxis"||e==="centric"&&t==="angleAxis"||e==="radial"&&t==="radiusAxis",C1=(e,t,r,n)=>{if(n)return e.map(l=>l.coordinate);var a,i,o=e.map(l=>(l.coordinate===t&&(a=!0),l.coordinate===r&&(i=!0),l.coordinate));return a||o.push(t),i||o.push(r),o},E1=(e,t,r)=>{if(!e)return null;var n=e.duplicateDomain,a=e.type,i=e.range,o=e.scale,l=e.realScaleType,s=e.isCategorical,u=e.categoricalDomain,c=e.tickCount,d=e.ticks,f=e.niceTicks,p=e.axisType;if(!o)return null;var v=l==="scaleBand"&&o.bandwidth?o.bandwidth()/2:2,h=(t||r)&&a==="category"&&o.bandwidth?o.bandwidth()/v:0;if(h=p==="angleAxis"&&i&&i.length>=2?ur(i[0]-i[1])*2*h:h,t&&(d||f)){var b=(d||f||[]).map((m,g)=>{var x=n?n.indexOf(m):m,A=o.map(x);return Ie(A)?{coordinate:A+h,value:m,offset:h,index:g}:null}).filter(cr);return b}return s&&u?u.map((m,g)=>{var x=o.map(m);return Ie(x)?{coordinate:x+h,value:m,index:g,offset:h}:null}).filter(cr):o.ticks&&!r&&c!=null?o.ticks(c).map((m,g)=>{var x=o.map(m);return Ie(x)?{coordinate:x+h,value:m,index:g,offset:h}:null}).filter(cr):o.domain().map((m,g)=>{var x=o.map(m);return Ie(x)?{coordinate:x+h,value:n?n[m]:m,index:g,offset:h}:null}).filter(cr)};var $L=e=>{var t,r=e.length;if(!(r<=0)){var n=(t=e[0])===null||t===void 0?void 0:t.length;if(!(n==null||n<=0))for(var a=0;a=0?(u[0]=i,i+=f,u[1]=i):(u[0]=o,o+=f,u[1]=o)}}}},HL=e=>{var t,r=e.length;if(!(r<=0)){var n=(t=e[0])===null||t===void 0?void 0:t.length;if(!(n==null||n<=0))for(var a=0;a=0?(s[0]=i,i+=u,s[1]=i):(s[0]=0,s[1]=0)}}}},VL={sign:$L,expand:Kb,none:wn,silhouette:Gb,wiggle:Xb,positive:HL},xO=(e,t,r)=>{var n,a=(n=VL[r])!==null&&n!==void 0?n:wn,i=Wb().keys(t).value((l,s)=>Number(_t(l,s,0))).order(Js).offset(a),o=i(e);return o.forEach((l,s)=>{l.forEach((u,c)=>{var d=_t(e[c],t[s],0);Array.isArray(d)&&d.length===2&&ye(d[0])&&ye(d[1])&&(u[0]=d[0],u[1]=d[1])})}),o};function bO(e){return e==null?void 0:String(e)}function k1(e){var t=e.axis,r=e.ticks,n=e.bandSize,a=e.entry,i=e.index,o=e.dataKey;if(t.type==="category"){if(!t.allowDuplicatedCategory&&t.dataKey&&!Mt(a[t.dataKey])){var l=zm(r,"value",a[t.dataKey]);if(l)return l.coordinate+n/2}return r!=null&&r[i]?r[i].coordinate+n/2:null}var s=_t(a,Mt(o)?t.dataKey:o),u=t.scale.map(s);return ye(u)?u:null}var FL=e=>{var t=e.flat(2).filter(ye);return[Math.min(...t),Math.max(...t)]},qL=e=>[e[0]===1/0?0:e[0],e[1]===-1/0?0:e[1]],wO=(e,t,r)=>{if(!(e==null||Object.keys(e).length===0))return qL(Object.keys(e).reduce((n,a)=>{var i=e[a];if(!i)return n;var o=i.stackedData,l=o.reduce((s,u)=>{var c=Yv(u,t,r),d=FL(c);return!Ie(d[0])||!Ie(d[1])?s:[Math.min(s[0],d[0]),Math.max(s[1],d[1])]},[1/0,-1/0]);return[Math.min(l[0],n[0]),Math.max(l[1],n[1])]},[1/0,-1/0]))},O1=/^dataMin[\s]*-[\s]*([0-9]+([.]{1}[0-9]+){0,1})$/,T1=/^dataMax[\s]*\+[\s]*([0-9]+([.]{1}[0-9]+){0,1})$/,su=(e,t,r)=>{if(e&&e.scale&&e.scale.bandwidth){var n=e.scale.bandwidth();if(!r||n>0)return n}if(e&&t&&t.length>=2){for(var a=ci(t,h=>h.coordinate),i=[],o=0,l=1,s=a.length;lf&&(p=Math.min(v,p));return p===1/0?0:p}return r?void 0:0};function M1(e){var t=e.tooltipEntrySettings,r=e.dataKey,n=e.payload,a=e.value,i=e.name;return nn(nn({},t),{},{dataKey:r,payload:n,value:a,name:i})}function D1(e,t){if(e!=null)return String(e);if(typeof t=="string")return t}var SO=(e,t)=>{if(t==="horizontal")return e.relativeX;if(t==="vertical")return e.relativeY},AO=(e,t)=>t==="centric"?e.angle:e.radius;var Dr=e=>e.layout.width,Pr=e=>e.layout.height,NO=e=>e.layout.scale,Wv=e=>e.layout.margin;var uu=V(e=>e.cartesianAxis.xAxis,e=>Object.values(e)),cu=V(e=>e.cartesianAxis.yAxis,e=>Object.values(e));var _O="data-recharts-item-index",CO="data-recharts-item-id",al=60,Ed=30;function EO(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function Kv(e){for(var t=1;te.brush.height;function XL(e){var t=cu(e);return t.reduce((r,n)=>{if(n.orientation==="left"&&!n.mirror&&!n.hide){var a=typeof n.width=="number"?n.width:al;return r+a}return r},0)}function ZL(e){var t=cu(e);return t.reduce((r,n)=>{if(n.orientation==="right"&&!n.mirror&&!n.hide){var a=typeof n.width=="number"?n.width:al;return r+a}return r},0)}function QL(e){var t=uu(e);return t.reduce((r,n)=>{if(n.orientation==="top"&&!n.mirror&&!n.hide){var a=typeof n.height=="number"?n.height:Ed;return r+a}return r},0)}function JL(e){var t=uu(e);return t.reduce((r,n)=>{if(n.orientation==="bottom"&&!n.mirror&&!n.hide){var a=typeof n.height=="number"?n.height:Ed;return r+a}return r},0)}var Ct=V([Dr,Pr,Wv,GL,XL,ZL,QL,JL,e1,hk],(e,t,r,n,a,i,o,l,s,u)=>{var c={left:(r.left||0)+a,right:(r.right||0)+i},d={top:(r.top||0)+o,bottom:(r.bottom||0)+l},f=Kv(Kv({},d),c),p=f.bottom;f.bottom+=n,f=yO(f,s,u);var v=e-f.left-f.right,h=t-f.top-f.bottom;return Kv(Kv({brushBottom:p},f),{},{width:Math.max(v,0),height:Math.max(h,0)})}),kO=V(Ct,e=>({x:e.left,y:e.top,width:e.width,height:e.height})),Gv=V(Dr,Pr,(e,t)=>({x:0,y:0,width:e,height:t}));var RL=te(ae()),Xv=te(ae()),eB=(0,Xv.createContext)(null),xt=()=>(0,Xv.useContext)(eB)!=null;var du=e=>e.brush,il=V([du,Ct,Wv],(e,t,r)=>({height:e.height,x:ye(e.x)?e.x:t.left,y:ye(e.y)?e.y:t.top+t.height+t.brushBottom-(r?.bottom||0),width:ye(e.width)?e.width:t.width}));var ol=te(ae()),rr=te(ae());function OO(e,t,{signal:r,edges:n}={}){let a,i=null,o=n!=null&&n.includes("leading"),l=n==null||n.includes("trailing"),s=()=>{i!==null&&(e.apply(a,i),a=void 0,i=null)},u=()=>{l&&s(),p()},c=null,d=()=>{c!=null&&clearTimeout(c),c=setTimeout(()=>{c=null,u()},t)},f=()=>{c!==null&&(clearTimeout(c),c=null)},p=()=>{f(),a=void 0,i=null},v=()=>{s()},h=function(...b){if(r?.aborted)return;a=this,i=b;let m=c==null;d(),o&&m&&s()};return h.schedule=d,h.cancel=p,h.flush=v,r?.addEventListener("abort",p,{once:!0}),h}function TO(e,t=0,r={}){typeof r!="object"&&(r={});let{leading:n=!1,trailing:a=!0,maxWait:i}=r,o=Array(2);n&&(o[0]="leading"),a&&(o[1]="trailing");let l,s=null,u=OO(function(...f){l=e.apply(this,f),s=null},t,{edges:o}),c=function(...f){return i!=null&&(s===null&&(s=Date.now()),Date.now()-s>=i)?(l=e.apply(this,f),s=Date.now(),u.cancel(),u.schedule(),l):(u.apply(this,f),l)},d=()=>(u.flush(),l);return c.cancel=u.cancel,c.flush=d,c}function P1(e,t=0,r={}){let{leading:n=!0,trailing:a=!0}=r;return TO(e,t,{leading:n,maxWait:t,trailing:a})}var tB=!0,fu=function(t,r){for(var n=arguments.length,a=new Array(n>2?n-2:0),i=2;ia[o++]))}};var ca={width:"100%",height:"100%",debounce:0,minWidth:0,initialDimension:{width:-1,height:-1}},j1=(e,t,r)=>{var n=r.width,a=n===void 0?ca.width:n,i=r.height,o=i===void 0?ca.height:i,l=r.aspect,s=r.maxHeight,u=li(a)?e:Number(a),c=li(o)?t:Number(o);return l&&l>0&&(u?c=u/l:c&&(u=c*l),s&&c!=null&&c>s&&(c=s)),{calculatedWidth:u,calculatedHeight:c}},rB={width:0,height:0,overflow:"visible"},nB={width:0,overflowX:"visible"},aB={height:0,overflowY:"visible"},iB={},MO=e=>{var t=e.width,r=e.height,n=li(t),a=li(r);return n&&a?rB:n?nB:a?aB:iB};function DO(e){var t=e.width,r=e.height,n=e.aspect,a=t,i=r;return a===void 0&&i===void 0?(a=ca.width,i=ca.height):a===void 0?a=n&&n>0?void 0:ca.width:i===void 0&&(i=n&&n>0?void 0:ca.height),{width:a,height:i}}var oB=["aspect","initialDimension","width","height","minWidth","minHeight","maxHeight","children","debounce","id","className","onResize","style"];function Zv(){return Zv=Object.assign?Object.assign.bind():function(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r({width:r,height:n}),[r,n]);return gB(a)?ol.createElement(zO.Provider,{value:a},t):null}var kd=()=>(0,rr.useContext)(zO),yB=(0,rr.forwardRef)((e,t)=>{var r=e.aspect,n=e.initialDimension,a=n===void 0?ca.initialDimension:n,i=e.width,o=e.height,l=e.minWidth,s=l===void 0?ca.minWidth:l,u=e.minHeight,c=e.maxHeight,d=e.children,f=e.debounce,p=f===void 0?ca.debounce:f,v=e.id,h=e.className,b=e.onResize,m=e.style,g=m===void 0?{}:m,x=vB(e,oB),A=(0,rr.useRef)(null),k=(0,rr.useRef)();k.current=b,(0,rr.useImperativeHandle)(t,()=>A.current);var _=(0,rr.useState)({containerWidth:a.width,containerHeight:a.height}),M=cB(_,2),E=M[0],L=M[1],I=(0,rr.useCallback)((y,C)=>{L(O=>{var D=Math.round(y),N=Math.round(C);return O.containerWidth===D&&O.containerHeight===N?O:{containerWidth:D,containerHeight:N}})},[]);(0,rr.useEffect)(()=>{if(A.current==null||typeof ResizeObserver>"u")return sa;var y=T=>{var $,Y=T[0];if(Y!=null){var J=Y.contentRect,fe=J.width,pe=J.height;I(fe,pe),($=k.current)===null||$===void 0||$.call(k,fe,pe)}};p>0&&(y=P1(y,p,{trailing:!0,leading:!1}));var C=new ResizeObserver(y),O=A.current.getBoundingClientRect(),D=O.width,N=O.height;return I(D,N),C.observe(A.current),()=>{C.disconnect()}},[I,p]);var j=E.containerWidth,z=E.containerHeight;fu(!r||r>0,"The aspect(%s) must be greater than zero.",r);var X=j1(j,z,{width:i,height:o,aspect:r,maxHeight:c}),R=X.calculatedWidth,Q=X.calculatedHeight;return fu(j<0||z<0||R!=null&&R>0||Q!=null&&Q>0,`The width(%s) and height(%s) of chart should be greater than 0, + please check the style of container, or the props width(%s) and height(%s), + or add a minWidth(%s) or minHeight(%s) or use aspect(%s) to control the + height and width.`,R,Q,i,o,s,u,r),ol.createElement("div",Zv({id:v?"".concat(v):void 0,className:Le("recharts-responsive-container",h),style:jO(jO({},g),{},{width:i,height:o,minWidth:s,minHeight:u,maxHeight:c}),ref:A},x),ol.createElement("div",{style:MO({width:i,height:o})},ol.createElement(LO,{width:R,height:Q},d)))}),pu=(0,rr.forwardRef)((e,t)=>{var r=kd();if(rn(r.width)&&rn(r.height))return e.children;var n=DO({width:e.width,height:e.height,aspect:e.aspect}),a=n.width,i=n.height,o=j1(void 0,void 0,{width:a,height:i,aspect:e.aspect,maxHeight:e.maxHeight}),l=o.calculatedWidth,s=o.calculatedHeight;return ye(l)&&ye(s)?ol.createElement(LO,{width:l,height:s},e.children):ol.createElement(yB,Zv({},e,{width:a,height:i,ref:t}))});var ll=()=>{var e,t=xt(),r=Se(kO),n=Se(il),a=(e=Se(du))===null||e===void 0?void 0:e.padding;return!t||!n||!a?r:{width:n.width-a.left-a.right,height:n.height-a.top-a.bottom,x:a.left,y:a.top}},xB={top:0,bottom:0,left:0,right:0,width:0,height:0,brushBottom:0},Qv=()=>{var e;return(e=Se(Ct))!==null&&e!==void 0?e:xB},Jv=()=>Se(Dr),Rv=()=>Se(Pr);var ot=e=>e.layout.layoutType,Ea=()=>Se(ot),mu=()=>{var e=Ea();if(e==="horizontal"||e==="vertical")return e},I1=e=>{var t=e.layout.layoutType;if(t==="centric"||t==="radial")return t};var UO=()=>{var e=Ea();return e!==void 0},sl=e=>{var t=Ve(),r=xt(),n=e.width,a=e.height,i=kd(),o=n,l=a;return i&&(o=i.width>0?i.width:n,l=i.height>0?i.height:a),(0,BO.useEffect)(()=>{!r&&rn(o)&&rn(l)&&t(mO({width:o,height:l}))},[t,r,o,l]),null};var bB={settings:{layout:"horizontal",align:"center",verticalAlign:"bottom",itemSorter:"value",position:void 0,offset:0},size:{width:0,height:0},payload:[]},$O=rt({name:"legend",initialState:bB,reducers:{setLegendSize(e,t){e.size.width=t.payload.width,e.size.height=t.payload.height},setLegendSettings(e,t){e.settings.align=t.payload.align,e.settings.layout=t.payload.layout,e.settings.verticalAlign=t.payload.verticalAlign,e.settings.itemSorter=t.payload.itemSorter,e.settings.position=t.payload.position,e.settings.offset=t.payload.offset},addLegendPayload:{reducer(e,t){e.payload.push($e(t.payload))},prepare:lt()},replaceLegendPayload:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=Cr(e).payload.indexOf($e(n));i>-1&&(e.payload[i]=$e(a))},prepare:lt()},removeLegendPayload:{reducer(e,t){var r=Cr(e).payload.indexOf($e(t.payload));r>-1&&e.payload.splice(r,1)},prepare:lt()}}}),Od=$O.actions,ene=Od.setLegendSize,tne=Od.setLegendSettings,HO=Od.addLegendPayload,VO=Od.replaceLegendPayload,FO=Od.removeLegendPayload;var qO=$O.reducer;var $t=te(ae(),1),GB=te(GO(),1);var kB=Symbol.for("react.forward_ref");var OB=Symbol.for("react.memo");var TB=kB,MB=OB;function DB(e){e()}function PB(){let e=null,t=null;return{clear(){e=null,t=null},notify(){DB(()=>{let r=e;for(;r;)r.callback(),r=r.next})},get(){let r=[],n=e;for(;n;)r.push(n),n=n.next;return r},subscribe(r){let n=!0,a=t={callback:r,next:null,prev:t};return a.prev?a.prev.next=a:e=a,function(){!n||e===null||(n=!1,a.next?a.next.prev=a.prev:t=a.prev,a.prev?a.prev.next=a.next:e=a.next)}}}}var XO={notify(){},get:()=>[]};function jB(e,t){let r,n=XO,a=0,i=!1;function o(h){c();let b=n.subscribe(h),m=!1;return()=>{m||(m=!0,b(),d())}}function l(){n.notify()}function s(){v.onStateChange&&v.onStateChange()}function u(){return i}function c(){a++,r||(r=t?t.addNestedSub(s):e.subscribe(s),n=PB())}function d(){a--,r&&a===0&&(r(),r=void 0,n.clear(),n=XO)}function f(){i||(i=!0,c())}function p(){i&&(i=!1,d())}let v={addNestedSub:o,notifyNestedSubs:l,handleChangeWrapper:s,isSubscribed:u,trySubscribe:f,tryUnsubscribe:p,getListeners:()=>n};return v}var IB=()=>typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",zB=IB(),LB=()=>typeof navigator<"u"&&navigator.product==="ReactNative",BB=LB(),UB=()=>zB||BB?$t.useLayoutEffect:$t.useEffect,$B=UB();function ZO(e,t){return e===t?e!==0||t!==0||1/e===1/t:e!==e&&t!==t}function QO(e,t){if(ZO(e,t))return!0;if(typeof e!="object"||e===null||typeof t!="object"||t===null)return!1;let r=Object.keys(e),n=Object.keys(t);if(r.length!==n.length)return!1;for(let a=0;a{let s=jB(a);return{store:a,subscription:s,getServerState:n?()=>n:void 0}},[a,n]),o=$t.useMemo(()=>a.getState(),[a]);return $B(()=>{let{subscription:s}=i;return s.onStateChange=s.notifyNestedSubs,s.trySubscribe(),o!==a.getState()&&s.notifyNestedSubs(),()=>{s.tryUnsubscribe(),s.onStateChange=void 0}},[i,o]),$t.createElement((r||WB).Provider,{value:i},t)}var JO=KB;var XB=new Set(["axisLine","tickLine","activeBar","activeDot","activeLabel","activeShape","allowEscapeViewBox","background","cursor","dot","label","line","margin","padding","position","shape","style","tick","wrapperStyle","radius","throttledEvents"]);function ZB(e,t){return e==null&&t==null?!0:typeof e=="number"&&typeof t=="number"?e===t||e!==e&&t!==t:e===t}function po(e,t){var r=new Set([...Object.keys(e),...Object.keys(t)]);for(var n of r)if(XB.has(n)){if(e[n]==null&&t[n]==null)continue;if(!QO(e[n],t[n]))return!1}else if(!ZB(e[n],t[n]))return!1;return!0}var Zn=te(ae()),I8=te(ae()),z8=te(ap());var da=te(ae());function z1(){return z1=Object.assign?Object.assign.bind():function(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r{var t=e.separator,r=t===void 0?vu.separator:t,n=e.contentStyle,a=e.itemStyle,i=e.labelStyle,o=i===void 0?vu.labelStyle:i,l=e.payload,s=e.formatter,u=e.itemSorter,c=e.wrapperClassName,d=e.labelClassName,f=e.label,p=e.labelFormatter,v=e.accessibilityLayer,h=v===void 0?vu.accessibilityLayer:v,b=()=>{if(l&&l.length){var E={padding:0,margin:0},L=oU(l,u),I=L.map((j,z)=>{if(!j||j.type==="none")return null;var X=j.formatter||s||iU,R=j.value,Q=j.name,y=R,C=Q;if(X){var O=X(R,Q,j,z,l);if(Array.isArray(O)){var D=eU(O,2);y=D[0],C=D[1]}else if(O!=null)y=O;else return null}var N=Md(Md({},vu.itemStyle),{},{color:j.color||vu.itemStyle.color},a);return da.createElement("li",{className:"recharts-tooltip-item",key:"tooltip-item-".concat(z),style:N},Fr(C)?da.createElement("span",{className:"recharts-tooltip-item-name"},C):null,Fr(C)?da.createElement("span",{className:"recharts-tooltip-item-separator"},r):null,da.createElement("span",{className:"recharts-tooltip-item-value"},y),da.createElement("span",{className:"recharts-tooltip-item-unit"},j.unit||""))});return da.createElement("ul",{className:"recharts-tooltip-item-list",style:E},I)}return null},m=Md(Md({},vu.contentStyle),n),g=Md({margin:0},o),x=!Mt(f),A=x?f:"",k=Le("recharts-default-tooltip",c),_=Le("recharts-tooltip-label",d);x&&p&&l!==void 0&&l!==null&&(A=p(f,l));var M=h?{role:"status","aria-live":"assertive"}:{};return da.createElement("div",z1({className:k,style:m},M),da.createElement("p",{className:_,style:g},da.isValidElement(A)?A:"".concat(A)),b())};var mo=te(ae());var Dd="recharts-tooltip-wrapper",lU={visibility:"hidden"};function sU(e){var t=e.coordinate,r=e.translateX,n=e.translateY;return Le(Dd,{["".concat(Dd,"-right")]:ye(r)&&t&&ye(t.x)&&r>=t.x,["".concat(Dd,"-left")]:ye(r)&&t&&ye(t.x)&&r=t.y,["".concat(Dd,"-top")]:ye(n)&&t&&ye(t.y)&&n0?a:0),d=r[n]+a;if(t[n])return o[n]?c:d;var f=s[n];if(f==null)return 0;if(o[n]){var p=c,v=f;return pb?Math.max(c,f):Math.max(d,f)}function uU(e){var t=e.translateX,r=e.translateY,n=e.useTranslate3d;return{transform:n?"translate3d(".concat(t,"px, ").concat(r,"px, 0)"):"translate(".concat(t,"px, ").concat(r,"px)")}}function nT(e){var t=e.allowEscapeViewBox,r=e.coordinate,n=e.offsetTop,a=e.offsetLeft,i=e.position,o=e.reverseDirection,l=e.tooltipBox,s=e.useTranslate3d,u=e.viewBox,c,d,f;return l&&l.height>0&&l.width>0&&r?(d=rT({allowEscapeViewBox:t,coordinate:r,key:"x",offset:a,position:i,reverseDirection:o,tooltipDimension:l.width,viewBox:u,viewBoxDimension:u.width}),f=rT({allowEscapeViewBox:t,coordinate:r,key:"y",offset:n,position:i,reverseDirection:o,tooltipDimension:l.height,viewBox:u,viewBoxDimension:u.height}),c=uU({translateX:d,translateY:f,useTranslate3d:s})):c=lU,{cssProperties:c,cssClasses:sU({translateX:d,translateY:f,coordinate:r})}}var eh=te(ae());var cU=()=>!(typeof window<"u"&&window.document&&window.document.createElement&&window.setTimeout),Yn={devToolsEnabled:!0,isSsr:cU()};function dU(e,t){return vU(e)||mU(e,t)||pU(e,t)||fU()}function fU(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function pU(e,t){if(e){if(typeof e=="string")return aT(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?aT(e,t):void 0}}function aT(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);rYn.isSsr||!window.matchMedia?!1:window.matchMedia("(prefers-reduced-motion: reduce)").matches),t=dU(e,2),r=t[0],n=t[1];return(0,eh.useEffect)(()=>{if(window.matchMedia){var a=window.matchMedia("(prefers-reduced-motion: reduce)"),i=()=>{n(a.matches)};return a.addEventListener("change",i),()=>{a.removeEventListener("change",i)}}},[]),r}function iT(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function hu(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r({dismissed:!1,dismissedAtCoordinate:{x:0,y:0}})),u=xU(s,2),c=u[0],d=u[1];mo.useEffect(()=>{var m=g=>{if(g.key==="Escape"){var x,A,k,_;d({dismissed:!0,dismissedAtCoordinate:{x:(x=(A=e.coordinate)===null||A===void 0?void 0:A.x)!==null&&x!==void 0?x:0,y:(k=(_=e.coordinate)===null||_===void 0?void 0:_.y)!==null&&k!==void 0?k:0}})}};return document.addEventListener("keydown",m),()=>{document.removeEventListener("keydown",m)}},[(t=e.coordinate)===null||t===void 0?void 0:t.x,(r=e.coordinate)===null||r===void 0?void 0:r.y]),c.dismissed&&(((n=(a=e.coordinate)===null||a===void 0?void 0:a.x)!==null&&n!==void 0?n:0)!==c.dismissedAtCoordinate.x||((i=(o=e.coordinate)===null||o===void 0?void 0:o.y)!==null&&i!==void 0?i:0)!==c.dismissedAtCoordinate.y)&&d(hu(hu({},c),{},{dismissed:!1}));var f=nT({allowEscapeViewBox:e.allowEscapeViewBox,coordinate:e.coordinate,offsetLeft:typeof e.offset=="number"?e.offset:e.offset.x,offsetTop:typeof e.offset=="number"?e.offset:e.offset.y,position:e.position,reverseDirection:e.reverseDirection,tooltipBox:e.lastBoundingBox,useTranslate3d:e.useTranslate3d,viewBox:e.viewBox}),p=f.cssClasses,v=f.cssProperties,h=e.hasPortalFromProps?{}:hu(hu({transition:NU({prefersReducedMotion:l,isAnimationActive:e.isAnimationActive,active:e.active,animationDuration:e.animationDuration,animationEasing:e.animationEasing})},v),{},{pointerEvents:"none",position:"absolute",top:0,left:0}),b=hu(hu({},h),{},{visibility:!c.dismissed&&e.active&&e.hasPayload?"visible":"hidden"},e.wrapperStyle);return mo.createElement("div",{xmlns:"http://www.w3.org/1999/xhtml",tabIndex:-1,className:p,style:b,ref:e.innerRef},e.children)}var lT=mo.memo(_U);var rh=()=>{var e;return(e=Se(t=>t.rootProps.accessibilityLayer))!==null&&e!==void 0?e:!0};var P0=te(ae()),Yu=te(ae());var pT=te(ae());function L1(){return L1=Object.assign?Object.assign.bind():function(e){for(var t=1;tIe(e.x)&&Ie(e.y),dT=e=>e.base!=null&&nh(e.base)&&nh(e),Pd=e=>e.x,jd=e=>e.y,OU=(e,t)=>{if(typeof e=="function")return e;var r="curve".concat(Ks(e));if((r==="curveMonotone"||r==="curveBump")&&t){var n=cT["".concat(r).concat(t==="vertical"?"Y":"X")];if(n)return n}return cT[r]||uo},fT={connectNulls:!1,type:"linear"},TU=e=>{var t=e.type,r=t===void 0?fT.type:t,n=e.points,a=n===void 0?[]:n,i=e.baseLine,o=e.layout,l=e.connectNulls,s=l===void 0?fT.connectNulls:l,u=OU(r,o),c=s?a.filter(nh):a;if(Array.isArray(i)){var d,f=a.map((m,g)=>uT(uT({},m),{},{base:i[g]}));o==="vertical"?d=Xs().y(jd).x1(Pd).x0(m=>m.base.x):d=Xs().x(Pd).y1(jd).y0(m=>m.base.y);var p=d.defined(dT).curve(u),v=s?f.filter(dT):f;return p(v)}var h;o==="vertical"&&ye(i)?h=Xs().y(jd).x1(Pd).x0(i):ye(i)?h=Xs().x(Pd).y1(jd).y0(i):h=md().x(Pd).y(jd);var b=h.defined(nh).curve(u);return b(c)},gu=e=>{var t=e.className,r=e.points,n=e.path,a=e.pathRef,i=Ea();if((!r||!r.length)&&!n)return null;var o={type:e.type,points:e.points,baseLine:e.baseLine,layout:e.layout||i,connectNulls:e.connectNulls},l=r&&r.length?TU(o):n;return pT.createElement("path",L1({},hr(e),Rs(e),{className:Le("recharts-curve",t),d:l===null?void 0:l,ref:a}))};var vT=te(ae());var MU=["x","y","top","left","width","height","className"];function B1(){return B1=Object.assign?Object.assign.bind():function(e){for(var t=1;t"M".concat(e,",").concat(a,"v").concat(n,"M").concat(i,",").concat(t,"h").concat(r),hT=e=>{var t=e.x,r=t===void 0?0:t,n=e.y,a=n===void 0?0:n,i=e.top,o=i===void 0?0:i,l=e.left,s=l===void 0?0:l,u=e.width,c=u===void 0?0:u,d=e.height,f=d===void 0?0:d,p=e.className,v=zU(e,MU),h=DU({x:r,y:a,top:o,left:s,width:c,height:f},v);return!ye(r)||!ye(a)||!ye(c)||!ye(f)||!ye(o)||!ye(s)?null:vT.createElement("path",B1({},Wt(h),{className:Le("recharts-cross",p),d:BU(r,a,c,f,o,s)}))};function gT(e,t,r,n){var a=n/2;return{stroke:"none",fill:"#ccc",x:e==="horizontal"?t.x-a:r.left+.5,y:e==="horizontal"?r.top+.5:t.y-a,width:e==="horizontal"?n:r.width-1,height:e==="horizontal"?r.height-1:n}}var uh=te(ae()),Wn=te(ae());var Id=te(ae());var ah=1e-4,bT=(e,t)=>[0,3*e,3*t-6*e,3*e-3*t+1],wT=(e,t)=>e.map((r,n)=>r*t**n).reduce((r,n)=>r+n),yT=(e,t)=>r=>{var n=bT(e,t);return wT(n,r)},UU=(e,t)=>r=>{var n=bT(e,t),a=[...n.map((i,o)=>i*o).slice(1),0];return wT(a,r)},$U=e=>{var t,r=e.split("(");if(r.length!==2||r[0]!=="cubic-bezier")return null;var n=(t=r[1])===null||t===void 0||(t=t.split(")")[0])===null||t===void 0?void 0:t.split(",");if(n==null||n.length!==4)return null;var a=n.map(i=>parseFloat(i));return[a[0],a[1],a[2],a[3]]},HU=function(){for(var t=arguments.length,r=new Array(t),n=0;n{var a=yT(e,r),i=yT(t,n),o=UU(e,r),l=u=>u>1?1:u<0?0:u,s=u=>{for(var c=u>1?1:u,d=c,f=0;f<8;++f){var p=a(d)-c,v=o(d);if(Math.abs(p-c)0&&arguments[0]!==void 0?arguments[0]:{},r=t.stiff,n=r===void 0?100:r,a=t.damping,i=a===void 0?8:a,o=t.dt,l=o===void 0?16.67:o,s=1,u=[0],c=0,d=0,f=1e4,p=0;p{var g,x,A;if(m<=0)return 0;if(m>=1)return s;var k=m*b,_=Math.floor(k),M=k-_;return((g=u[_])!==null&&g!==void 0?g:0)+(((x=u[_+1])!==null&&x!==void 0?x:0)-((A=u[_])!==null&&A!==void 0?A:0))*M}},ST=e=>{if(typeof e=="string")switch(e){case"ease":case"ease-in-out":case"ease-out":case"ease-in":case"linear":return xT(e);case"spring":return FU();default:if(e.split("(")[0]==="cubic-bezier")return xT(e)}return typeof e=="function"?e:null};var yu=te(ae());var AT=(e,t,r)=>{var n,a=i=>{var o=t.tick(i);if(t.getState()==="active"){if(r(t.getInterpolated()),t.getProgress()===1){t.complete(),n=void 0;return}n=e.setTimeout(a,o);return}n=e.setTimeout(a,o)};return n=e.setTimeout(a,0),()=>{var i;return(i=n)===null||i===void 0?void 0:i()}};var NT=(0,yu.createContext)(AT),Hne=NT.Provider;function _T(e){var t=(0,yu.useContext)(NT);return(0,yu.useMemo)(()=>e??t,[e,t])}function qU(e,t,r){return(t=YU(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function YU(e){var t=WU(e,"string");return typeof t=="symbol"?t:t+""}function WU(e,t){if(typeof e!="object"||!e)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var n=r.call(e,t||"default");if(typeof n!="object")return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}var CT="init",ET="pending",kT="active",KU="completed";function U1(e){return Math.max(0,e)}var $1=class{getAnimationStartedTime(){return this.animationStartedTime}getBeginStartedTime(){return this.beginStartedTime}constructor(t){var r;qU(this,"state",CT),this.animationId=t.animationId,this.onAnimationEnd=t.onAnimationEnd,this.animationDuration=U1(t.animationDuration),this.animationBegin=U1(t.animationBegin),this.progress=0,this.from=t.from,this.to=t.to,this.easing=t.easing,(r=t.onAnimationStart)===null||r===void 0||r.call(t)}getState(){return this.state}getEasing(){return this.easing}getAnimationDuration(){return this.animationDuration}tick(t){if(this.getState()===CT)return this.state=ET,this.beginStartedTime=t,this.animationBegin;if(this.getState()===ET){if(this.beginStartedTime==null)throw new Error;var r=t-this.beginStartedTime;return r>=this.animationBegin?(this.state=kT,this.animationStartedTime=t,this.nextAnimationUpdate(0)):U1(this.animationBegin-r)}if(this.getState()===kT){if(this.animationStartedTime==null)throw new Error;var n=t-this.animationStartedTime;return this.setProgress(n/this.animationDuration),this.nextAnimationUpdate(n)}return 0}setProgress(t){this.progress=Math.min(1,Math.max(0,t))}getProgress(){return this.progress}complete(){if(this.progress=1,this.state==="active"){var t;(t=this.onAnimationEnd)===null||t===void 0||t.call(this)}this.state=KU}getFrom(){return this.from}getTo(){return this.to}getAnimationId(){return this.animationId}getAnimationBegin(){return this.animationBegin}},ih=class extends $1{nextAnimationUpdate(){return 0}getInterpolated(){return this.easing(Fn(this.getFrom(),this.getTo(),this.getProgress()))}};var oh=class{setTimeout(t){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:0,n=performance.now(),a=null,i=o=>{o-n>=r?t(o):a=requestAnimationFrame(i)};return a=requestAnimationFrame(i),()=>{a!=null&&cancelAnimationFrame(a)}}};function GU(e,t){return JU(e)||QU(e,t)||ZU(e,t)||XU()}function XU(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function ZU(e,t){if(e){if(typeof e=="string")return OT(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?OT(e,t):void 0}}function OT(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{},onAnimationStart:()=>{}},TT=0,H1=1;function lh(e){var t=Dt(e,RU),r=t.animationId,n=t.isActive,a=t.canBegin,i=t.duration,o=t.easing,l=t.begin,s=t.onAnimationEnd,u=t.onAnimationStart,c=t.children,d=th(),f=n==="auto"?!Yn.isSsr&&!d:n,p=_T(t.animationController),v=(0,Id.useState)(f?TT:H1),h=GU(v,2),b=h[0],m=h[1];return(0,Id.useEffect)(()=>{f||m(H1)},[f]),(0,Id.useEffect)(()=>{var g=ST(o);if(!f||!a||g==null)return sa;var x=new oh,A=new ih({animationId:r,easing:g,animationDuration:i,animationBegin:l,onAnimationStart:u,onAnimationEnd:s,from:TT,to:H1});return p(x,A,m)},[p,r,f,a,i,o,l,u,s]),c(Number(b))}var V1=te(ae());function sh(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:"animation-",r=(0,V1.useRef)(si(t)),n=(0,V1.useRef)(e);return n.current!==e&&(r.current=si(t),n.current=e),r.current}var e$=e=>e.replace(/([A-Z])/g,t=>"-".concat(t.toLowerCase())),MT=(e,t,r)=>e.map(n=>"".concat(e$(n)," ").concat(t,"ms ").concat(r)).join(",");var t$=["radius"],r$=["radius"],DT,PT,jT,IT,zT,LT,BT,UT,$T,HT;function VT(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function FT(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r{var i=Na(r),o=Na(n),l=Math.min(Math.abs(i)/2,Math.abs(o)/2),s=o>=0?1:-1,u=i>=0?1:-1,c=o>=0&&i>=0||o<0&&i<0?1:0,d;if(l>0&&Array.isArray(a)){for(var f=[0,0,0,0],p=0,v=4;pl?l:b}d=sr(DT||(DT=ka(["M",",",""])),e,t+s*f[0]),f[0]>0&&(d+=sr(PT||(PT=ka(["A ",",",",0,0,",",",",",""])),f[0],f[0],c,e+u*f[0],t)),d+=sr(jT||(jT=ka(["L ",",",""])),e+r-u*f[1],t),f[1]>0&&(d+=sr(IT||(IT=ka(["A ",",",",0,0,",`, + `,",",""])),f[1],f[1],c,e+r,t+s*f[1])),d+=sr(zT||(zT=ka(["L ",",",""])),e+r,t+n-s*f[2]),f[2]>0&&(d+=sr(LT||(LT=ka(["A ",",",",0,0,",`, + `,",",""])),f[2],f[2],c,e+r-u*f[2],t+n)),d+=sr(BT||(BT=ka(["L ",",",""])),e+u*f[3],t+n),f[3]>0&&(d+=sr(UT||(UT=ka(["A ",",",",0,0,",`, + `,",",""])),f[3],f[3],c,e,t+n-s*f[3])),d+="Z"}else if(l>0&&a===+a&&a>0){var m=Math.min(l,a);d=sr($T||($T=ka(["M ",",",` + A `,",",",0,0,",",",",",` + L `,",",` + A `,",",",0,0,",",",",",` + L `,",",` + A `,",",",0,0,",",",",",` + L `,",",` + A `,",",",0,0,",",",","," Z"])),e,t+s*m,m,m,c,e+u*m,t,e+r-u*m,t,m,m,c,e+r,t+s*m,e+r,t+n-s*m,m,m,c,e+r-u*m,t+n,e+u*m,t+n,m,m,c,e,t+n-s*m)}else d=sr(HT||(HT=ka(["M ",","," h "," v "," h "," Z"])),e,t,r,n,-r);return d},KT={x:0,y:0,width:0,height:0,radius:0,isAnimationActive:!1,isUpdateAnimationActive:!1,animationBegin:0,animationDuration:1500,animationEasing:"ease"},GT=e=>{var t=Dt(e,KT),r=(0,Wn.useRef)(null),n=(0,Wn.useState)(-1),a=l$(n,2),i=a[0],o=a[1];(0,Wn.useEffect)(()=>{if(r.current&&r.current.getTotalLength)try{var D=r.current.getTotalLength();D&&o(D)}catch{}},[]);var l=t.x,s=t.y,u=t.width,c=t.height,d=t.radius,f=t.className,p=t.animationEasing,v=t.animationDuration,h=t.animationBegin,b=t.isAnimationActive,m=t.isUpdateAnimationActive,g=(0,Wn.useRef)(u),x=(0,Wn.useRef)(c),A=(0,Wn.useRef)(l),k=(0,Wn.useRef)(s),_=(0,Wn.useMemo)(()=>({x:l,y:s,width:u,height:c,radius:d}),[l,s,u,c,d]),M=sh(_,"rectangle-");if(l!==+l||s!==+s||u!==+u||c!==+c||u===0||c===0)return null;var E=Le("recharts-rectangle",f);if(!m){var L=Wt(t),I=L.radius,j=qT(L,t$);return uh.createElement("path",ch({},j,{x:Na(l),y:Na(s),width:Na(u),height:Na(c),radius:typeof d=="number"?d:void 0,className:E,d:WT(l,s,u,c,d)}))}var z=g.current,X=x.current,R=A.current,Q=k.current,y="0px ".concat(i===-1?1:i,"px"),C="".concat(i,"px ").concat(i,"px"),O=MT(["strokeDasharray"],v,typeof p=="string"?p:KT.animationEasing);return uh.createElement(lh,{animationId:M,key:M,canBegin:i>0,duration:v,easing:p,isActive:m,begin:h},D=>{var N=Fn(z,u,D),T=Fn(X,c,D),$=Fn(R,l,D),Y=Fn(Q,s,D);r.current&&(g.current=N,x.current=T,A.current=$,k.current=Y);var J;b?D>0?J={transition:O,strokeDasharray:C}:J={strokeDasharray:y}:J={strokeDasharray:C};var fe=Wt(t),pe=fe.radius,ge=qT(fe,r$);return uh.createElement("path",ch({},ge,{radius:typeof d=="number"?d:void 0,className:E,d:WT($,Y,N,T,d),ref:r,style:FT(FT({},J),t.style)}))})};function XT(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function ZT(e){for(var t=1;te*180/Math.PI,Ht=(e,t,r,n)=>({x:e+Math.cos(-zd*n)*r,y:t+Math.sin(-zd*n)*r}),QT=function(t,r){var n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{top:0,right:0,bottom:0,left:0,width:0,height:0,brushBottom:0};return Math.min(Math.abs(t-(n.left||0)-(n.right||0)),Math.abs(r-(n.top||0)-(n.bottom||0)))/2},h$=(e,t)=>{var r=e.x,n=e.y,a=t.x,i=t.y;return Math.sqrt((r-a)**2+(n-i)**2)},g$=(e,t)=>{var r=e.x,n=e.y,a=t.cx,i=t.cy,o=h$({x:r,y:n},{x:a,y:i});if(o<=0)return{radius:o,angle:0};var l=(r-a)/o,s=Math.acos(l);return n>i&&(s=2*Math.PI-s),{radius:o,angle:v$(s),angleInRadian:s}},y$=e=>{var t=e.startAngle,r=e.endAngle,n=Math.floor(t/360),a=Math.floor(r/360),i=Math.min(n,a);return{startAngle:t-i*360,endAngle:r-i*360}},x$=(e,t)=>{var r=t.startAngle,n=t.endAngle,a=Math.floor(r/360),i=Math.floor(n/360),o=Math.min(a,i);return e+o*360},JT=(e,t)=>{var r=e.relativeX,n=e.relativeY,a=g$({x:r,y:n},t),i=a.radius,o=a.angle,l=t.innerRadius,s=t.outerRadius;if(is||i===0)return null;var u=y$(t),c=u.startAngle,d=u.endAngle,f=o,p;if(c<=d){for(;f>d;)f-=360;for(;f=c&&f<=d}else{for(;f>c;)f-=360;for(;f=d&&f<=c}return p?ZT(ZT({},t),{},{radius:i,angle:x$(f,t)}):null};function dh(e){var t=e.cx,r=e.cy,n=e.radius,a=e.startAngle,i=e.endAngle,o=Ht(t,r,n,a),l=Ht(t,r,n,i);return{points:[o,l],cx:t,cy:r,radius:n,startAngle:a,endAngle:i}}var o3=te(ae());var RT,e3,t3,r3,n3,a3,i3;function F1(){return F1=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var r=ur(t-e),n=Math.min(Math.abs(t-e),359.999);return r*n},fh=e=>{var t=e.cx,r=e.cy,n=e.radius,a=e.angle,i=e.sign,o=e.isExternal,l=e.cornerRadius,s=e.cornerIsExternal,u=l*(o?1:-1)+n,c=Math.asin(l/u)/zd,d=s?a:a+i*c,f=Ht(t,r,u,d),p=Ht(t,r,n,d),v=s?a-i*c:a,h=Ht(t,r,u*Math.cos(c*zd),v);return{center:f,circleTangency:p,lineTangency:h,theta:c}},l3=e=>{var t=e.cx,r=e.cy,n=e.innerRadius,a=e.outerRadius,i=e.startAngle,o=e.endAngle,l=b$(i,o),s=i+l,u=Ht(t,r,a,i),c=Ht(t,r,a,s),d=sr(RT||(RT=ul(["M ",",",` + A `,",",`,0, + `,",",`, + `,",",` + `])),u.x,u.y,a,a,+(Math.abs(l)>180),+(i>s),c.x,c.y);if(n>0){var f=Ht(t,r,n,i),p=Ht(t,r,n,s);d+=sr(e3||(e3=ul(["L ",",",` + A `,",",`,0, + `,",",`, + `,","," Z"])),p.x,p.y,n,n,+(Math.abs(l)>180),+(i<=s),f.x,f.y)}else d+=sr(t3||(t3=ul(["L ",","," Z"])),t,r);return d},w$=e=>{var t=e.cx,r=e.cy,n=e.innerRadius,a=e.outerRadius,i=e.cornerRadius,o=e.forceCornerRadius,l=e.cornerIsExternal,s=e.startAngle,u=e.endAngle,c=ur(u-s),d=fh({cx:t,cy:r,radius:a,angle:s,sign:c,cornerRadius:i,cornerIsExternal:l}),f=d.circleTangency,p=d.lineTangency,v=d.theta,h=fh({cx:t,cy:r,radius:a,angle:u,sign:-c,cornerRadius:i,cornerIsExternal:l}),b=h.circleTangency,m=h.lineTangency,g=h.theta,x=l?Math.abs(s-u):Math.abs(s-u)-v-g;if(x<0)return o?sr(r3||(r3=ul(["M ",",",` + a`,",",",0,0,1,",`,0 + a`,",",",0,0,1,",`,0 + `])),p.x,p.y,i,i,i*2,i,i,-i*2):l3({cx:t,cy:r,innerRadius:n,outerRadius:a,startAngle:s,endAngle:u});var A=sr(n3||(n3=ul(["M ",",",` + A`,",",",0,0,",",",",",` + A`,",",",0,",",",",",",",` + A`,",",",0,0,",",",",",` + `])),p.x,p.y,i,i,+(c<0),f.x,f.y,a,a,+(x>180),+(c<0),b.x,b.y,i,i,+(c<0),m.x,m.y);if(n>0){var k=fh({cx:t,cy:r,radius:n,angle:s,sign:c,isExternal:!0,cornerRadius:i,cornerIsExternal:l}),_=k.circleTangency,M=k.lineTangency,E=k.theta,L=fh({cx:t,cy:r,radius:n,angle:u,sign:-c,isExternal:!0,cornerRadius:i,cornerIsExternal:l}),I=L.circleTangency,j=L.lineTangency,z=L.theta,X=l?Math.abs(s-u):Math.abs(s-u)-E-z;if(X<0&&i===0)return"".concat(A,"L").concat(t,",").concat(r,"Z");A+=sr(a3||(a3=ul(["L",",",` + A`,",",",0,0,",",",",",` + A`,",",",0,",",",",",",",` + A`,",",",0,0,",",",",","Z"])),j.x,j.y,i,i,+(c<0),I.x,I.y,n,n,+(X>180),+(c>0),_.x,_.y,i,i,+(c<0),M.x,M.y)}else A+=sr(i3||(i3=ul(["L",",","Z"])),t,r);return A},S$={cx:0,cy:0,innerRadius:0,outerRadius:0,startAngle:0,endAngle:0,cornerRadius:0,forceCornerRadius:!1,cornerIsExternal:!1},s3=e=>{var t=Dt(e,S$),r=t.cx,n=t.cy,a=t.innerRadius,i=t.outerRadius,o=t.cornerRadius,l=t.forceCornerRadius,s=t.cornerIsExternal,u=t.startAngle,c=t.endAngle,d=t.className;if(i0&&Math.abs(u-c)<360?h=w$({cx:r,cy:n,innerRadius:a,outerRadius:i,cornerRadius:Math.min(v,p/2),forceCornerRadius:l,cornerIsExternal:s,startAngle:u,endAngle:c}):h=l3({cx:r,cy:n,innerRadius:a,outerRadius:i,startAngle:u,endAngle:c}),o3.createElement("path",F1({},Wt(t),{className:f,d:h}))};function u3(e,t,r){if(e==="horizontal")return[{x:t.x,y:r.top},{x:t.x,y:r.top+r.height}];if(e==="vertical")return[{x:r.left,y:t.y},{x:r.left+r.width,y:t.y}];if(Fm(t)){if(e==="centric"){var n=t.cx,a=t.cy,i=t.innerRadius,o=t.outerRadius,l=t.angle,s=Ht(n,a,i,l),u=Ht(n,a,o,l);return[{x:s.x,y:s.y},{x:u.x,y:u.y}]}return dh(t)}}function c3(e){return gv(e)?NaN:Number(e)}function ph(e){return e?(e=c3(e),e===1/0||e===-1/0?(e<0?-1:1)*Number.MAX_VALUE:e===e?e:0):e===0?e:0}function mh(e,t,r){r&&typeof r!="number"&&bd(e,t,r)&&(t=r=void 0),e=ph(e),t===void 0?(t=e,e=0):t=ph(t),r=r===void 0?ee.chartData,q1=V([on],e=>{var t=e.chartData!=null?e.chartData.length-1:0;return{chartData:e.chartData,computedData:e.computedData,dataEndIndex:t,dataStartIndex:0}}),Ld=(e,t,r,n)=>n?q1(e):on(e),d3=(e,t,r)=>r?q1(e):on(e),f3=V([Ld],e=>{var t=e.chartData,r=e.dataStartIndex,n=e.dataEndIndex;return t!=null?t.slice(r,n+1):[]}),Lae=V([q1],e=>{var t=e.chartData,r=e.dataStartIndex,n=e.dataEndIndex;return t!=null?t.slice(r,n+1):[]}),p3=V([on],e=>{var t=e.chartData,r=e.dataStartIndex,n=e.dataEndIndex;return t!=null?t.slice(r,n+1):[]});function Y1(e,t){return C$(e)||_$(e,t)||N$(e,t)||A$()}function A$(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function N$(e,t){if(e){if(typeof e=="string")return m3(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?m3(e,t):void 0}}function m3(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);re.length)&&(t=e.length);for(var r=0,n=Array(t);r{var t=Bd(e,2),r=t[0],n=t[1],a=r,i=n;return r>n&&(a=n,i=r),[a,i]},Z1=(e,t,r)=>{if(e.lte(0))return new Re.default(0);var n=G1(e.toNumber()),a=new Re.default(10).pow(n),i=e.div(a),o=n!==1?.05:.1,l=new Re.default(Math.ceil(i.div(o).toNumber())).add(r).mul(o),s=l.mul(a);return t?new Re.default(s.toNumber()):new Re.default(Math.ceil(s.toNumber()))},b3=(e,t,r)=>{var n;if(e.lte(0))return new Re.default(0);var a=[1,2,2.5,5],i=e.toNumber(),o=Math.floor(new Re.default(i).abs().log(10).toNumber()),l=new Re.default(10).pow(o),s=e.div(l).toNumber(),u=a.findIndex(p=>p>=s-1e-10);if(u===-1&&(l=l.mul(10),u=0),u+=r,u>=a.length){var c=Math.floor(u/a.length);u%=a.length,l=l.mul(new Re.default(10).pow(c))}var d=(n=a[u])!==null&&n!==void 0?n:1,f=new Re.default(d).mul(l);return t?f:new Re.default(Math.ceil(f.toNumber()))},M$=(e,t,r)=>{var n=new Re.default(1),a=new Re.default(e);if(!a.isint()&&r){var i=Math.abs(e);i<1?(n=new Re.default(10).pow(G1(e)-1),a=new Re.default(Math.floor(a.div(n).toNumber())).mul(n)):i>1&&(a=new Re.default(Math.floor(e)))}else e===0?a=new Re.default(Math.floor((t-1)/2)):r||(a=new Re.default(Math.floor(e)));for(var o=Math.floor((t-1)/2),l=[],s=0;s4&&arguments[4]!==void 0?arguments[4]:0,o=arguments.length>5&&arguments[5]!==void 0?arguments[5]:Z1;if(!Number.isFinite((r-t)/(n-1)))return{step:new Re.default(0),tickMin:new Re.default(0),tickMax:new Re.default(0)};var l=o(new Re.default(r).sub(t).div(n-1),a,i),s;t<=0&&r>=0?s=new Re.default(0):(s=new Re.default(t).add(r).div(2),s=s.sub(new Re.default(s).mod(l)));var u=Math.ceil(s.sub(t).div(l).toNumber()),c=Math.ceil(new Re.default(r).sub(s).div(l).toNumber()),d=u+c+1;return d>n?w3(t,r,n,a,i+1,o):(d0?c+(n-d):c,u=r>0?u:u+(n-d)),{step:l,tickMin:s.sub(new Re.default(u).mul(l)),tickMax:s.add(new Re.default(c).mul(l))})};var gh=function(t){var r=Bd(t,2),n=r[0],a=r[1],i=arguments.length>1&&arguments[1]!==void 0?arguments[1]:6,o=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!0,l=arguments.length>3&&arguments[3]!==void 0?arguments[3]:"auto",s=Math.max(i,2),u=x3([n,a]),c=Bd(u,2),d=c[0],f=c[1];if(d===-1/0||f===1/0){var p=f===1/0?[d,...Array(i-1).fill(1/0)]:[...Array(i-1).fill(-1/0),f];return n>a?p.reverse():p}if(d===f)return M$(d,i,o);var v=l==="snap125"?b3:Z1,h=w3(d,f,s,o,0,v),b=h.step,m=h.tickMin,g=h.tickMax,x=X1(m,g.add(new Re.default(.1).mul(b)),b);return n>a?x.reverse():x},yh=function(t,r){var n=Bd(t,2),a=n[0],i=n[1],o=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!0,l=arguments.length>3&&arguments[3]!==void 0?arguments[3]:"auto",s=x3([a,i]),u=Bd(s,2),c=u[0],d=u[1];if(c===-1/0||d===1/0)return[a,i];if(c===d)return[c];var f=l==="snap125"?b3:Z1,p=Math.max(r,2),v=f(new Re.default(d).sub(c).div(p-1),o,0),h=[...X1(new Re.default(c),new Re.default(d),v),d];if(o===!1){h=h.map(m=>Math.round(m));var b=h.length-1;b>0&&h[b]===h[b-1]&&(h=h.slice(0,b))}return a>i?h.reverse():h};var S3=e=>e.rootProps.barCategoryGap;var xu=e=>e.rootProps.stackOffset,xh=e=>e.rootProps.reverseStackOrder,bu=e=>e.options.chartName,bh=e=>e.rootProps.syncId,Q1=e=>e.rootProps.syncMethod,wh=e=>e.options.eventEmitter,A3=e=>e.rootProps.baseValue;var ct={grid:-100,barBackground:-50,area:100,cursorRectangle:200,bar:300,line:400,axis:500,scatter:600,activeBar:1e3,cursorLine:1100,activeDot:1200,label:2e3};var vo={allowDecimals:!1,allowDuplicatedCategory:!0,allowDataOverflow:!1,angle:0,angleAxisId:0,axisLine:!0,axisLineType:"polygon",cx:0,cy:0,hide:!1,includeHidden:!1,label:!1,niceTicks:"auto",orientation:"outer",reversed:!1,scale:"auto",tick:!0,tickLine:!0,tickSize:8,type:"auto",zIndex:ct.axis};var fa={allowDataOverflow:!1,allowDecimals:!1,allowDuplicatedCategory:!0,angle:0,axisLine:!0,includeHidden:!1,hide:!1,niceTicks:"auto",label:!1,orientation:"right",radiusAxisId:0,reversed:!1,scale:"auto",stroke:"#ccc",tick:!0,tickCount:5,tickLine:!0,type:"auto",zIndex:ct.axis};var cl=(e,t)=>{if(!(!e||!t))return e!=null&&e.reversed?[t[1],t[0]]:t};function dl(e,t,r){if(r!=="auto")return r;if(e!=null)return an(e,t)?"category":"number"}function N3(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function Sh(e){for(var t=1;t{if(t!=null)return e.polarAxis.angleAxis[t]},Ah=V([I$,I1],(e,t)=>{var r;if(e!=null)return e;var n=(r=dl(t,"angleAxis",_3.type))!==null&&r!==void 0?r:"category";return Sh(Sh({},_3),{},{type:n})}),z$=(e,t)=>e.polarAxis.radiusAxis[t],Nh=V([z$,I1],(e,t)=>{var r;if(e!=null)return e;var n=(r=dl(t,"radiusAxis",C3.type))!==null&&r!==void 0?r:"category";return Sh(Sh({},C3),{},{type:n})}),_h=e=>e.polarOptions,J1=V([Dr,Pr,Ct],QT),E3=V([_h,J1],(e,t)=>{if(e!=null)return Vn(e.innerRadius,t,0)}),k3=V([_h,J1],(e,t)=>{if(e!=null)return Vn(e.outerRadius,t,t*.8)}),L$=e=>{if(e==null)return[0,0];var t=e.startAngle,r=e.endAngle;return[t,r]},R1=V([_h],L$),pie=V([Ah,R1],cl),e2=V([J1,E3,k3],(e,t,r)=>{if(!(e==null||t==null||r==null))return[t,r]}),mie=V([Nh,e2],cl),Ch=V([ot,_h,E3,k3,Dr,Pr],(e,t,r,n,a,i)=>{if(!(e!=="centric"&&e!=="radial"||t==null||r==null||n==null)){var o=t.cx,l=t.cy,s=t.startAngle,u=t.endAngle;return{cx:Vn(o,a,a/2),cy:Vn(l,i,i/2),innerRadius:r,outerRadius:n,startAngle:s,endAngle:u,clockWise:!1}}});var dr=(e,t)=>t;var Ud=(e,t,r)=>r;function wu(e){return e?.id}function Eh(e,t,r){var n=t.chartData,a=n===void 0?[]:n,i=r.allowDuplicatedCategory,o=r.dataKey,l=new Map;return e.forEach(s=>{var u,c=(u=s.data)!==null&&u!==void 0?u:a;if(!(c==null||c.length===0)){var d=wu(s);c.forEach((f,p)=>{var v=o==null||i?p:String(_t(f,o,null)),h=_t(f,s.dataKey,0),b;l.has(v)?b=l.get(v):b={},Object.assign(b,{[d]:h}),l.set(v,b)})}}),Array.from(l.values())}function $d(e){return"stackId"in e&&e.stackId!=null&&e.dataKey!=null}var fl=(e,t)=>e===t?!0:e==null||t==null?!1:e[0]===t[0]&&e[1]===t[1];function Su(e,t){return Array.isArray(e)&&Array.isArray(t)&&e.length===0&&t.length===0?!0:e===t}function O3(e,t){if(e.length===t.length){for(var r=0;r{var t=ot(e);return t==="horizontal"?"xAxis":t==="vertical"?"yAxis":t==="centric"?"angleAxis":"radiusAxis"};var ho=e=>e.tooltip.settings.axisId;function Hd(e){if(e!=null){var t=e.ticks,r=e.bandwidth,n=e.range(),a=[Math.min(...n),Math.max(...n)];return{domain:()=>e.domain(),range:(function(i){function o(){return i.apply(this,arguments)}return o.toString=function(){return i.toString()},o})(()=>a),rangeMin:()=>a[0],rangeMax:()=>a[1],isInRange(i){var o=a[0],l=a[1];return o<=l?i>=o&&i<=l:i>=l&&i<=o},bandwidth:r?()=>r.call(e):void 0,ticks:t?i=>t.call(e,i):void 0,map:(i,o)=>{var l=e(i);if(l!=null){if(e.bandwidth&&o!==null&&o!==void 0&&o.position){var s=e.bandwidth();switch(o.position){case"middle":l+=s/2;break;case"end":l+=s;break;default:break}}return l}}}}}var T3=(e,t)=>{if(t!=null)switch(e){case"linear":{if(!_n(t)){for(var r,n,a=0;an)&&(n=i))}return r!==void 0&&n!==void 0?[r,n]:void 0}return t}default:return t}};var uf={};Cj(uf,{scaleBand:()=>Yd,scaleDiverging:()=>s0,scaleDivergingLog:()=>P2,scaleDivergingPow:()=>u0,scaleDivergingSqrt:()=>J4,scaleDivergingSymlog:()=>j2,scaleIdentity:()=>Wh,scaleImplicit:()=>jh,scaleLinear:()=>Yh,scaleLog:()=>Kh,scaleOrdinal:()=>_u,scalePoint:()=>L3,scalePow:()=>rf,scaleQuantile:()=>Zh,scaleQuantize:()=>Qh,scaleRadial:()=>Xh,scaleSequential:()=>a0,scaleSequentialLog:()=>M2,scaleSequentialPow:()=>i0,scaleSequentialQuantile:()=>o0,scaleSequentialSqrt:()=>Q4,scaleSequentialSymlog:()=>D2,scaleSqrt:()=>x4,scaleSymlog:()=>Gh,scaleThreshold:()=>Jh,scaleTime:()=>O2,scaleUtc:()=>T2,tickFormat:()=>Qd});function qr(e,t){return e==null||t==null?NaN:et?1:e>=t?0:NaN}function t2(e,t){return e==null||t==null?NaN:te?1:t>=e?0:NaN}function pl(e){let t,r,n;e.length!==2?(t=qr,r=(l,s)=>qr(e(l),s),n=(l,s)=>e(l)-s):(t=e===qr||e===t2?e:B$,r=e,n=e);function a(l,s,u=0,c=l.length){if(u>>1;r(l[d],s)<0?u=d+1:c=d}while(u>>1;r(l[d],s)<=0?u=d+1:c=d}while(uu&&n(l[d-1],s)>-n(l[d],s)?d-1:d}return{left:a,center:o,right:i}}function B$(){return 0}function Vd(e){return e===null?NaN:+e}function*M3(e,t){if(t===void 0)for(let r of e)r!=null&&(r=+r)>=r&&(yield r);else{let r=-1;for(let n of e)(n=t(n,++r,e))!=null&&(n=+n)>=n&&(yield n)}}var D3=pl(qr),P3=D3.right,U$=D3.left,$$=pl(Vd).center,pa=P3;var Au=class extends Map{constructor(t,r=F$){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:r}}),t!=null)for(let[n,a]of t)this.set(n,a)}get(t){return super.get(j3(this,t))}has(t){return super.has(j3(this,t))}set(t,r){return super.set(H$(this,t),r)}delete(t){return super.delete(V$(this,t))}};function j3({_intern:e,_key:t},r){let n=t(r);return e.has(n)?e.get(n):r}function H$({_intern:e,_key:t},r){let n=t(r);return e.has(n)?e.get(n):(e.set(n,r),r)}function V$({_intern:e,_key:t},r){let n=t(r);return e.has(n)&&(r=e.get(n),e.delete(n)),r}function F$(e){return e!==null&&typeof e=="object"?e.valueOf():e}function I3(e=qr){if(e===qr)return r2;if(typeof e!="function")throw new TypeError("compare is not a function");return(t,r)=>{let n=e(t,r);return n||n===0?n:(e(r,r)===0)-(e(t,t)===0)}}function r2(e,t){return(e==null||!(e>=e))-(t==null||!(t>=t))||(et?1:0)}var q$=Math.sqrt(50),Y$=Math.sqrt(10),W$=Math.sqrt(2);function kh(e,t,r){let n=(t-e)/Math.max(0,r),a=Math.floor(Math.log10(n)),i=n/Math.pow(10,a),o=i>=q$?10:i>=Y$?5:i>=W$?2:1,l,s,u;return a<0?(u=Math.pow(10,-a)/o,l=Math.round(e*u),s=Math.round(t*u),l/ut&&--s,u=-u):(u=Math.pow(10,a)*o,l=Math.round(e/u),s=Math.round(t/u),l*ut&&--s),s0))return[];if(e===t)return[e];let n=t=a))return[];let l=i-a+1,s=new Array(l);if(n)if(o<0)for(let u=0;u=n)&&(r=n);else{let n=-1;for(let a of e)(a=t(a,++n,e))!=null&&(r=a)&&(r=a)}return r}function Th(e,t){let r;if(t===void 0)for(let n of e)n!=null&&(r>n||r===void 0&&n>=n)&&(r=n);else{let n=-1;for(let a of e)(a=t(a,++n,e))!=null&&(r>a||r===void 0&&a>=a)&&(r=a)}return r}function Mh(e,t,r=0,n=1/0,a){if(t=Math.floor(t),r=Math.floor(Math.max(0,r)),n=Math.floor(Math.min(e.length-1,n)),!(r<=t&&t<=n))return e;for(a=a===void 0?r2:I3(a);n>r;){if(n-r>600){let s=n-r+1,u=t-r+1,c=Math.log(s),d=.5*Math.exp(2*c/3),f=.5*Math.sqrt(c*d*(s-d)/s)*(u-s/2<0?-1:1),p=Math.max(r,Math.floor(t-u*d/s+f)),v=Math.min(n,Math.floor(t+(s-u)*d/s+f));Mh(e,t,p,v,a)}let i=e[t],o=r,l=n;for(qd(e,r,t),a(e[n],i)>0&&qd(e,r,n);o0;)--l}a(e[r],i)===0?qd(e,r,l):(++l,qd(e,l,n)),l<=t&&(r=l+1),t<=l&&(n=l-1)}return e}function qd(e,t,r){let n=e[t];e[t]=e[r],e[r]=n}function Dh(e,t,r){if(e=Float64Array.from(M3(e,r)),!(!(n=e.length)||isNaN(t=+t))){if(t<=0||n<2)return Th(e);if(t>=1)return Oh(e);var n,a=(n-1)*t,i=Math.floor(a),o=Oh(Mh(e,i).subarray(0,i+1)),l=Th(e.subarray(i+1));return o+(l-o)*(a-i)}}function n2(e,t,r=Vd){if(!(!(n=e.length)||isNaN(t=+t))){if(t<=0||n<2)return+r(e[0],0,e);if(t>=1)return+r(e[n-1],n-1,e);var n,a=(n-1)*t,i=Math.floor(a),o=+r(e[i],i,e),l=+r(e[i+1],i+1,e);return o+(l-o)*(a-i)}}function Ph(e,t,r){e=+e,t=+t,r=(a=arguments.length)<2?(t=e,e=0,1):a<3?1:+r;for(var n=-1,a=Math.max(0,Math.ceil((t-e)/r))|0,i=new Array(a);++n>8&15|t>>4&240,t>>4&15|t&240,(t&15)<<4|t&15,1):r===8?zh(t>>24&255,t>>16&255,t>>8&255,(t&255)/255):r===4?zh(t>>12&15|t>>8&240,t>>8&15|t>>4&240,t>>4&15|t&240,((t&15)<<4|t&15)/255):null):(t=G$.exec(e))?new ln(t[1],t[2],t[3],1):(t=X$.exec(e))?new ln(t[1]*255/100,t[2]*255/100,t[3]*255/100,1):(t=Z$.exec(e))?zh(t[1],t[2],t[3],t[4]):(t=Q$.exec(e))?zh(t[1]*255/100,t[2]*255/100,t[3]*255/100,t[4]):(t=J$.exec(e))?q3(t[1],t[2]/100,t[3]/100,1):(t=R$.exec(e))?q3(t[1],t[2]/100,t[3]/100,t[4]):B3.hasOwnProperty(e)?H3(B3[e]):e==="transparent"?new ln(NaN,NaN,NaN,0):null}function H3(e){return new ln(e>>16&255,e>>8&255,e&255,1)}function zh(e,t,r,n){return n<=0&&(e=t=r=NaN),new ln(e,t,r,n)}function rH(e){return e instanceof Gd||(e=go(e)),e?(e=e.rgb(),new ln(e.r,e.g,e.b,e.opacity)):new ln}function Eu(e,t,r,n){return arguments.length===1?rH(e):new ln(e,t,r,n??1)}function ln(e,t,r,n){this.r=+e,this.g=+t,this.b=+r,this.opacity=+n}Ih(ln,Eu,a2(Gd,{brighter(e){return e=e==null?Bh:Math.pow(Bh,e),new ln(this.r*e,this.g*e,this.b*e,this.opacity)},darker(e){return e=e==null?Wd:Math.pow(Wd,e),new ln(this.r*e,this.g*e,this.b*e,this.opacity)},rgb(){return this},clamp(){return new ln(hl(this.r),hl(this.g),hl(this.b),Uh(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:V3,formatHex:V3,formatHex8:nH,formatRgb:F3,toString:F3}));function V3(){return`#${vl(this.r)}${vl(this.g)}${vl(this.b)}`}function nH(){return`#${vl(this.r)}${vl(this.g)}${vl(this.b)}${vl((isNaN(this.opacity)?1:this.opacity)*255)}`}function F3(){let e=Uh(this.opacity);return`${e===1?"rgb(":"rgba("}${hl(this.r)}, ${hl(this.g)}, ${hl(this.b)}${e===1?")":`, ${e})`}`}function Uh(e){return isNaN(e)?1:Math.max(0,Math.min(1,e))}function hl(e){return Math.max(0,Math.min(255,Math.round(e)||0))}function vl(e){return e=hl(e),(e<16?"0":"")+e.toString(16)}function q3(e,t,r,n){return n<=0?e=t=r=NaN:r<=0||r>=1?e=t=NaN:t<=0&&(e=NaN),new ma(e,t,r,n)}function W3(e){if(e instanceof ma)return new ma(e.h,e.s,e.l,e.opacity);if(e instanceof Gd||(e=go(e)),!e)return new ma;if(e instanceof ma)return e;e=e.rgb();var t=e.r/255,r=e.g/255,n=e.b/255,a=Math.min(t,r,n),i=Math.max(t,r,n),o=NaN,l=i-a,s=(i+a)/2;return l?(t===i?o=(r-n)/l+(r0&&s<1?0:o,new ma(o,l,s,e.opacity)}function K3(e,t,r,n){return arguments.length===1?W3(e):new ma(e,t,r,n??1)}function ma(e,t,r,n){this.h=+e,this.s=+t,this.l=+r,this.opacity=+n}Ih(ma,K3,a2(Gd,{brighter(e){return e=e==null?Bh:Math.pow(Bh,e),new ma(this.h,this.s,this.l*e,this.opacity)},darker(e){return e=e==null?Wd:Math.pow(Wd,e),new ma(this.h,this.s,this.l*e,this.opacity)},rgb(){var e=this.h%360+(this.h<0)*360,t=isNaN(e)||isNaN(this.s)?0:this.s,r=this.l,n=r+(r<.5?r:1-r)*t,a=2*r-n;return new ln(i2(e>=240?e-240:e+120,a,n),i2(e,a,n),i2(e<120?e+240:e-120,a,n),this.opacity)},clamp(){return new ma(Y3(this.h),Lh(this.s),Lh(this.l),Uh(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){let e=Uh(this.opacity);return`${e===1?"hsl(":"hsla("}${Y3(this.h)}, ${Lh(this.s)*100}%, ${Lh(this.l)*100}%${e===1?")":`, ${e})`}`}}));function Y3(e){return e=(e||0)%360,e<0?e+360:e}function Lh(e){return Math.max(0,Math.min(1,e||0))}function i2(e,t,r){return(e<60?t+(r-t)*e/60:e<180?r:e<240?t+(r-t)*(240-e)/60:t)*255}function o2(e,t,r,n,a){var i=e*e,o=i*e;return((1-3*e+3*i-o)*t+(4-6*i+3*o)*r+(1+3*e+3*i-3*o)*n+o*a)/6}function G3(e){var t=e.length-1;return function(r){var n=r<=0?r=0:r>=1?(r=1,t-1):Math.floor(r*t),a=e[n],i=e[n+1],o=n>0?e[n-1]:2*a-i,l=n()=>e;function aH(e,t){return function(r){return e+r*t}}function iH(e,t,r){return e=Math.pow(e,r),t=Math.pow(t,r)-e,r=1/r,function(n){return Math.pow(e+n*t,r)}}function Z3(e){return(e=+e)==1?$h:function(t,r){return r-t?iH(t,r,e):Xd(isNaN(t)?r:t)}}function $h(e,t){var r=t-e;return r?aH(e,r):Xd(isNaN(e)?t:e)}var l2=(function e(t){var r=Z3(t);function n(a,i){var o=r((a=Eu(a)).r,(i=Eu(i)).r),l=r(a.g,i.g),s=r(a.b,i.b),u=$h(a.opacity,i.opacity);return function(c){return a.r=o(c),a.g=l(c),a.b=s(c),a.opacity=u(c),a+""}}return n.gamma=e,n})(1);function Q3(e){return function(t){var r=t.length,n=new Array(r),a=new Array(r),i=new Array(r),o,l;for(o=0;or&&(i=t.slice(r,i),l[o]?l[o]+=i:l[++o]=i),(n=n[0])===(a=a[0])?l[o]?l[o]+=a:l[++o]=a:(l[++o]=null,s.push({i:o,x:yo(n,a)})),r=s2.lastIndex;return rt&&(r=e,e=t,t=r),function(n){return Math.max(e,Math.min(t,n))}}function uH(e,t,r){var n=e[0],a=e[1],i=t[0],o=t[1];return a2?cH:uH,s=u=null,d}function d(f){return f==null||isNaN(f=+f)?i:(s||(s=l(e.map(n),t,r)))(n(o(f)))}return d.invert=function(f){return o(a((u||(u=l(t,e.map(n),yo)))(f)))},d.domain=function(f){return arguments.length?(e=Array.from(f,xo),c()):e.slice()},d.range=function(f){return arguments.length?(t=Array.from(f),c()):t.slice()},d.rangeRound=function(f){return t=Array.from(f),r=gl,c()},d.clamp=function(f){return arguments.length?(o=f?!0:fr,c()):o!==fr},d.interpolate=function(f){return arguments.length?(r=f,c()):r},d.unknown=function(f){return arguments.length?(i=f,d):i},function(f,p){return n=f,a=p,c()}}function xl(){return yl()(fr,fr)}function i4(e){return Math.abs(e=Math.round(e))>=1e21?e.toLocaleString("en").replace(/,/g,""):e.toString(10)}function bl(e,t){if(!isFinite(e)||e===0)return null;var r=(e=t?e.toExponential(t-1):e.toExponential()).indexOf("e"),n=e.slice(0,r);return[n.length>1?n[0]+n.slice(2):n,+e.slice(r+1)]}function Ma(e){return e=bl(Math.abs(e)),e?e[1]:NaN}function o4(e,t){return function(r,n){for(var a=r.length,i=[],o=0,l=e[0],s=0;a>0&&l>0&&(s+l+1>n&&(l=Math.max(1,n-s)),i.push(r.substring(a-=l,a+l)),!((s+=l+1)>n));)l=e[o=(o+1)%e.length];return i.reverse().join(t)}}function l4(e){return function(t){return t.replace(/[0-9]/g,function(r){return e[+r]})}}var dH=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Da(e){if(!(t=dH.exec(e)))throw new Error("invalid format: "+e);var t;return new Vh({fill:t[1],align:t[2],sign:t[3],symbol:t[4],zero:t[5],width:t[6],comma:t[7],precision:t[8]&&t[8].slice(1),trim:t[9],type:t[10]})}Da.prototype=Vh.prototype;function Vh(e){this.fill=e.fill===void 0?" ":e.fill+"",this.align=e.align===void 0?">":e.align+"",this.sign=e.sign===void 0?"-":e.sign+"",this.symbol=e.symbol===void 0?"":e.symbol+"",this.zero=!!e.zero,this.width=e.width===void 0?void 0:+e.width,this.comma=!!e.comma,this.precision=e.precision===void 0?void 0:+e.precision,this.trim=!!e.trim,this.type=e.type===void 0?"":e.type+""}Vh.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function s4(e){e:for(var t=e.length,r=1,n=-1,a;r0&&(n=0);break}return n>0?e.slice(0,n)+e.slice(a+1):e}var Zd;function u4(e,t){var r=bl(e,t);if(!r)return Zd=void 0,e.toPrecision(t);var n=r[0],a=r[1],i=a-(Zd=Math.max(-8,Math.min(8,Math.floor(a/3)))*3)+1,o=n.length;return i===o?n:i>o?n+new Array(i-o+1).join("0"):i>0?n.slice(0,i)+"."+n.slice(i):"0."+new Array(1-i).join("0")+bl(e,Math.max(0,t+i-1))[0]}function f2(e,t){var r=bl(e,t);if(!r)return e+"";var n=r[0],a=r[1];return a<0?"0."+new Array(-a).join("0")+n:n.length>a+1?n.slice(0,a+1)+"."+n.slice(a+1):n+new Array(a-n.length+2).join("0")}var p2={"%":(e,t)=>(e*100).toFixed(t),b:e=>Math.round(e).toString(2),c:e=>e+"",d:i4,e:(e,t)=>e.toExponential(t),f:(e,t)=>e.toFixed(t),g:(e,t)=>e.toPrecision(t),o:e=>Math.round(e).toString(8),p:(e,t)=>f2(e*100,t),r:f2,s:u4,X:e=>Math.round(e).toString(16).toUpperCase(),x:e=>Math.round(e).toString(16)};function m2(e){return e}var c4=Array.prototype.map,d4=["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];function f4(e){var t=e.grouping===void 0||e.thousands===void 0?m2:o4(c4.call(e.grouping,Number),e.thousands+""),r=e.currency===void 0?"":e.currency[0]+"",n=e.currency===void 0?"":e.currency[1]+"",a=e.decimal===void 0?".":e.decimal+"",i=e.numerals===void 0?m2:l4(c4.call(e.numerals,String)),o=e.percent===void 0?"%":e.percent+"",l=e.minus===void 0?"\u2212":e.minus+"",s=e.nan===void 0?"NaN":e.nan+"";function u(d,f){d=Da(d);var p=d.fill,v=d.align,h=d.sign,b=d.symbol,m=d.zero,g=d.width,x=d.comma,A=d.precision,k=d.trim,_=d.type;_==="n"?(x=!0,_="g"):p2[_]||(A===void 0&&(A=12),k=!0,_="g"),(m||p==="0"&&v==="=")&&(m=!0,p="0",v="=");var M=(f&&f.prefix!==void 0?f.prefix:"")+(b==="$"?r:b==="#"&&/[boxX]/.test(_)?"0"+_.toLowerCase():""),E=(b==="$"?n:/[%p]/.test(_)?o:"")+(f&&f.suffix!==void 0?f.suffix:""),L=p2[_],I=/[defgprs%]/.test(_);A=A===void 0?6:/[gprs]/.test(_)?Math.max(1,Math.min(21,A)):Math.max(0,Math.min(20,A));function j(z){var X=M,R=E,Q,y,C;if(_==="c")R=L(z)+R,z="";else{z=+z;var O=z<0||1/z<0;if(z=isNaN(z)?s:L(Math.abs(z),A),k&&(z=s4(z)),O&&+z==0&&h!=="+"&&(O=!1),X=(O?h==="("?h:l:h==="-"||h==="("?"":h)+X,R=(_==="s"&&!isNaN(z)&&Zd!==void 0?d4[8+Zd/3]:"")+R+(O&&h==="("?")":""),I){for(Q=-1,y=z.length;++QC||C>57){R=(C===46?a+z.slice(Q+1):z.slice(Q))+R,z=z.slice(0,Q);break}}}x&&!m&&(z=t(z,1/0));var D=X.length+z.length+R.length,N=D>1)+X+z+R+N.slice(D);break;default:z=N+X+z+R;break}return i(z)}return j.toString=function(){return d+""},j}function c(d,f){var p=Math.max(-8,Math.min(8,Math.floor(Ma(f)/3)))*3,v=Math.pow(10,-p),h=u((d=Da(d),d.type="f",d),{suffix:d4[8+p/3]});return function(b){return h(v*b)}}return{format:u,formatPrefix:c}}var Fh,ku,qh;v2({thousands:",",grouping:[3],currency:["$",""]});function v2(e){return Fh=f4(e),ku=Fh.format,qh=Fh.formatPrefix,Fh}function h2(e){return Math.max(0,-Ma(Math.abs(e)))}function g2(e,t){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(Ma(t)/3)))*3-Ma(Math.abs(e)))}function y2(e,t){return e=Math.abs(e),t=Math.abs(t)-e,Math.max(0,Ma(t)-Ma(e))+1}function Qd(e,t,r,n){var a=Nu(e,t,r),i;switch(n=Da(n??",f"),n.type){case"s":{var o=Math.max(Math.abs(e),Math.abs(t));return n.precision==null&&!isNaN(i=g2(a,o))&&(n.precision=i),qh(n,o)}case"":case"e":case"g":case"p":case"r":{n.precision==null&&!isNaN(i=y2(a,Math.max(Math.abs(e),Math.abs(t))))&&(n.precision=i-(n.type==="e"));break}case"f":case"%":{n.precision==null&&!isNaN(i=h2(a))&&(n.precision=i-(n.type==="%")*2);break}}return ku(n)}function Yr(e){var t=e.domain;return e.ticks=function(r){var n=t();return ml(n[0],n[n.length-1],r??10)},e.tickFormat=function(r,n){var a=t();return Qd(a[0],a[a.length-1],r??10,n)},e.nice=function(r){r==null&&(r=10);var n=t(),a=0,i=n.length-1,o=n[a],l=n[i],s,u,c=10;for(l0;){if(u=Fd(o,l,r),u===s)return n[a]=o,n[i]=l,t(n);if(u>0)o=Math.floor(o/u)*u,l=Math.ceil(l/u)*u;else if(u<0)o=Math.ceil(o*u)/u,l=Math.floor(l*u)/u;else break;s=u}return e},e}function Yh(){var e=xl();return e.copy=function(){return Ta(e,Yh())},Pt.apply(e,arguments),Yr(e)}function Wh(e){var t;function r(n){return n==null||isNaN(n=+n)?t:n}return r.invert=r,r.domain=r.range=function(n){return arguments.length?(e=Array.from(n,xo),r):e.slice()},r.unknown=function(n){return arguments.length?(t=n,r):t},r.copy=function(){return Wh(e).unknown(t)},e=arguments.length?Array.from(e,xo):[0,1],Yr(r)}function Jd(e,t){e=e.slice();var r=0,n=e.length-1,a=e[r],i=e[n],o;return iMath.pow(e,t)}function hH(e){return e===Math.E?Math.log:e===10&&Math.log10||e===2&&Math.log2||(e=Math.log(e),t=>Math.log(t)/e)}function v4(e){return(t,r)=>-e(-t,r)}function Rd(e){let t=e(p4,m4),r=t.domain,n=10,a,i;function o(){return a=hH(n),i=vH(n),r()[0]<0?(a=v4(a),i=v4(i),e(fH,pH)):e(p4,m4),t}return t.base=function(l){return arguments.length?(n=+l,o()):n},t.domain=function(l){return arguments.length?(r(l),o()):r()},t.ticks=l=>{let s=r(),u=s[0],c=s[s.length-1],d=c0){for(;f<=p;++f)for(v=1;vc)break;m.push(h)}}else for(;f<=p;++f)for(v=n-1;v>=1;--v)if(h=f>0?v/i(-f):v*i(f),!(hc)break;m.push(h)}m.length*2{if(l==null&&(l=10),s==null&&(s=n===10?"s":","),typeof s!="function"&&(!(n%1)&&(s=Da(s)).precision==null&&(s.trim=!0),s=ku(s)),l===1/0)return s;let u=Math.max(1,n*l/t.ticks().length);return c=>{let d=c/i(Math.round(a(c)));return d*nr(Jd(r(),{floor:l=>i(Math.floor(a(l))),ceil:l=>i(Math.ceil(a(l)))})),t}function Kh(){let e=Rd(yl()).domain([1,10]);return e.copy=()=>Ta(e,Kh()).base(e.base()),Pt.apply(e,arguments),e}function h4(e){return function(t){return Math.sign(t)*Math.log1p(Math.abs(t/e))}}function g4(e){return function(t){return Math.sign(t)*Math.expm1(Math.abs(t))*e}}function ef(e){var t=1,r=e(h4(t),g4(t));return r.constant=function(n){return arguments.length?e(h4(t=+n),g4(t)):t},Yr(r)}function Gh(){var e=ef(yl());return e.copy=function(){return Ta(e,Gh()).constant(e.constant())},Pt.apply(e,arguments)}function y4(e){return function(t){return t<0?-Math.pow(-t,e):Math.pow(t,e)}}function gH(e){return e<0?-Math.sqrt(-e):Math.sqrt(e)}function yH(e){return e<0?-e*e:e*e}function tf(e){var t=e(fr,fr),r=1;function n(){return r===1?e(fr,fr):r===.5?e(gH,yH):e(y4(r),y4(1/r))}return t.exponent=function(a){return arguments.length?(r=+a,n()):r},Yr(t)}function rf(){var e=tf(yl());return e.copy=function(){return Ta(e,rf()).exponent(e.exponent())},Pt.apply(e,arguments),e}function x4(){return rf.apply(null,arguments).exponent(.5)}function b4(e){return Math.sign(e)*e*e}function xH(e){return Math.sign(e)*Math.sqrt(Math.abs(e))}function Xh(){var e=xl(),t=[0,1],r=!1,n;function a(i){var o=xH(e(i));return isNaN(o)?n:r?Math.round(o):o}return a.invert=function(i){return e.invert(b4(i))},a.domain=function(i){return arguments.length?(e.domain(i),a):e.domain()},a.range=function(i){return arguments.length?(e.range((t=Array.from(i,xo)).map(b4)),a):t.slice()},a.rangeRound=function(i){return a.range(i).round(!0)},a.round=function(i){return arguments.length?(r=!!i,a):r},a.clamp=function(i){return arguments.length?(e.clamp(i),a):e.clamp()},a.unknown=function(i){return arguments.length?(n=i,a):n},a.copy=function(){return Xh(e.domain(),t).round(r).clamp(e.clamp()).unknown(n)},Pt.apply(a,arguments),Yr(a)}function Zh(){var e=[],t=[],r=[],n;function a(){var o=0,l=Math.max(1,t.length);for(r=new Array(l-1);++o0?r[l-1]:e[0],l=r?[n[r-1],t]:[n[u-1],n[u]]},o.unknown=function(s){return arguments.length&&(i=s),o},o.thresholds=function(){return n.slice()},o.copy=function(){return Qh().domain([e,t]).range(a).unknown(i)},Pt.apply(Yr(o),arguments)}function Jh(){var e=[.5],t=[0,1],r,n=1;function a(i){return i!=null&&i<=i?t[pa(e,i,0,n)]:r}return a.domain=function(i){return arguments.length?(e=Array.from(i),n=Math.min(e.length,t.length-1),a):e.slice()},a.range=function(i){return arguments.length?(t=Array.from(i),n=Math.min(e.length,t.length-1),a):t.slice()},a.invertExtent=function(i){var o=t.indexOf(i);return[e[o-1],e[o]]},a.unknown=function(i){return arguments.length?(r=i,a):r},a.copy=function(){return Jh().domain(e).range(t).unknown(r)},Pt.apply(a,arguments)}var x2=new Date,b2=new Date;function ht(e,t,r,n){function a(i){return e(i=arguments.length===0?new Date:new Date(+i)),i}return a.floor=i=>(e(i=new Date(+i)),i),a.ceil=i=>(e(i=new Date(i-1)),t(i,1),e(i),i),a.round=i=>{let o=a(i),l=a.ceil(i);return i-o(t(i=new Date(+i),o==null?1:Math.floor(o)),i),a.range=(i,o,l)=>{let s=[];if(i=a.ceil(i),l=l==null?1:Math.floor(l),!(i0))return s;let u;do s.push(u=new Date(+i)),t(i,l),e(i);while(uht(o=>{if(o>=o)for(;e(o),!i(o);)o.setTime(o-1)},(o,l)=>{if(o>=o)if(l<0)for(;++l<=0;)for(;t(o,-1),!i(o););else for(;--l>=0;)for(;t(o,1),!i(o););}),r&&(a.count=(i,o)=>(x2.setTime(+i),b2.setTime(+o),e(x2),e(b2),Math.floor(r(x2,b2))),a.every=i=>(i=Math.floor(i),!isFinite(i)||!(i>0)?null:i>1?a.filter(n?o=>n(o)%i===0:o=>a.count(0,o)%i===0):a)),a}var nf=ht(()=>{},(e,t)=>{e.setTime(+e+t)},(e,t)=>t-e);nf.every=e=>(e=Math.floor(e),!isFinite(e)||!(e>0)?null:e>1?ht(t=>{t.setTime(Math.floor(t/e)*e)},(t,r)=>{t.setTime(+t+r*e)},(t,r)=>(r-t)/e):nf);var Wse=nf.range;var Gn=ht(e=>{e.setTime(e-e.getMilliseconds())},(e,t)=>{e.setTime(+e+t*1e3)},(e,t)=>(t-e)/1e3,e=>e.getUTCSeconds()),w4=Gn.range;var Ou=ht(e=>{e.setTime(e-e.getMilliseconds()-e.getSeconds()*1e3)},(e,t)=>{e.setTime(+e+t*6e4)},(e,t)=>(t-e)/6e4,e=>e.getMinutes()),bH=Ou.range,Tu=ht(e=>{e.setUTCSeconds(0,0)},(e,t)=>{e.setTime(+e+t*6e4)},(e,t)=>(t-e)/6e4,e=>e.getUTCMinutes()),wH=Tu.range;var Mu=ht(e=>{e.setTime(e-e.getMilliseconds()-e.getSeconds()*1e3-e.getMinutes()*6e4)},(e,t)=>{e.setTime(+e+t*36e5)},(e,t)=>(t-e)/36e5,e=>e.getHours()),SH=Mu.range,Du=ht(e=>{e.setUTCMinutes(0,0,0)},(e,t)=>{e.setTime(+e+t*36e5)},(e,t)=>(t-e)/36e5,e=>e.getUTCHours()),AH=Du.range;var mi=ht(e=>e.setHours(0,0,0,0),(e,t)=>e.setDate(e.getDate()+t),(e,t)=>(t-e-(t.getTimezoneOffset()-e.getTimezoneOffset())*6e4)/864e5,e=>e.getDate()-1),NH=mi.range,Al=ht(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/864e5,e=>e.getUTCDate()-1),_H=Al.range,Rh=ht(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/864e5,e=>Math.floor(e/864e5)),CH=Rh.range;function Nl(e){return ht(t=>{t.setDate(t.getDate()-(t.getDay()+7-e)%7),t.setHours(0,0,0,0)},(t,r)=>{t.setDate(t.getDate()+r*7)},(t,r)=>(r-t-(r.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/6048e5)}var vi=Nl(0),Pu=Nl(1),A4=Nl(2),N4=Nl(3),bo=Nl(4),_4=Nl(5),C4=Nl(6),E4=vi.range,EH=Pu.range,kH=A4.range,OH=N4.range,TH=bo.range,MH=_4.range,DH=C4.range;function _l(e){return ht(t=>{t.setUTCDate(t.getUTCDate()-(t.getUTCDay()+7-e)%7),t.setUTCHours(0,0,0,0)},(t,r)=>{t.setUTCDate(t.getUTCDate()+r*7)},(t,r)=>(r-t)/6048e5)}var hi=_l(0),ju=_l(1),k4=_l(2),O4=_l(3),wo=_l(4),T4=_l(5),M4=_l(6),D4=hi.range,PH=ju.range,jH=k4.range,IH=O4.range,zH=wo.range,LH=T4.range,BH=M4.range;var Iu=ht(e=>{e.setDate(1),e.setHours(0,0,0,0)},(e,t)=>{e.setMonth(e.getMonth()+t)},(e,t)=>t.getMonth()-e.getMonth()+(t.getFullYear()-e.getFullYear())*12,e=>e.getMonth()),UH=Iu.range,zu=ht(e=>{e.setUTCDate(1),e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCMonth(e.getUTCMonth()+t)},(e,t)=>t.getUTCMonth()-e.getUTCMonth()+(t.getUTCFullYear()-e.getUTCFullYear())*12,e=>e.getUTCMonth()),$H=zu.range;var En=ht(e=>{e.setMonth(0,1),e.setHours(0,0,0,0)},(e,t)=>{e.setFullYear(e.getFullYear()+t)},(e,t)=>t.getFullYear()-e.getFullYear(),e=>e.getFullYear());En.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:ht(t=>{t.setFullYear(Math.floor(t.getFullYear()/e)*e),t.setMonth(0,1),t.setHours(0,0,0,0)},(t,r)=>{t.setFullYear(t.getFullYear()+r*e)});var HH=En.range,kn=ht(e=>{e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCFullYear(e.getUTCFullYear()+t)},(e,t)=>t.getUTCFullYear()-e.getUTCFullYear(),e=>e.getUTCFullYear());kn.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:ht(t=>{t.setUTCFullYear(Math.floor(t.getUTCFullYear()/e)*e),t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,r)=>{t.setUTCFullYear(t.getUTCFullYear()+r*e)});var VH=kn.range;function j4(e,t,r,n,a,i){let o=[[Gn,1,1e3],[Gn,5,5*1e3],[Gn,15,15*1e3],[Gn,30,30*1e3],[i,1,6e4],[i,5,5*6e4],[i,15,15*6e4],[i,30,30*6e4],[a,1,36e5],[a,3,3*36e5],[a,6,6*36e5],[a,12,12*36e5],[n,1,864e5],[n,2,2*864e5],[r,1,6048e5],[t,1,2592e6],[t,3,3*2592e6],[e,1,31536e6]];function l(u,c,d){let f=cb).right(o,f);if(p===o.length)return e.every(Nu(u/31536e6,c/31536e6,d));if(p===0)return nf.every(Math.max(Nu(u,c,d),1));let[v,h]=o[f/o[p-1][2]53)return null;"w"in H||(H.w=1),"Z"in H?(ee=C2(of(H.y,0,1)),ie=ee.getUTCDay(),ee=ie>4||ie===0?ju.ceil(ee):ju(ee),ee=Al.offset(ee,(H.V-1)*7),H.y=ee.getUTCFullYear(),H.m=ee.getUTCMonth(),H.d=ee.getUTCDate()+(H.w+6)%7):(ee=_2(of(H.y,0,1)),ie=ee.getDay(),ee=ie>4||ie===0?Pu.ceil(ee):Pu(ee),ee=mi.offset(ee,(H.V-1)*7),H.y=ee.getFullYear(),H.m=ee.getMonth(),H.d=ee.getDate()+(H.w+6)%7)}else("W"in H||"U"in H)&&("w"in H||(H.w="u"in H?H.u%7:"W"in H?1:0),ie="Z"in H?C2(of(H.y,0,1)).getUTCDay():_2(of(H.y,0,1)).getDay(),H.m=0,H.d="W"in H?(H.w+6)%7+H.W*7-(ie+5)%7:H.w+H.U*7-(ie+6)%7);return"Z"in H?(H.H+=H.Z/100|0,H.M+=H.Z%100,C2(H)):_2(H)}}function E(se,Ne,F,H){for(var q=0,ee=Ne.length,ie=F.length,K,xe;q=ie)return-1;if(K=Ne.charCodeAt(q++),K===37){if(K=Ne.charAt(q++),xe=k[K in I4?Ne.charAt(q++):K],!xe||(H=xe(se,F,H))<0)return-1}else if(K!=F.charCodeAt(H++))return-1}return H}function L(se,Ne,F){var H=u.exec(Ne.slice(F));return H?(se.p=c.get(H[0].toLowerCase()),F+H[0].length):-1}function I(se,Ne,F){var H=p.exec(Ne.slice(F));return H?(se.w=v.get(H[0].toLowerCase()),F+H[0].length):-1}function j(se,Ne,F){var H=d.exec(Ne.slice(F));return H?(se.w=f.get(H[0].toLowerCase()),F+H[0].length):-1}function z(se,Ne,F){var H=m.exec(Ne.slice(F));return H?(se.m=g.get(H[0].toLowerCase()),F+H[0].length):-1}function X(se,Ne,F){var H=h.exec(Ne.slice(F));return H?(se.m=b.get(H[0].toLowerCase()),F+H[0].length):-1}function R(se,Ne,F){return E(se,t,Ne,F)}function Q(se,Ne,F){return E(se,r,Ne,F)}function y(se,Ne,F){return E(se,n,Ne,F)}function C(se){return o[se.getDay()]}function O(se){return i[se.getDay()]}function D(se){return s[se.getMonth()]}function N(se){return l[se.getMonth()]}function T(se){return a[+(se.getHours()>=12)]}function $(se){return 1+~~(se.getMonth()/3)}function Y(se){return o[se.getUTCDay()]}function J(se){return i[se.getUTCDay()]}function fe(se){return s[se.getUTCMonth()]}function pe(se){return l[se.getUTCMonth()]}function ge(se){return a[+(se.getUTCHours()>=12)]}function ze(se){return 1+~~(se.getUTCMonth()/3)}return{format:function(se){var Ne=_(se+="",x);return Ne.toString=function(){return se},Ne},parse:function(se){var Ne=M(se+="",!1);return Ne.toString=function(){return se},Ne},utcFormat:function(se){var Ne=_(se+="",A);return Ne.toString=function(){return se},Ne},utcParse:function(se){var Ne=M(se+="",!0);return Ne.toString=function(){return se},Ne}}}var I4={"-":"",_:" ",0:"0"},gr=/^\s*\d+/,qH=/^%/,YH=/[\\^$*+?|[\]().{}]/g;function nt(e,t,r){var n=e<0?"-":"",a=(n?-e:e)+"",i=a.length;return n+(i[t.toLowerCase(),r]))}function KH(e,t,r){var n=gr.exec(t.slice(r,r+1));return n?(e.w=+n[0],r+n[0].length):-1}function GH(e,t,r){var n=gr.exec(t.slice(r,r+1));return n?(e.u=+n[0],r+n[0].length):-1}function XH(e,t,r){var n=gr.exec(t.slice(r,r+2));return n?(e.U=+n[0],r+n[0].length):-1}function ZH(e,t,r){var n=gr.exec(t.slice(r,r+2));return n?(e.V=+n[0],r+n[0].length):-1}function QH(e,t,r){var n=gr.exec(t.slice(r,r+2));return n?(e.W=+n[0],r+n[0].length):-1}function z4(e,t,r){var n=gr.exec(t.slice(r,r+4));return n?(e.y=+n[0],r+n[0].length):-1}function L4(e,t,r){var n=gr.exec(t.slice(r,r+2));return n?(e.y=+n[0]+(+n[0]>68?1900:2e3),r+n[0].length):-1}function JH(e,t,r){var n=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(t.slice(r,r+6));return n?(e.Z=n[1]?0:-(n[2]+(n[3]||"00")),r+n[0].length):-1}function RH(e,t,r){var n=gr.exec(t.slice(r,r+1));return n?(e.q=n[0]*3-3,r+n[0].length):-1}function eV(e,t,r){var n=gr.exec(t.slice(r,r+2));return n?(e.m=n[0]-1,r+n[0].length):-1}function B4(e,t,r){var n=gr.exec(t.slice(r,r+2));return n?(e.d=+n[0],r+n[0].length):-1}function tV(e,t,r){var n=gr.exec(t.slice(r,r+3));return n?(e.m=0,e.d=+n[0],r+n[0].length):-1}function U4(e,t,r){var n=gr.exec(t.slice(r,r+2));return n?(e.H=+n[0],r+n[0].length):-1}function rV(e,t,r){var n=gr.exec(t.slice(r,r+2));return n?(e.M=+n[0],r+n[0].length):-1}function nV(e,t,r){var n=gr.exec(t.slice(r,r+2));return n?(e.S=+n[0],r+n[0].length):-1}function aV(e,t,r){var n=gr.exec(t.slice(r,r+3));return n?(e.L=+n[0],r+n[0].length):-1}function iV(e,t,r){var n=gr.exec(t.slice(r,r+6));return n?(e.L=Math.floor(n[0]/1e3),r+n[0].length):-1}function oV(e,t,r){var n=qH.exec(t.slice(r,r+1));return n?r+n[0].length:-1}function lV(e,t,r){var n=gr.exec(t.slice(r));return n?(e.Q=+n[0],r+n[0].length):-1}function sV(e,t,r){var n=gr.exec(t.slice(r));return n?(e.s=+n[0],r+n[0].length):-1}function $4(e,t){return nt(e.getDate(),t,2)}function uV(e,t){return nt(e.getHours(),t,2)}function cV(e,t){return nt(e.getHours()%12||12,t,2)}function dV(e,t){return nt(1+mi.count(En(e),e),t,3)}function Y4(e,t){return nt(e.getMilliseconds(),t,3)}function fV(e,t){return Y4(e,t)+"000"}function pV(e,t){return nt(e.getMonth()+1,t,2)}function mV(e,t){return nt(e.getMinutes(),t,2)}function vV(e,t){return nt(e.getSeconds(),t,2)}function hV(e){var t=e.getDay();return t===0?7:t}function gV(e,t){return nt(vi.count(En(e)-1,e),t,2)}function W4(e){var t=e.getDay();return t>=4||t===0?bo(e):bo.ceil(e)}function yV(e,t){return e=W4(e),nt(bo.count(En(e),e)+(En(e).getDay()===4),t,2)}function xV(e){return e.getDay()}function bV(e,t){return nt(Pu.count(En(e)-1,e),t,2)}function wV(e,t){return nt(e.getFullYear()%100,t,2)}function SV(e,t){return e=W4(e),nt(e.getFullYear()%100,t,2)}function AV(e,t){return nt(e.getFullYear()%1e4,t,4)}function NV(e,t){var r=e.getDay();return e=r>=4||r===0?bo(e):bo.ceil(e),nt(e.getFullYear()%1e4,t,4)}function _V(e){var t=e.getTimezoneOffset();return(t>0?"-":(t*=-1,"+"))+nt(t/60|0,"0",2)+nt(t%60,"0",2)}function H4(e,t){return nt(e.getUTCDate(),t,2)}function CV(e,t){return nt(e.getUTCHours(),t,2)}function EV(e,t){return nt(e.getUTCHours()%12||12,t,2)}function kV(e,t){return nt(1+Al.count(kn(e),e),t,3)}function K4(e,t){return nt(e.getUTCMilliseconds(),t,3)}function OV(e,t){return K4(e,t)+"000"}function TV(e,t){return nt(e.getUTCMonth()+1,t,2)}function MV(e,t){return nt(e.getUTCMinutes(),t,2)}function DV(e,t){return nt(e.getUTCSeconds(),t,2)}function PV(e){var t=e.getUTCDay();return t===0?7:t}function jV(e,t){return nt(hi.count(kn(e)-1,e),t,2)}function G4(e){var t=e.getUTCDay();return t>=4||t===0?wo(e):wo.ceil(e)}function IV(e,t){return e=G4(e),nt(wo.count(kn(e),e)+(kn(e).getUTCDay()===4),t,2)}function zV(e){return e.getUTCDay()}function LV(e,t){return nt(ju.count(kn(e)-1,e),t,2)}function BV(e,t){return nt(e.getUTCFullYear()%100,t,2)}function UV(e,t){return e=G4(e),nt(e.getUTCFullYear()%100,t,2)}function $V(e,t){return nt(e.getUTCFullYear()%1e4,t,4)}function HV(e,t){var r=e.getUTCDay();return e=r>=4||r===0?wo(e):wo.ceil(e),nt(e.getUTCFullYear()%1e4,t,4)}function VV(){return"+0000"}function V4(){return"%"}function F4(e){return+e}function q4(e){return Math.floor(+e/1e3)}var Lu,e0,X4,t0,Z4;k2({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function k2(e){return Lu=E2(e),e0=Lu.format,X4=Lu.parse,t0=Lu.utcFormat,Z4=Lu.utcParse,Lu}function FV(e){return new Date(e)}function qV(e){return e instanceof Date?+e:+new Date(+e)}function r0(e,t,r,n,a,i,o,l,s,u){var c=xl(),d=c.invert,f=c.domain,p=u(".%L"),v=u(":%S"),h=u("%I:%M"),b=u("%I %p"),m=u("%a %d"),g=u("%b %d"),x=u("%B"),A=u("%Y");function k(_){return(s(_)<_?p:l(_)<_?v:o(_)<_?h:i(_)<_?b:n(_)<_?a(_)<_?m:g:r(_)<_?x:A)(_)}return c.invert=function(_){return new Date(d(_))},c.domain=function(_){return arguments.length?f(Array.from(_,qV)):f().map(FV)},c.ticks=function(_){var M=f();return e(M[0],M[M.length-1],_??10)},c.tickFormat=function(_,M){return M==null?k:u(M)},c.nice=function(_){var M=f();return(!_||typeof _.range!="function")&&(_=t(M[0],M[M.length-1],_??10)),_?f(Jd(M,_)):c},c.copy=function(){return Ta(c,r0(e,t,r,n,a,i,o,l,s,u))},c}function O2(){return Pt.apply(r0(A2,N2,En,Iu,vi,mi,Mu,Ou,Gn,e0).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)}function T2(){return Pt.apply(r0(w2,S2,kn,zu,hi,Al,Du,Tu,Gn,t0).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)}function n0(){var e=0,t=1,r,n,a,i,o=fr,l=!1,s;function u(d){return d==null||isNaN(d=+d)?s:o(a===0?.5:(d=(i(d)-r)*a,l?Math.max(0,Math.min(1,d)):d))}u.domain=function(d){return arguments.length?([e,t]=d,r=i(e=+e),n=i(t=+t),a=r===n?0:1/(n-r),u):[e,t]},u.clamp=function(d){return arguments.length?(l=!!d,u):l},u.interpolator=function(d){return arguments.length?(o=d,u):o};function c(d){return function(f){var p,v;return arguments.length?([p,v]=f,o=d(p,v),u):[o(0),o(1)]}}return u.range=c(Cn),u.rangeRound=c(gl),u.unknown=function(d){return arguments.length?(s=d,u):s},function(d){return i=d,r=d(e),n=d(t),a=r===n?0:1/(n-r),u}}function gi(e,t){return t.domain(e.domain()).interpolator(e.interpolator()).clamp(e.clamp()).unknown(e.unknown())}function a0(){var e=Yr(n0()(fr));return e.copy=function(){return gi(e,a0())},Kn.apply(e,arguments)}function M2(){var e=Rd(n0()).domain([1,10]);return e.copy=function(){return gi(e,M2()).base(e.base())},Kn.apply(e,arguments)}function D2(){var e=ef(n0());return e.copy=function(){return gi(e,D2()).constant(e.constant())},Kn.apply(e,arguments)}function i0(){var e=tf(n0());return e.copy=function(){return gi(e,i0()).exponent(e.exponent())},Kn.apply(e,arguments)}function Q4(){return i0.apply(null,arguments).exponent(.5)}function o0(){var e=[],t=fr;function r(n){if(n!=null&&!isNaN(n=+n))return t((pa(e,n,1)-1)/(e.length-1))}return r.domain=function(n){if(!arguments.length)return e.slice();e=[];for(let a of n)a!=null&&!isNaN(a=+a)&&e.push(a);return e.sort(qr),r},r.interpolator=function(n){return arguments.length?(t=n,r):t},r.range=function(){return e.map((n,a)=>t(a/(e.length-1)))},r.quantiles=function(n){return Array.from({length:n+1},(a,i)=>Dh(e,i/n))},r.copy=function(){return o0(t).domain(e)},Kn.apply(r,arguments)}function l0(){var e=0,t=.5,r=1,n=1,a,i,o,l,s,u=fr,c,d=!1,f;function p(h){return isNaN(h=+h)?f:(h=.5+((h=+c(h))-i)*(n*h{if(e!=null){var n=e.scale,a=e.type;if(n==="auto")return a==="category"&&r&&(r.indexOf("LineChart")>=0||r.indexOf("AreaChart")>=0||r.indexOf("ComposedChart")>=0&&!t)?"point":a==="category"?"band":"linear";if(typeof n=="string")return KV(n)?n:"point"}};function GV(e,t){for(var r=0,n=e.length,a=e[0]t)?r=i+1:n=i}return r}function d0(e,t){if(e){var r=t??e.domain(),n=r.map(i=>{var o;return(o=e(i))!==null&&o!==void 0?o:0}),a=e.range();if(!(r.length===0||a.length<2))return i=>{var o,l,s=GV(n,i);if(s<=0)return r[0];if(s>=r.length)return r[r.length-1];var u=(o=n[s-1])!==null&&o!==void 0?o:0,c=(l=n[s])!==null&&l!==void 0?l:0;return Math.abs(i-u)<=Math.abs(i-c)?r[s-1]:r[s]}}}function eM(e){if(e!=null)return"invert"in e&&typeof e.invert=="function"?e.invert.bind(e):d0(e,void 0)}function tM(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function f0(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);re.cartesianAxis.xAxis[t],ja=(e,t)=>{var r=z2(e,t);return r??Gt},Xt={allowDataOverflow:!1,allowDecimals:!0,allowDuplicatedCategory:!0,angle:0,dataKey:void 0,domain:I2,hide:!0,id:0,includeHidden:!1,interval:"preserveEnd",minTickGap:5,mirror:!1,name:void 0,orientation:"left",padding:{top:0,bottom:0},reversed:!1,scale:"auto",tick:!0,tickCount:5,tickFormatter:void 0,ticks:void 0,type:"number",unit:void 0,niceTicks:"auto",width:al},L2=(e,t)=>e.cartesianAxis.yAxis[t],Ia=(e,t)=>{var r=L2(e,t);return r??Xt},rF={domain:[0,"auto"],includeHidden:!1,reversed:!1,allowDataOverflow:!1,allowDuplicatedCategory:!1,dataKey:void 0,id:0,name:"",range:[64,64],scale:"auto",type:"number",unit:""},B2=(e,t)=>{var r=e.cartesianAxis.zAxis[t];return r??rF},jr=(e,t,r)=>{switch(t){case"xAxis":return ja(e,r);case"yAxis":return Ia(e,r);case"zAxis":return B2(e,r);case"angleAxis":return Ah(e,r);case"radiusAxis":return Nh(e,r);default:throw new Error("Unexpected axis type: ".concat(t))}},nF=(e,t,r)=>{switch(t){case"xAxis":return ja(e,r);case"yAxis":return Ia(e,r);default:throw new Error("Unexpected axis type: ".concat(t))}},ff=(e,t,r)=>{switch(t){case"xAxis":return ja(e,r);case"yAxis":return Ia(e,r);case"angleAxis":return Ah(e,r);case"radiusAxis":return Nh(e,r);default:throw new Error("Unexpected axis type: ".concat(t))}},U2=e=>e.graphicalItems.cartesianItems.some(t=>t.type==="bar")||e.graphicalItems.polarItems.some(t=>t.type==="radialBar");function $2(e,t){return r=>{switch(e){case"xAxis":return"xAxisId"in r&&r.xAxisId===t;case"yAxis":return"yAxisId"in r&&r.yAxisId===t;case"zAxis":return"zAxisId"in r&&r.zAxisId===t;case"angleAxis":return"angleAxisId"in r&&r.angleAxisId===t;case"radiusAxis":return"radiusAxisId"in r&&r.radiusAxisId===t;default:return!1}}}var H2=e=>e.graphicalItems.cartesianItems,aF=V([dr,Ud],$2),V2=(e,t,r)=>e.filter(r).filter(n=>t?.includeHidden===!0?!0:!n.hide),Uu=V([H2,jr,aF],V2,{memoizeOptions:{resultEqualityCheck:Su}}),iM=V([Uu],e=>e.filter(t=>t.type==="area"||t.type==="bar").filter($d)),F2=e=>e.filter(t=>!("stackId"in t)||t.stackId===void 0),iF=V([Uu],F2),q2=e=>e.map(t=>t.data).filter(Boolean).flat(1),oF=V([Uu],e=>e.some(t=>!t.data)),oM=V([Uu],q2,{memoizeOptions:{resultEqualityCheck:Su}}),Y2=(e,t)=>{var r=t.chartData,n=r===void 0?[]:r,a=t.dataStartIndex,i=t.dataEndIndex;return e.length>0?e:n.slice(a,i+1)},W2=V([oM,Ld],Y2),lF=(e,t,r)=>t?.dataKey!=null?e.map(n=>({value:_t(n,t.dataKey)})):r.length>0?r.map(n=>n.dataKey).flatMap(n=>e.map(a=>({value:_t(a,n)}))):e.map(n=>({value:n})),K2=(e,t,r,n,a,i)=>{var o=n.chartData,l=o===void 0?[]:o,s=n.dataStartIndex,u=n.dataEndIndex,c=lF(e,t,r);if(a&&t?.dataKey!=null&&i.length>0){var d=l.slice(s,u+1),f=d.map(p=>({value:_t(p,t.dataKey)})).filter(p=>p.value!=null);return[...f,...c]}return c},pf=V([W2,jr,Uu,Ld,oF,oM],K2);function Bu(e){if(Fr(e)||e instanceof Date){var t=Number(e);if(Ie(t))return t}}function nM(e){if(Array.isArray(e)){var t=[Bu(e[0]),Bu(e[1])];return _n(t)?t:void 0}var r=Bu(e);if(r!=null)return[r,r]}function va(e){return e.map(Bu).filter(cr)}function sF(e,t){var r=Bu(e),n=Bu(t);return r==null&&n==null?0:r==null?-1:n==null?1:r-n}var uF=V([pf],e=>e?.map(t=>t.value).sort(sF));function lM(e,t){switch(e){case"xAxis":return t.direction==="x";case"yAxis":return t.direction==="y";default:return!1}}function cF(e,t,r){if(!r)return[];if(!r.length)return[];var n;if(typeof t=="number"&&!Tr(t))n=t;else if(Array.isArray(t)){var a=va(t);a.length>0&&(n=Math.max(...a))}return n==null?[]:va(r.flatMap(i=>{var o=_t(e,i.dataKey),l,s;if(Array.isArray(o)){var u=aM(o,2);l=u[0],s=u[1]}else l=s=o;if(!(!Ie(l)||!Ie(s)))return[n-l,n+s]}))}var Zt=e=>{var t=Kt(e),r=ho(e);return ff(e,t,r)},yi=V([Zt],e=>e?.dataKey),dF=V([iM,Ld,Zt],Eh),G2=(e,t,r,n)=>{var a={},i=t.reduce((o,l)=>{if(l.stackId==null)return o;var s=o[l.stackId];return s==null&&(s=[]),s.push(l),o[l.stackId]=s,o},a);return Object.fromEntries(Object.entries(i).map(o=>{var l=aM(o,2),s=l[0],u=l[1],c=n?[...u].reverse():u,d=c.map(wu);return[s,{stackedData:xO(e,d,r),graphicalItems:c}]}))},X2=V([dF,iM,xu,xh],G2),Z2=(e,t,r,n)=>{var a=t.dataStartIndex,i=t.dataEndIndex;if(n==null&&r!=="zAxis")return wO(e,a,i)},fF=V([jr],e=>e.allowDataOverflow),p0=e=>{var t;if(e==null||!("domain"in e))return I2;if(e.domain!=null)return e.domain;if("ticks"in e&&e.ticks!=null){if(e.type==="number"){var r=va(e.ticks);return[Math.min(...r),Math.max(...r)]}if(e.type==="category")return e.ticks.map(String)}return(t=e?.domain)!==null&&t!==void 0?t:I2},sM=V([jr],p0),uM=V([sM,fF],vh),pF=V([X2,on,dr,uM],Z2,{memoizeOptions:{resultEqualityCheck:fl}}),m0=e=>e.errorBars,mF=(e,t,r)=>e.flatMap(n=>t[n.id]).filter(Boolean).filter(n=>lM(r,n)),df=function(){for(var t=arguments.length,r=new Array(t),n=0;n5&&arguments[5]!==void 0?arguments[5]:[],l,s;if(n.length>0&&n.forEach(u=>{var c,d=u.data!=null?[...u.data]:o,f=(c=a[u.id])===null||c===void 0?void 0:c.filter(p=>lM(i,p));d.forEach(p=>{var v,h=_t(p,(v=r.dataKey)!==null&&v!==void 0?v:u.dataKey),b=cF(p,h,f);if(b.length>=2){var m=Math.min(...b),g=Math.max(...b);(l==null||ms)&&(s=g)}var x=nM(h);x!=null&&(l=l==null?x[0]:Math.min(l,x[0]),s=s==null?x[1]:Math.max(s,x[1]))})}),r?.dataKey!=null&&n.length===0&&t.forEach(u=>{var c=nM(_t(u,r.dataKey));c!=null&&(l=l==null?c[0]:Math.min(l,c[0]),s=s==null?c[1]:Math.max(s,c[1]))}),Ie(l)&&Ie(s))return[l,s]},vF=V([W2,jr,iF,m0,dr,f3],Q2,{memoizeOptions:{resultEqualityCheck:fl}});function hF(e){var t=e.value;if(Fr(t)||t instanceof Date)return t}var gF=(e,t,r)=>{var n=e.map(hF).filter(a=>a!=null);return r&&(t.dataKey==null||t.allowDuplicatedCategory&&Ob(n))?mh(0,e.length):t.allowDuplicatedCategory?n:Array.from(new Set(n))},J2=e=>e.referenceElements.dots,Cl=(e,t,r)=>e.filter(n=>n.ifOverflow==="extendDomain").filter(n=>t==="xAxis"?n.xAxisId===r:n.yAxisId===r),yF=V([J2,dr,Ud],Cl),R2=e=>e.referenceElements.areas,xF=V([R2,dr,Ud],Cl),ew=e=>e.referenceElements.lines,bF=V([ew,dr,Ud],Cl),tw=(e,t)=>{if(e!=null){var r=va(e.map(n=>t==="xAxis"?n.x:n.y));if(r.length!==0)return[Math.min(...r),Math.max(...r)]}},wF=V(yF,dr,tw),rw=(e,t)=>{if(e!=null){var r=va(e.flatMap(n=>[t==="xAxis"?n.x1:n.y1,t==="xAxis"?n.x2:n.y2]));if(r.length!==0)return[Math.min(...r),Math.max(...r)]}},SF=V([xF,dr],rw);function AF(e){var t;if(e.x!=null)return va([e.x]);var r=(t=e.segment)===null||t===void 0?void 0:t.map(n=>n.x);return r==null||r.length===0?[]:va(r)}function NF(e){var t;if(e.y!=null)return va([e.y]);var r=(t=e.segment)===null||t===void 0?void 0:t.map(n=>n.y);return r==null||r.length===0?[]:va(r)}var nw=(e,t)=>{if(e!=null){var r=e.flatMap(n=>t==="xAxis"?AF(n):NF(n));if(r.length!==0)return[Math.min(...r),Math.max(...r)]}},_F=V([bF,dr],nw),CF=V(wF,_F,SF,(e,t,r)=>df(e,r,t)),aw=(e,t,r,n,a,i,o,l,s)=>{if(r!=null)return r;var u=o==="vertical"&&l==="xAxis"||o==="horizontal"&&l==="yAxis",c=u?df(n,i,a):df(i,a),d=h3(t,c,e.allowDataOverflow);return d??(e.allowDataOverflow&&c==null&&s!=null?s:d)},EF=e=>{if(!(e==null||e.type!=="number"||!("ticks"in e)||e.ticks==null)){var t=va(e.ticks);if(t.length!==0)return[Math.min(...t),Math.max(...t)]}},kF=V([jr],EF,{memoizeOptions:{resultEqualityCheck:fl}}),OF=V([jr,sM,uM,pF,vF,CF,ot,dr,kF],aw,{memoizeOptions:{resultEqualityCheck:fl}}),TF=[0,1],iw=(e,t,r,n,a,i,o)=>{if(!((e==null||r==null||r.length===0)&&o===void 0)){var l=e.dataKey,s=e.type,u=an(t,i);if(u&&l==null){var c;return mh(0,(c=r?.length)!==null&&c!==void 0?c:0)}return s==="category"?gF(n,e,u):a==="expand"&&!u?TF:o}},ow=V([jr,ot,W2,pf,xu,dr,OF],iw),$u=V([jr,U2,bu],c0),lw=(e,t,r)=>{var n=t.niceTicks;if(n!=="none"){var a=p0(t),i=Array.isArray(a)&&(a[0]==="auto"||a[1]==="auto");if((n==="snap125"||n==="adaptive")&&t!=null&&t.tickCount&&_n(e)){if(i)return gh(e,t.tickCount,t.allowDecimals,n);if(t.type==="number")return yh(e,t.tickCount,t.allowDecimals,n)}if(n==="auto"&&r==="linear"&&t!=null&&t.tickCount){if(i&&_n(e))return gh(e,t.tickCount,t.allowDecimals,"adaptive");if(t.type==="number"&&_n(e))return yh(e,t.tickCount,t.allowDecimals,"adaptive")}}},sw=V([ow,ff,$u],lw),uw=(e,t,r,n)=>{if(n!=="angleAxis"&&e?.type==="number"&&_n(t)&&Array.isArray(r)&&r.length>0){var a,i,o=t[0],l=(a=r[0])!==null&&a!==void 0?a:0,s=t[1],u=(i=r[r.length-1])!==null&&i!==void 0?i:0;return[Math.min(o,l),Math.max(s,u)]}return t},MF=V([jr,ow,sw,dr],uw),DF=V(pf,jr,(e,t)=>{if(!(!t||t.type!=="number")){var r=1/0,n=Array.from(va(e.map(d=>d.value))).sort((d,f)=>d-f),a=n[0],i=n[n.length-1];if(a==null||i==null)return 1/0;var o=i-a;if(o===0)return 1/0;for(var l=0;la,(e,t,r,n,a)=>{if(!Ie(e))return 0;var i=t==="vertical"?n.height:n.width;if(a==="gap")return e*i/2;if(a==="no-gap"){var o=Vn(r,e*i),l=e*i/2;return l-o-(l-o)/i*o}return 0}),PF=(e,t,r)=>{var n=ja(e,t);return n==null||typeof n.padding!="string"?0:cM(e,"xAxis",t,r,n.padding)},jF=(e,t,r)=>{var n=Ia(e,t);return n==null||typeof n.padding!="string"?0:cM(e,"yAxis",t,r,n.padding)},IF=V(ja,PF,(e,t)=>{var r,n;if(e==null)return{left:0,right:0};var a=e.padding;return typeof a=="string"?{left:t,right:t}:{left:((r=a.left)!==null&&r!==void 0?r:0)+t,right:((n=a.right)!==null&&n!==void 0?n:0)+t}}),zF=V(Ia,jF,(e,t)=>{var r,n;if(e==null)return{top:0,bottom:0};var a=e.padding;return typeof a=="string"?{top:t,bottom:t}:{top:((r=a.top)!==null&&r!==void 0?r:0)+t,bottom:((n=a.bottom)!==null&&n!==void 0?n:0)+t}}),cw=V([Ct,IF,il,du,(e,t,r)=>r],(e,t,r,n,a)=>{var i=n.padding;return a?[i.left,r.width-i.right]:[e.left+t.left,e.left+e.width-t.right]}),dw=V([Ct,ot,zF,il,du,(e,t,r)=>r],(e,t,r,n,a,i)=>{var o=a.padding;return i?[n.height-o.bottom,o.top]:t==="horizontal"?[e.top+e.height-r.bottom,e.top+r.top]:[e.top+r.top,e.top+e.height-r.bottom]}),Hu=(e,t,r,n)=>{var a;switch(t){case"xAxis":return cw(e,r,n);case"yAxis":return dw(e,r,n);case"zAxis":return(a=B2(e,r))===null||a===void 0?void 0:a.range;case"angleAxis":return R1(e);case"radiusAxis":return e2(e,r);default:return}},dM=V([jr,Hu],cl),LF=V([$u,MF],T3),fw=V([jr,$u,LF,dM],cf),pw=(e,t,r,n)=>{if(!(r==null||r.dataKey==null)){var a=r.type,i=r.scale,o=an(e,n);if(o&&(a==="number"||i!=="auto"))return t.map(l=>l.value)}},mw=V([ot,pf,ff,dr],pw),v0=V([fw],Hd),vde=V([fw],eM),hde=V([fw,uF],d0),gde=V([Uu,m0,dr],mF);function fM(e,t){return e.idt.id?1:0}var h0=(e,t)=>t,g0=(e,t,r)=>r,BF=V(uu,h0,g0,(e,t,r)=>e.filter(n=>n.orientation===t).filter(n=>n.mirror===r).sort(fM)),UF=V(cu,h0,g0,(e,t,r)=>e.filter(n=>n.orientation===t).filter(n=>n.mirror===r).sort(fM)),pM=(e,t)=>{var r=typeof t.height=="number"?t.height:Ed;return{width:e.width,height:r}},$F=(e,t)=>{var r=typeof t.width=="number"?t.width:al;return{width:r,height:e.height}},mM=V(Ct,ja,pM),HF=(e,t,r)=>{switch(t){case"top":return e.top;case"bottom":return r-e.bottom;default:return 0}},VF=(e,t,r)=>{switch(t){case"left":return e.left;case"right":return r-e.right;default:return 0}},FF=V(Pr,Ct,BF,h0,g0,(e,t,r,n,a)=>{var i={},o;return r.forEach(l=>{var s=pM(t,l);o==null&&(o=HF(t,n,e));var u=n==="top"&&!a||n==="bottom"&&a;i[l.id]=o-Number(u)*s.height,o+=(u?-1:1)*s.height}),i}),qF=V(Dr,Ct,UF,h0,g0,(e,t,r,n,a)=>{var i={},o;return r.forEach(l=>{var s=$F(t,l);o==null&&(o=VF(t,n,e));var u=n==="left"&&!a||n==="right"&&a;i[l.id]=o-Number(u)*s.width,o+=(u?-1:1)*s.width}),i}),YF=(e,t)=>{var r=ja(e,t);if(r!=null)return FF(e,r.orientation,r.mirror)},vM=V([Ct,ja,YF,(e,t)=>t],(e,t,r,n)=>{if(t!=null){var a=r?.[n];return a==null?{x:e.left,y:0}:{x:e.left,y:a}}}),WF=(e,t)=>{var r=Ia(e,t);if(r!=null)return qF(e,r.orientation,r.mirror)},hM=V([Ct,Ia,WF,(e,t)=>t],(e,t,r,n)=>{if(t!=null){var a=r?.[n];return a==null?{x:0,y:e.top}:{x:a,y:e.top}}}),gM=V(Ct,Ia,(e,t)=>{var r=typeof t.width=="number"?t.width:al;return{width:r,height:e.height}});var vw=(e,t,r,n)=>{if(r!=null){var a=r.allowDuplicatedCategory,i=r.type,o=r.dataKey,l=an(e,n),s=t.map(c=>c.value),u=s.filter(c=>c!=null);if(o&&l&&i==="category"&&a&&Ob(u))return s}},hw=V([ot,pf,jr,dr],vw),gw=V([ot,nF,$u,v0,hw,mw,Hu,sw,dr],(e,t,r,n,a,i,o,l,s)=>{if(t!=null){var u=an(e,s);return{angle:t.angle,interval:t.interval,minTickGap:t.minTickGap,orientation:t.orientation,tick:t.tick,tickCount:t.tickCount,tickFormatter:t.tickFormatter,ticks:t.ticks,type:t.type,unit:t.unit,axisType:s,categoricalDomain:i,duplicateDomain:a,isCategorical:u,niceTicks:l,range:o,realScaleType:r,scale:n}}}),KF=(e,t,r,n,a,i,o,l,s)=>{if(!(t==null||n==null)){var u=an(e,s),c=t.type,d=t.ticks,f=t.tickCount,p=r==="scaleBand"&&typeof n.bandwidth=="function"?n.bandwidth()/2:2,v=c==="category"&&n.bandwidth?n.bandwidth()/p:0;v=s==="angleAxis"&&i!=null&&i.length>=2?ur(i[0]-i[1])*2*v:v;var h=d||a;return h?h.map((b,m)=>{var g=o?o.indexOf(b):b,x=n.map(g);return Ie(x)?{index:m,coordinate:x+v,value:b,offset:v}:null}).filter(cr):u&&l?l.map((b,m)=>{var g=n.map(b);return Ie(g)?{coordinate:g+v,value:b,index:m,offset:v}:null}).filter(cr):n.ticks?n.ticks(f).map((b,m)=>{var g=n.map(b);return Ie(g)?{coordinate:g+v,value:b,index:m,offset:v}:null}).filter(cr):n.domain().map((b,m)=>{var g=n.map(b);return Ie(g)?{coordinate:g+v,value:o?o[b]:b,index:m,offset:v}:null}).filter(cr)}},y0=V([ot,ff,$u,v0,sw,Hu,hw,mw,dr],KF),GF=(e,t,r,n,a,i,o)=>{if(!(t==null||r==null||n==null||n[0]===n[1])){var l=an(e,o),s=t.tickCount,u=0;return u=o==="angleAxis"&&n?.length>=2?ur(n[0]-n[1])*2*u:u,l&&i?i.map((c,d)=>{var f=r.map(c);return Ie(f)?{coordinate:f+u,value:c,index:d,offset:u}:null}).filter(cr):r.ticks?r.ticks(s).map((c,d)=>{var f=r.map(c);return Ie(f)?{coordinate:f+u,value:c,index:d,offset:u}:null}).filter(cr):r.domain().map((c,d)=>{var f=r.map(c);return Ie(f)?{coordinate:f+u,value:a?a[c]:c,index:d,offset:u}:null}).filter(cr)}},yw=V([ot,ff,v0,Hu,hw,mw,dr],GF),xw=V(jr,v0,(e,t)=>{if(!(e==null||t==null))return f0(f0({},e),{},{scale:t})}),XF=V([jr,$u,ow,dM],cf),ZF=V([XF],Hd),yde=V((e,t,r)=>B2(e,r),ZF,(e,t)=>{if(!(e==null||t==null))return f0(f0({},e),{},{scale:t})}),yM=V([ot,uu,cu],(e,t,r)=>{switch(e){case"horizontal":return t.some(n=>n.reversed)?"right-to-left":"left-to-right";case"vertical":return r.some(n=>n.reversed)?"bottom-to-top":"top-to-bottom";case"centric":case"radial":return"left-to-right";default:return}}),QF=(e,t,r)=>{var n;return(n=e.renderedTicks[t])===null||n===void 0?void 0:n[r]},xde=V([QF],e=>{if(!(!e||e.length===0))return t=>{var r,n=1/0,a=e[0];for(var i of e){var o=Math.abs(i.coordinate-t);oe.options.defaultTooltipEventType,ww=e=>e.options.validateTooltipEventTypes;function Sw(e,t,r){if(e==null)return t;var n=e?"axis":"item";return r==null?t:r.includes(n)?n:t}function So(e,t){var r=bw(e),n=ww(e);return Sw(t,r,n)}function xM(e){return Se(t=>So(t,e))}var x0=(e,t)=>{var r,n=Number(t);if(!(Tr(n)||t==null))return n>=0?e==null||(r=e[n])===null||r===void 0?void 0:r.value:void 0};var bM=e=>e.tooltip.settings;var xi={active:!1,index:null,dataKey:void 0,graphicalItemId:void 0,coordinate:void 0},JF={itemInteraction:{click:xi,hover:xi},axisInteraction:{click:xi,hover:xi},keyboardInteraction:xi,syncInteraction:{active:!1,index:null,dataKey:void 0,label:void 0,coordinate:void 0,sourceViewBox:void 0,graphicalItemId:void 0},tooltipItemPayloads:[],settings:{shared:void 0,trigger:"hover",axisId:0,active:!1,defaultIndex:void 0}},wM=rt({name:"tooltip",initialState:JF,reducers:{addTooltipEntrySettings:{reducer(e,t){e.tooltipItemPayloads.push($e(t.payload))},prepare:lt()},replaceTooltipEntrySettings:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=Cr(e).tooltipItemPayloads.indexOf($e(n));i>-1&&(e.tooltipItemPayloads[i]=$e(a))},prepare:lt()},removeTooltipEntrySettings:{reducer(e,t){var r=Cr(e).tooltipItemPayloads.indexOf($e(t.payload));r>-1&&e.tooltipItemPayloads.splice(r,1)},prepare:lt()},setTooltipSettingsState(e,t){e.settings=t.payload},setActiveMouseOverItemIndex(e,t){e.syncInteraction.active=!1,e.syncInteraction.sourceViewBox=void 0,e.keyboardInteraction.active=!1,e.itemInteraction.hover.active=!0,e.itemInteraction.hover.index=t.payload.activeIndex,e.itemInteraction.hover.dataKey=t.payload.activeDataKey,e.itemInteraction.hover.graphicalItemId=t.payload.activeGraphicalItemId,e.itemInteraction.hover.coordinate=t.payload.activeCoordinate},mouseLeaveChart(e){e.itemInteraction.hover.active=!1,e.axisInteraction.hover.active=!1},mouseLeaveItem(e){e.itemInteraction.hover.active=!1},setActiveClickItemIndex(e,t){e.syncInteraction.active=!1,e.syncInteraction.sourceViewBox=void 0,e.itemInteraction.click.active=!0,e.keyboardInteraction.active=!1,e.itemInteraction.click.index=t.payload.activeIndex,e.itemInteraction.click.dataKey=t.payload.activeDataKey,e.itemInteraction.click.graphicalItemId=t.payload.activeGraphicalItemId,e.itemInteraction.click.coordinate=t.payload.activeCoordinate},setMouseOverAxisIndex(e,t){e.syncInteraction.active=!1,e.syncInteraction.sourceViewBox=void 0,e.axisInteraction.hover.active=!0,e.keyboardInteraction.active=!1,e.axisInteraction.hover.index=t.payload.activeIndex,e.axisInteraction.hover.dataKey=t.payload.activeDataKey,e.axisInteraction.hover.coordinate=t.payload.activeCoordinate},setMouseClickAxisIndex(e,t){e.syncInteraction.active=!1,e.syncInteraction.sourceViewBox=void 0,e.keyboardInteraction.active=!1,e.axisInteraction.click.active=!0,e.axisInteraction.click.index=t.payload.activeIndex,e.axisInteraction.click.dataKey=t.payload.activeDataKey,e.axisInteraction.click.coordinate=t.payload.activeCoordinate},setSyncInteraction(e,t){e.syncInteraction=t.payload},setKeyboardInteraction(e,t){e.keyboardInteraction.active=t.payload.active,e.keyboardInteraction.index=t.payload.activeIndex,e.keyboardInteraction.coordinate=t.payload.activeCoordinate}}}),Xn=wM.actions,SM=Xn.addTooltipEntrySettings,AM=Xn.replaceTooltipEntrySettings,NM=Xn.removeTooltipEntrySettings,_M=Xn.setTooltipSettingsState,CM=Xn.setActiveMouseOverItemIndex,kde=Xn.mouseLeaveItem,b0=Xn.mouseLeaveChart,Ode=Xn.setActiveClickItemIndex,w0=Xn.setMouseOverAxisIndex,EM=Xn.setMouseClickAxisIndex,Vu=Xn.setSyncInteraction,mf=Xn.setKeyboardInteraction;var kM=wM.reducer;function OM(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function S0(e){for(var t=1;t{if(t==null)return xi;var a=rq(e,t,r);if(a==null)return xi;if(a.active)return a;if(e.keyboardInteraction.active)return e.keyboardInteraction;if(e.syncInteraction.active&&e.syncInteraction.index!=null)return e.syncInteraction;var i=e.settings.active===!0;if(nq(a)){if(i)return S0(S0({},a),{},{active:!0})}else if(n!=null)return{active:!0,coordinate:void 0,dataKey:void 0,index:n,graphicalItemId:void 0};return S0(S0({},xi),{},{coordinate:a.coordinate})};function aq(e){if(typeof e=="number")return Number.isFinite(e)?e:void 0;if(e instanceof Date){var t=e.valueOf();return Number.isFinite(t)?t:void 0}var r=Number(e);return Number.isFinite(r)?r:void 0}function iq(e,t){var r=aq(e),n=t[0],a=t[1];if(r===void 0)return!1;var i=Math.min(n,a),o=Math.max(n,a);return r>=i&&r<=o}function oq(e,t,r){if(r==null||t==null)return!0;var n=_t(e,t);return n==null||!_n(r)?!0:iq(n,r)}var Ao=(e,t,r,n)=>{var a=e?.index;if(a==null)return null;var i=Number(a);if(!Ie(i))return a;var o=0,l=1/0;t.length>0&&(l=t.length-1);var s=Math.max(o,Math.min(i,l)),u=t[s];return u==null||oq(u,r,n)?String(s):null};var N0=(e,t,r,n,a,i,o)=>{if(i!=null){var l=o[0],s=l?.getPosition(i);if(s!=null)return s;var u=a?.[Number(i)];if(u)return r==="horizontal"?{x:u.coordinate,y:(n.top+t)/2}:{x:(n.left+e)/2,y:u.coordinate}}};var _0=(e,t,r,n)=>{if(t==="axis")return e.tooltipItemPayloads;if(e.tooltipItemPayloads.length===0)return[];var a;if(r==="hover"?a=e.itemInteraction.hover.graphicalItemId:a=e.itemInteraction.click.graphicalItemId,e.syncInteraction.active&&a==null)return e.tooltipItemPayloads;if(a==null&&(n!=null||e.keyboardInteraction.active)){var i=e.tooltipItemPayloads[0];return i!=null?[i]:[]}return e.tooltipItemPayloads.filter(o=>{var l;return((l=o.settings)===null||l===void 0?void 0:l.graphicalItemId)===a})};var C0=e=>e.options.tooltipPayloadSearcher;var bi=e=>e.tooltip;function TM(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function MM(e){for(var t=1;te(t)}function DM(e){if(typeof e=="string")return e}function pq(e){if(!(e==null||typeof e!="object")){var t="name"in e?cq(e.name):void 0,r="unit"in e?dq(e.unit):void 0,n="dataKey"in e?fq(e.dataKey):void 0,a="payload"in e?e.payload:void 0,i="color"in e?DM(e.color):void 0,o="fill"in e?DM(e.fill):void 0;return{name:t,unit:r,dataKey:n,payload:a,color:i,fill:o}}}function mq(e,t){return e??t}var E0=(e,t,r,n,a,i,o)=>{if(!(t==null||i==null)){var l=r.chartData,s=r.computedData,u=r.dataStartIndex,c=r.dataEndIndex,d=[];return e.reduce((f,p)=>{var v,h=p.dataDefinedOnItem,b=p.settings,m=mq(h,l),g=Array.isArray(m)?Yv(m,u,c):m,x=(v=b?.dataKey)!==null&&v!==void 0?v:n,A=b?.nameKey,k;if(n&&Array.isArray(g)&&!Array.isArray(g[0])&&o==="axis"?(k=zm(g,n,a),k==null&&(k=i(g,t,s,A))):k=i(g,t,s,A),Array.isArray(k))k.forEach(M=>{var E,L,I=pq(M),j=I?.name,z=I?.dataKey,X=I?.payload,R=MM(MM({},b),{},{name:j,unit:I?.unit,color:(E=I?.color)!==null&&E!==void 0?E:b?.color,fill:(L=I?.fill)!==null&&L!==void 0?L:b?.fill});f.push(M1({tooltipEntrySettings:R,dataKey:z,payload:X,value:_t(X,z),name:j==null?void 0:String(j)}))});else{var _;f.push(M1({tooltipEntrySettings:b,dataKey:x,payload:k,value:_t(k,x),name:(_=_t(k,A))!==null&&_!==void 0?_:b?.name}))}return f},d)}};var Aw=V([Zt,U2,bu],c0),vq=V([e=>e.graphicalItems.cartesianItems,e=>e.graphicalItems.polarItems],(e,t)=>[...e,...t]),hq=V([Kt,ho],$2),No=V([vq,Zt,hq],V2,{memoizeOptions:{resultEqualityCheck:Su}}),gq=V([No],e=>e.filter($d)),PM=V([No],q2,{memoizeOptions:{resultEqualityCheck:Su}}),yq=V([No],e=>e.some(t=>!t.data)),wi=V([PM,on],Y2),xq=V([gq,on,Zt],Eh),Nw=V([wi,Zt,No,on,yq,PM],K2),jM=V([Zt],p0),bq=V([Zt],e=>e.allowDataOverflow),IM=V([jM,bq],vh),wq=V([No],e=>e.filter($d)),Sq=V([xq,wq,xu,xh],G2),Aq=V([Sq,on,Kt,IM],Z2),Nq=V([No],F2),_q=V([wi,Zt,Nq,m0,Kt,p3],Q2,{memoizeOptions:{resultEqualityCheck:fl}}),Cq=V([J2,Kt,ho],Cl),Eq=V([Cq,Kt],tw),kq=V([R2,Kt,ho],Cl),Oq=V([kq,Kt],rw),Tq=V([ew,Kt,ho],Cl),Mq=V([Tq,Kt],nw),Dq=V([Eq,Mq,Oq],df),Pq=V([Zt,jM,IM,Aq,_q,Dq,ot,Kt],aw),_o=V([Zt,ot,wi,Nw,xu,Kt,Pq],iw),jq=V([_o,Zt,Aw],lw),Iq=V([Zt,_o,jq,Kt],uw),zM=e=>{var t=Kt(e),r=ho(e),n=!1;return Hu(e,t,r,n)},_w=V([Zt,zM],cl),zq=V([Zt,Aw,Iq,_w],cf),Cw=V([zq],Hd),Lq=V([ot,Nw,Zt,Kt],vw),Bq=V([ot,Nw,Zt,Kt],pw),Uq=(e,t,r,n,a,i,o,l)=>{if(t){var s=t.type,u=an(e,l);if(n){var c=r==="scaleBand"&&n.bandwidth?n.bandwidth()/2:2,d=s==="category"&&n.bandwidth?n.bandwidth()/c:0;return d=l==="angleAxis"&&a!=null&&a?.length>=2?ur(a[0]-a[1])*2*d:d,u&&o?o.map((f,p)=>{var v=n.map(f);return Ie(v)?{coordinate:v+d,value:f,index:p,offset:d}:null}).filter(cr):n.domain().map((f,p)=>{var v=n.map(f);return Ie(v)?{coordinate:v+d,value:i?i[f]:f,index:p,offset:d}:null}).filter(cr)}}},sn=V([ot,Zt,Aw,Cw,zM,Lq,Bq,Kt],Uq),Ew=V([bw,ww,bM],(e,t,r)=>Sw(r.shared,e,t)),LM=e=>e.tooltip.settings.trigger,kw=e=>e.tooltip.settings.defaultIndex,vf=V([bi,Ew,LM,kw],A0),El=V([vf,wi,yi,_o],Ao),k0=V([sn,El],x0),BM=V([vf],e=>{if(e)return e.dataKey}),UM=V([vf],e=>{if(e)return e.graphicalItemId}),$M=V([bi,Ew,LM,kw],_0),$q=V([Dr,Pr,ot,Ct,sn,kw,$M],N0),Ow=V([vf,$q],(e,t)=>e!=null&&e.coordinate?e.coordinate:t),Tw=V([vf],e=>{var t;return(t=e?.active)!==null&&t!==void 0?t:!1}),Hq=V([$M,El,on,yi,k0,C0,Ew],E0),HM=V([Hq],e=>{if(e!=null){var t=e.map(r=>r.payload).filter(r=>r!=null);return Array.from(new Set(t))}});function VM(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function FM(e){for(var t=1;tSe(Zt),qM=()=>{var e=Yq(),t=Se(sn),r=Se(Cw);return!e||!r?su(void 0,t):su(FM(FM({},e),{},{scale:r}),t)};function YM(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function Fu(e){for(var t=1;t{var a=t.find(i=>i&&i.index===r);if(a){if(e==="horizontal")return{x:a.coordinate,y:n.relativeY};if(e==="vertical")return{x:n.relativeX,y:a.coordinate}}return{x:0,y:0}},KM=(e,t,r,n)=>{var a=t.find(u=>u&&u.index===r);if(a){if(e==="centric"){var i=a.coordinate,o=n.radius;return Fu(Fu(Fu({},n),Ht(n.cx,n.cy,o,i)),{},{angle:i,radius:o})}var l=a.coordinate,s=n.angle;return Fu(Fu(Fu({},n),Ht(n.cx,n.cy,l,s)),{},{angle:s,radius:l})}return{angle:0,clockWise:!1,cx:0,cy:0,endAngle:0,innerRadius:0,outerRadius:0,radius:0,startAngle:0,x:0,y:0}};function GM(e,t){var r=e.relativeX,n=e.relativeY;return r>=t.left&&r<=t.left+t.width&&n>=t.top&&n<=t.top+t.height}var Mw=(e,t,r,n,a)=>{var i,o=(i=t?.length)!==null&&i!==void 0?i:0;if(o<=1||e==null)return 0;if(n==="angleAxis"&&a!=null&&Math.abs(Math.abs(a[1]-a[0])-360)<=1e-6)for(var l=a[1]-a[0],s=(Q,y,C)=>[e,e+l,e-l].some(O=>(C?O>=Q:O>Q)&&O<=y),u=0;u0?(c=r[u-1])===null||c===void 0?void 0:c.coordinate:(d=r[o-1])===null||d===void 0?void 0:d.coordinate,b=(f=r[u])===null||f===void 0?void 0:f.coordinate,m=u>=o-1?(p=r[0])===null||p===void 0?void 0:p.coordinate:(v=r[u+1])===null||v===void 0?void 0:v.coordinate,g=void 0;if(!(h==null||b==null||m==null))if(ur(b-h)!==ur(m-b)){var x=[];if(ur(m-b)===ur(a[1]-a[0])){g=m;var A=b+a[1]-a[0];x[0]=Math.min(A,(A+h)/2),x[1]=Math.max(A,(A+h)/2)}else{g=h;var k=m+a[1]-a[0];x[0]=Math.min(b,(k+b)/2),x[1]=Math.max(b,(k+b)/2)}var _=[Math.min(b,(g+b)/2),Math.max(b,(g+b)/2)];if(s(_[0],_[1],!1)||s(x[0],x[1],!0)){var M;return(M=r[u])===null||M===void 0?void 0:M.index}}else{var E=Math.min(h,m),L=Math.max(h,m);if(s((E+b)/2,(L+b)/2,!1)){var I;return(I=r[u])===null||I===void 0?void 0:I.index}}}else if(t)for(var j=0;j(z.coordinate+R.coordinate)/2||j>0&&j(z.coordinate+R.coordinate)/2&&e<=(z.coordinate+X.coordinate)/2)return z.index}}return-1};var O0=()=>Se(bu),Dw=(e,t)=>t,XM=(e,t,r)=>r,Pw=(e,t,r,n)=>n,ZM=V(sn,e=>ci(e,t=>t.coordinate)),jw=V([bi,Dw,XM,Pw],A0),Iw=V([jw,wi,yi,_o],Ao),QM=(e,t,r)=>{if(t!=null){var n=bi(e);return t==="axis"?r==="hover"?n.axisInteraction.hover.dataKey:n.axisInteraction.click.dataKey:r==="hover"?n.itemInteraction.hover.dataKey:n.itemInteraction.click.dataKey}},JM=V([bi,Dw,XM,Pw],_0),hf=V([Dr,Pr,ot,Ct,sn,Pw,JM],N0),RM=V([jw,hf],(e,t)=>{var r;return(r=e.coordinate)!==null&&r!==void 0?r:t}),zw=V([sn,Iw],x0),e8=V([JM,Iw,on,yi,zw,C0,Dw],E0),t8=V([jw,Iw],(e,t)=>({isActive:e.active&&t!=null,activeIndex:t})),Xq=(e,t,r,n,a,i,o)=>{if(!(!e||!r||!n||!a)&&GM(e,o)){var l=SO(e,t),s=Mw(l,i,a,r,n),u=WM(t,a,s,e);return{activeIndex:String(s),activeCoordinate:u}}},Zq=(e,t,r,n,a,i,o)=>{if(!(!e||!n||!a||!i||!r)){var l=JT(e,r);if(l){var s=AO(l,t),u=Mw(s,o,i,n,a),c=KM(t,i,u,l);return{activeIndex:String(u),activeCoordinate:c}}}},r8=(e,t,r,n,a,i,o,l)=>{if(!(!e||!t||!n||!a||!i))return t==="horizontal"||t==="vertical"?Xq(e,t,n,a,i,o,l):Zq(e,t,r,n,a,i,o)};var qu=te(ae()),f8=te(ap());var n8=V(e=>e.zIndex.zIndexMap,(e,t)=>t,(e,t,r)=>r,(e,t,r)=>{if(t!=null){var n=e[t];if(n!=null)return r?n.panoramaElement:n.element}}),a8=V(e=>e.zIndex.zIndexMap,e=>{var t=Object.keys(e).map(n=>parseInt(n,10)).concat(Object.values(ct)),r=Array.from(new Set(t));return r.sort((n,a)=>n-a)},{memoizeOptions:{resultEqualityCheck:O3}});function i8(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function o8(e){for(var t=1;to8(o8({},e),{},{[t]:{element:void 0,panoramaElement:void 0,consumers:0}}),eY)},rY=new Set(Object.values(ct));function nY(e){return rY.has(e)}var l8=rt({name:"zIndex",initialState:tY,reducers:{registerZIndexPortal:{reducer:(e,t)=>{var r=t.payload.zIndex;e.zIndexMap[r]?e.zIndexMap[r].consumers+=1:e.zIndexMap[r]={consumers:1,element:void 0,panoramaElement:void 0}},prepare:lt()},unregisterZIndexPortal:{reducer:(e,t)=>{var r=t.payload.zIndex;e.zIndexMap[r]&&(e.zIndexMap[r].consumers-=1,e.zIndexMap[r].consumers<=0&&!nY(r)&&delete e.zIndexMap[r])},prepare:lt()},registerZIndexPortalElement:{reducer:(e,t)=>{var r=t.payload,n=r.zIndex,a=r.element,i=r.isPanorama;e.zIndexMap[n]?i?e.zIndexMap[n].panoramaElement=$e(a):e.zIndexMap[n].element=$e(a):e.zIndexMap[n]={consumers:0,element:i?void 0:$e(a),panoramaElement:i?$e(a):void 0}},prepare:lt()},unregisterZIndexPortalElement:{reducer:(e,t)=>{var r=t.payload.zIndex;e.zIndexMap[r]&&(t.payload.isPanorama?e.zIndexMap[r].panoramaElement=void 0:e.zIndexMap[r].element=void 0)},prepare:lt()}}}),T0=l8.actions,s8=T0.registerZIndexPortal,M0=T0.unregisterZIndexPortal,u8=T0.registerZIndexPortalElement,c8=T0.unregisterZIndexPortalElement;var d8=l8.reducer;function Er(e){var t=e.zIndex,r=e.children,n=UO(),a=n&&t!==void 0&&t!==0,i=xt(),o=(0,qu.useRef)(void 0),l=(0,qu.useRef)(new Set),s=Ve(),u=Se(d=>n8(d,t,i));if((0,qu.useLayoutEffect)(()=>{if(!a){var d=l.current;d.forEach(p=>{s(M0({zIndex:p}))}),d.clear(),o.current=void 0;return}if(l.current.has(t)||(s(s8({zIndex:t})),l.current.add(t)),u){o.current=u;var f=l.current;f.forEach(p=>{p!==t&&(s(M0({zIndex:p})),f.delete(p))})}},[s,t,a,u]),(0,qu.useLayoutEffect)(()=>{var d=l.current;return()=>{d.forEach(f=>{s(M0({zIndex:f}))}),d.clear()}},[s]),!a)return r;var c=u??o.current;return c?(0,f8.createPortal)(r,c):null}function Lw(){return Lw=Object.assign?Object.assign.bind():function(e){for(var t=1;t(0,j0.useContext)(Bw);var yf=te(ae());var y8=te(g8(),1);var x8=y8.default;var Wu=new x8;var z0="recharts.syncEvent.tooltip",$w="recharts.syncEvent.brush";var b8=(e,t)=>{if(t&&Array.isArray(e)){var r=Number.parseInt(t,10);if(!Tr(r))return e[r]}},dY={chartName:"",tooltipPayloadSearcher:()=>{},eventEmitter:void 0,defaultTooltipEventType:"axis"},w8=rt({name:"options",initialState:dY,reducers:{createEventEmitter:e=>{e.eventEmitter==null&&(e.eventEmitter=Symbol("rechartsEventEmitter"))}}}),S8=w8.reducer,A8=w8.actions.createEventEmitter;function N8(e){return e.tooltip.syncInteraction}var fY={chartData:void 0,computedData:void 0,dataStartIndex:0,dataEndIndex:0},_8=rt({name:"chartData",initialState:fY,reducers:{setChartData(e,t){if(e.chartData=$e(t.payload),t.payload==null){e.dataStartIndex=0,e.dataEndIndex=0;return}t.payload.length>0&&e.dataEndIndex!==t.payload.length-1&&(e.dataEndIndex=t.payload.length-1)},setComputedData(e,t){e.computedData=t.payload},setDataStartEndIndexes(e,t){var r=t.payload,n=r.startIndex,a=r.endIndex;n!=null&&(e.dataStartIndex=n),a!=null&&(e.dataEndIndex=a)}}}),Hw=_8.actions,Vw=Hw.setChartData,C8=Hw.setDataStartEndIndexes,pY=Hw.setComputedData;var E8=_8.reducer;var mY=["x","y"];function k8(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function Ku(e){for(var t=1;ts.rootProps.className);(0,yf.useEffect)(()=>{if(e==null)return sa;var s=(u,c,d)=>{if(t!==d&&e===u){if(c.payload.active===!1){r(Vu({active:!1,coordinate:void 0,dataKey:void 0,index:null,label:void 0,sourceViewBox:void 0,graphicalItemId:void 0}));return}if(n==="index"){var f;if(o&&c!==null&&c!==void 0&&(f=c.payload)!==null&&f!==void 0&&f.coordinate&&c.payload.sourceViewBox){var p=c.payload.coordinate,v=p.x,h=p.y,b=yY(p,mY),m=c.payload.sourceViewBox,g=m.x,x=m.y,A=m.width,k=m.height,_=Ku(Ku({},b),{},{x:o.x+(A?(v-g)/A:0)*o.width,y:o.y+(k?(h-x)/k:0)*o.height});r(Ku(Ku({},c),{},{payload:Ku(Ku({},c.payload),{},{coordinate:_})}))}else r(c);return}if(a!=null){var M;if(typeof n=="function"){var E={activeTooltipIndex:c.payload.index==null?void 0:Number(c.payload.index),isTooltipActive:c.payload.active,activeIndex:c.payload.index==null?void 0:Number(c.payload.index),activeLabel:c.payload.label,activeDataKey:c.payload.dataKey,activeCoordinate:c.payload.coordinate},L=n(a,E);M=a[L]}else n==="value"&&(M=a.find(C=>String(C.value)===c.payload.label));var I=c.payload.coordinate;if(I==null||o==null){r(Vu({active:!1,coordinate:void 0,dataKey:void 0,index:null,label:void 0,sourceViewBox:void 0,graphicalItemId:void 0}));return}if(M==null){r(Vu({active:!1,coordinate:void 0,dataKey:void 0,index:null,label:void 0,sourceViewBox:c.payload.sourceViewBox,graphicalItemId:void 0}));return}var j=I.x,z=I.y,X=Math.min(j,o.x+o.width),R=Math.min(z,o.y+o.height),Q={x:i==="horizontal"?M.coordinate:X,y:i==="horizontal"?R:M.coordinate},y=Vu({active:c.payload.active,coordinate:Q,dataKey:c.payload.dataKey,index:String(M.index),label:c.payload.label,sourceViewBox:c.payload.sourceViewBox,graphicalItemId:c.payload.graphicalItemId});r(y)}}};return Wu.on(z0,s),()=>{Wu.off(z0,s)}},[l,r,t,e,n,a,i,o])}function wY(){var e=Se(bh),t=Se(wh),r=Ve();(0,yf.useEffect)(()=>{if(e==null)return sa;var n=(a,i,o)=>{t!==o&&e===a&&r(C8(i))};return Wu.on($w,n),()=>{Wu.off($w,n)}},[r,t,e])}function O8(){var e=Ve();(0,yf.useEffect)(()=>{e(A8())},[e]),bY(),wY()}function T8(e,t,r,n,a,i){var o=Se(v=>QM(v,e,t)),l=Se(UM),s=Se(wh),u=Se(bh),c=Se(Q1),d=Se(N8),f=d?.sourceViewBox!=null,p=ll();(0,yf.useEffect)(()=>{if(!f&&u!=null&&s!=null){var v=Vu({active:i,coordinate:r,dataKey:o,index:a,label:typeof n=="number"?String(n):n,sourceViewBox:p,graphicalItemId:l});Wu.emit(z0,u,v,s)}},[f,r,o,l,a,n,s,u,c,i,p])}function M8(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function D8(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r{M(_M({shared:g,trigger:x,axisId:_,active:a,defaultIndex:E}))},[M,g,x,_,a,E]);var L=ll(),I=rh(),j=xM(g),z=(t=Se(F=>t8(F,j,x,E)))!==null&&t!==void 0?t:{},X=z.activeIndex,R=z.isActive,Q=Se(F=>e8(F,j,x,E)),y=Se(F=>zw(F,j,x,E)),C=Se(F=>RM(F,j,x,E)),O=Q,D=v8(),N=(r=a??R)!==null&&r!==void 0?r:!1,T=bk([O,N]),$=_Y(T,2),Y=$[0],J=$[1],fe=j==="axis"?y:void 0;T8(j,x,C,fe,X,N);var pe=k??D;if(pe==null||L==null||j==null)return null;var ge=O??j8;N||(ge=j8),u&&ge.length&&(ge=ZE(ge.filter(F=>F.value!=null&&(F.hide!==!0||n.includeHidden)),f,TY));var ze=ge.length>0,se=D8(D8({},n),{},{payload:ge,label:fe,active:N,activeIndex:X,coordinate:C,accessibilityLayer:I}),Ne=Zn.createElement(lT,{allowEscapeViewBox:i,animationDuration:o,animationEasing:l,isAnimationActive:c,active:N,coordinate:C,hasPayload:ze,offset:d,position:p,reverseDirection:v,useTranslate3d:h,viewBox:L,wrapperStyle:b,lastBoundingBox:Y,innerRef:J,hasPortalFromProps:!!k},MY(s,se));return Zn.createElement(Zn.Fragment,null,(0,z8.createPortal)(Ne,pe),N&&Zn.createElement(m8,{cursor:m,tooltipEventType:j,coordinate:C,payload:ge,index:X}))}var qw=te(ae()),$0=te(ae());function PY(e,t,r){return(t=jY(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function jY(e){var t=IY(e,"string");return typeof t=="symbol"?t:t+""}function IY(e,t){if(typeof e!="object"||!e)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var n=r.call(e,t||"default");if(typeof n!="object")return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}var L0=class{constructor(t){PY(this,"cache",new Map),this.maxSize=t}get(t){var r=this.cache.get(t);return r!==void 0&&(this.cache.delete(t),this.cache.set(t,r)),r}set(t,r){if(this.cache.has(t))this.cache.delete(t);else if(this.cache.size>=this.maxSize){var n=this.cache.keys().next().value;n!=null&&this.cache.delete(n)}this.cache.set(t,r)}clear(){this.cache.clear()}size(){return this.cache.size}};function L8(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function zY(e){for(var t=1;t{try{var r=document.getElementById(U8);r||(r=document.createElement("span"),r.setAttribute("id",U8),r.setAttribute("aria-hidden","true"),document.body.appendChild(r)),Object.assign(r.style,HY,t),r.textContent="".concat(e);var n=r.getBoundingClientRect();return{width:n.width,height:n.height}}catch{return{width:0,height:0}}},kl=function(t){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};if(t==null||Yn.isSsr)return{width:0,height:0};if(!H8.enableCache)return $8(t,r);var n=VY(t,r),a=B8.get(n);if(a)return a;var i=$8(t,r);return B8.set(n,i),i};var W8;function B0(e,t){return WY(e)||YY(e,t)||qY(e,t)||FY()}function FY(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function qY(e,t){if(e){if(typeof e=="string")return V8(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?V8(e,t):void 0}}function V8(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);re.length)&&(t=e.length);for(var r=0,n=Array(t);r{var t=e.children,r=e.breakAll,n=e.style;try{var a=[];Mt(t)||(r?a=t.toString().split(""):a=t.toString().split(R8));var i=a.map(l=>({word:l,width:kl(l,n).width})),o=r?0:kl("\xA0",n).width;return{wordsWithComputedWidth:i,spaceWidth:o}}catch{return null}};function H0(e){return e==="start"||e==="middle"||e==="end"||e==="inherit"}function tD(e){return Mt(e)||typeof e=="string"||typeof e=="number"||typeof e=="boolean"}var rD=(e,t,r,n)=>e.reduce((a,i)=>{var o=i.word,l=i.width,s=a[a.length-1];if(s&&l!=null&&(t==null||n||s.width+l+re.reduce((t,r)=>t.width>r.width?t:r),fW="\u2026",Q8=(e,t,r,n,a,i,o,l)=>{var s=e.slice(0,t),u=eD({breakAll:r,style:n,children:s+fW});if(!u)return[!1,[]];var c=rD(u.wordsWithComputedWidth,i,o,l),d=c.length>a||nD(c).width>Number(i);return[d,c]},pW=(e,t,r,n,a)=>{var i=e.maxLines,o=e.children,l=e.style,s=e.breakAll,u=ye(i),c=String(o),d=rD(t,n,r,a);if(!u||a)return d;var f=d.length>i||nD(d).width>Number(n);if(!f)return d;for(var p=0,v=c.length-1,h=0,b;p<=v&&h<=c.length-1;){var m=Math.floor((p+v)/2),g=m-1,x=Q8(c,g,s,l,i,n,r,a),A=X8(x,2),k=A[0],_=A[1],M=Q8(c,m,s,l,i,n,r,a),E=X8(M,1),L=E[0];if(!k&&!L&&(p=m+1),k&&L&&(v=m-1),!k&&L){b=_;break}h++}return b||d},J8=e=>{var t=Mt(e)?[]:e.toString().split(R8);return[{words:t,width:void 0}]},mW=e=>{var t=e.width,r=e.scaleToFit,n=e.children,a=e.style,i=e.breakAll,o=e.maxLines;if((t||r)&&!Yn.isSsr){var l,s,u=eD({breakAll:i,children:n,style:a});if(u){var c=u.wordsWithComputedWidth,d=u.spaceWidth;l=c,s=d}else return J8(n);return pW({breakAll:i,children:n,maxLines:o,style:a},l,s,t,!!r)}return J8(n)},aD="#808080",vW={angle:0,breakAll:!1,capHeight:"0.71em",fill:aD,lineHeight:"1em",scaleToFit:!1,textAnchor:"start",verticalAnchor:"end",x:0,y:0},xf=(0,$0.forwardRef)((e,t)=>{var r=Dt(e,vW),n=r.x,a=r.y,i=r.lineHeight,o=r.capHeight,l=r.fill,s=r.scaleToFit,u=r.textAnchor,c=r.verticalAnchor,d=G8(r,iW),f=(0,$0.useMemo)(()=>mW({breakAll:d.breakAll,children:d.children,maxLines:d.maxLines,scaleToFit:s,style:d.style,width:d.width}),[d.breakAll,d.children,d.maxLines,s,d.style,d.width]),p=d.dx,v=d.dy,h=d.angle,b=d.className,m=d.breakAll,g=G8(d,oW);if(!Fr(n)||!Fr(a)||f.length===0)return null;var x=Number(n)+(ye(p)?p:0),A=Number(a)+(ye(v)?v:0);if(!Ie(x)||!Ie(A))return null;var k;switch(c){case"start":k=U0("calc(".concat(o,")"));break;case"middle":k=U0("calc(".concat((f.length-1)/2," * -").concat(i," + (").concat(o," / 2))"));break;default:k=U0("calc(".concat(f.length-1," * -").concat(i,")"));break}var _=[],M=f[0];if(s&&M!=null){var E=M.width,L=d.width;_.push("scale(".concat(ye(L)&&ye(E)?L/E:1,")"))}return h&&_.push("rotate(".concat(h,", ").concat(x,", ").concat(A,")")),_.length&&(g.transform=_.join(" ")),qw.createElement("text",Fw({},Wt(g),{ref:t,x,y:A,className:Le("recharts-text",b),textAnchor:u,fill:l.includes("url")?aD:l}),f.map((I,j)=>{var z=I.words.join(m?"":" ");return qw.createElement("tspan",{x,dy:j===0?k:i,key:"".concat(z,"-").concat(j)},z)}))});xf.displayName="Text";var On=te(ae()),yr=te(ae());var hW=["labelRef"],gW=["content"];function iD(e,t){if(e==null)return{};var r,n,a=yW(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n{var t=e.x,r=e.y,n=e.upperWidth,a=e.lowerWidth,i=e.width,o=e.height,l=e.children,s=(0,yr.useMemo)(()=>({x:t,y:r,upperWidth:n,lowerWidth:a,width:i,height:o}),[t,r,n,a,i,o]);return On.createElement(lD.Provider,{value:s},l)},uD=()=>{var e=(0,yr.useContext)(lD),t=ll();return e||(t?pd(t):void 0)},SW=(0,yr.createContext)(null);var AW=()=>{var e=(0,yr.useContext)(SW),t=Se(Ch);return e||t},NW=e=>{var t=e.value,r=e.formatter,n=Mt(e.children)?t:e.children;return typeof r=="function"?r(n):n},Tl=e=>e!=null&&typeof e=="function",_W=(e,t)=>{var r=ur(t-e),n=Math.min(Math.abs(t-e),360);return r*n},CW=(e,t,r,n,a)=>{var i=e.offset,o=e.className,l=a.cx,s=a.cy,u=a.innerRadius,c=a.outerRadius,d=a.startAngle,f=a.endAngle,p=a.clockWise,v=(u+c)/2,h=_W(d,f),b=h>=0?1:-1,m,g;switch(t){case"insideStart":m=d+b*i,g=p;break;case"insideEnd":m=f-b*i,g=!p;break;case"end":m=f+b*i,g=p;break;default:throw new Error("Unsupported position ".concat(t))}g=h<=0?g:!g;var x=Ht(l,s,v,m),A=Ht(l,s,v,m+(g?1:-1)*359),k="M".concat(x.x,",").concat(x.y,` + A`).concat(v,",").concat(v,",0,1,").concat(g?0:1,`, + `).concat(A.x,",").concat(A.y),_=Mt(e.id)?si("recharts-radial-line-"):e.id;return On.createElement("text",Ai({},n,{dominantBaseline:"central",className:Le("recharts-radial-bar-label",o)}),On.createElement("defs",null,On.createElement("path",{id:_,d:k})),On.createElement("textPath",{xlinkHref:"#".concat(_)},r))},EW=(e,t,r)=>{var n=e.cx,a=e.cy,i=e.innerRadius,o=e.outerRadius,l=e.startAngle,s=e.endAngle,u=(l+s)/2;if(r==="outside"){var c=Ht(n,a,o+t,u),d=c.x,f=c.y;return{x:d,y:f,textAnchor:d>=n?"start":"end",verticalAnchor:"middle"}}if(r==="center")return{x:n,y:a,textAnchor:"middle",verticalAnchor:"middle"};if(r==="centerTop")return{x:n,y:a,textAnchor:"middle",verticalAnchor:"start"};if(r==="centerBottom")return{x:n,y:a,textAnchor:"middle",verticalAnchor:"end"};var p=(i+o)/2,v=Ht(n,a,p,u),h=v.x,b=v.y;return{x:h,y:b,textAnchor:"middle",verticalAnchor:"middle"}},Zu=e=>e!=null&&"cx"in e&&ye(e.cx),kW={angle:0,offset:5,zIndex:ct.label,position:"middle",textBreakAll:!1};function OW(e){if(!Zu(e))return e;var t=e.cx,r=e.cy,n=e.outerRadius,a=n*2;return{x:t-n,y:r-n,width:a,upperWidth:a,lowerWidth:a,height:a}}function Si(e){var t,r,n=Dt(e,kW),a=n.viewBox,i=n.parentViewBox,o=n.position,l=n.value,s=n.children,u=n.content,c=n.className,d=c===void 0?"":c,f=n.textBreakAll,p=n.labelRef,v=AW(),h=uD(),b=o==="center"?h:v??h,m,g,x;a==null?m=b:Zu(a)?m=a:m=pd(a);var A=OW(m);if(!m||Mt(l)&&Mt(s)&&!(0,yr.isValidElement)(u)&&typeof u!="function")return null;var k=Zu(m)&&(o==="insideStart"||o==="insideEnd"||o==="end");if(Zu(m))k||(x=EW(m,n.offset,n.position));else if(A){var _=uE({viewBox:A,position:o,offset:n.offset,parentViewBox:Zu(i)?void 0:i,clamp:!0});x=Ol(Ol({x:_.x,y:_.y,textAnchor:_.horizontalAnchor,verticalAnchor:_.verticalAnchor},_.width!==void 0?{width:_.width}:{}),_.height!==void 0?{height:_.height}:{})}var M=Ol(Ol(Ol(Ol({},((t=x)===null||t===void 0?void 0:t.x)!==void 0?{x:x.x}:{}),((r=x)===null||r===void 0?void 0:r.y)!==void 0?{y:x.y}:{}),n),{},{viewBox:m});if((0,yr.isValidElement)(u)){var E=M.labelRef,L=iD(M,hW);return(0,yr.cloneElement)(u,L)}if(typeof u=="function"){var I=M.content,j=iD(M,gW);if(g=(0,yr.createElement)(u,j),(0,yr.isValidElement)(g))return g}else g=NW(n);var z=Wt(n);return k&&Zu(m)?CW(n,o,g,z,m):x==null?null:On.createElement(Er,{zIndex:n.zIndex},On.createElement(xf,Ai({ref:p,className:Le("recharts-label",d)},z,x,{textAnchor:H0(z.textAnchor)?z.textAnchor:x.textAnchor,breakAll:f}),g))}Si.displayName="Label";var TW=(e,t,r)=>{if(!e)return null;var n={viewBox:t,labelRef:r};return e===!0?On.createElement(Si,Ai({key:"label-implicit"},n)):Fr(e)?On.createElement(Si,Ai({key:"label-implicit",value:e},n)):(0,yr.isValidElement)(e)?e.type===Si?(0,yr.cloneElement)(e,Ol({key:"label-implicit"},n)):On.createElement(Si,Ai({key:"label-implicit",content:e},n)):Tl(e)?On.createElement(Si,Ai({key:"label-implicit",content:e},n)):e&&typeof e=="object"?On.createElement(Si,Ai({},e,{key:"label-implicit"},n)):null};function cD(e){var t=e.label,r=e.labelRef,n=uD();return TW(t,n,r)||null}var Ni=te(ae()),Qu=te(ae());var MW=["valueAccessor"],DW=["dataKey","clockWise","id","textBreakAll","zIndex"];function F0(){return F0=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var t=Array.isArray(e.value)?e.value[e.value.length-1]:e.value;if(tD(t))return t},fD=(0,Qu.createContext)(void 0),pD=fD.Provider,mD=(0,Qu.createContext)(void 0),Qme=mD.Provider;function IW(){return(0,Qu.useContext)(fD)}function zW(){return(0,Qu.useContext)(mD)}function V0(e){var t=e.valueAccessor,r=t===void 0?jW:t,n=dD(e,MW),a=n.dataKey,i=n.clockWise,o=n.id,l=n.textBreakAll,s=n.zIndex,u=dD(n,DW),c=IW(),d=zW(),f=c||d;return!f||!f.length?null:Ni.createElement(Er,{zIndex:s??ct.label},Ni.createElement(lr,{className:"recharts-label-list"},f.map((p,v)=>{var h,b=Mt(a)?r(p,v):_t(p.payload,a),m=Mt(o)?{}:{id:"".concat(o,"-").concat(v)};return Ni.createElement(Si,F0({key:"label-".concat(v)},Wt(p),u,m,{fill:(h=n.fill)!==null&&h!==void 0?h:p.fill,parentViewBox:p.parentViewBox,value:b,textBreakAll:l,viewBox:p.viewBox,index:v,zIndex:0}))})))}V0.displayName="LabelList";function vD(e){var t=e.label;return t?t===!0?Ni.createElement(V0,{key:"labelList-implicit"}):Ni.isValidElement(t)||Tl(t)?Ni.createElement(V0,{key:"labelList-implicit",content:t}):typeof t=="object"?Ni.createElement(V0,F0({key:"labelList-implicit"},t,{type:String(t.type)})):null:null}var hD=te(ae());function Yw(){return Yw=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var t=e.cx,r=e.cy,n=e.r,a=e.className,i=Le("recharts-dot",a);return ye(t)&&ye(r)&&ye(n)?hD.createElement("circle",Yw({},hr(e),Rs(e),{className:i,cx:t,cy:r,r:n})):null};var LW={radiusAxis:{},angleAxis:{}},gD=rt({name:"polarAxis",initialState:LW,reducers:{addRadiusAxis(e,t){e.radiusAxis[t.payload.id]=$e(t.payload)},removeRadiusAxis(e,t){delete e.radiusAxis[t.payload.id]},addAngleAxis(e,t){e.angleAxis[t.payload.id]=$e(t.payload)},removeAngleAxis(e,t){delete e.angleAxis[t.payload.id]}}}),Y0=gD.actions,ove=Y0.addRadiusAxis,lve=Y0.removeRadiusAxis,sve=Y0.addAngleAxis,uve=Y0.removeAngleAxis;var yD=gD.reducer;function xD(e){return e&&typeof e=="object"&&"className"in e&&typeof e.className=="string"?e.className:""}var BW=te(ae());var W0=e=>e&&typeof e=="object"&&"clipDot"in e?!!e.clipDot:!0;var Ju=te(ae()),bf=te(ae());function bD(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function wD(e){for(var t=1;t{n||(a.current===null?r(SM(t)):a.current!==t&&r(AM({prev:a.current,next:t})),a.current=t)},[t,r,n]),(0,wf.useLayoutEffect)(()=>()=>{a.current&&(r(NM(a.current)),a.current=null)},[r]),null}var Sf=te(ae());function _D(e){var t=e.legendPayload,r=Ve(),n=xt(),a=(0,Sf.useRef)(null);return(0,Sf.useLayoutEffect)(()=>{n||(a.current===null?r(HO(t)):a.current!==t&&r(VO({prev:a.current,next:t})),a.current=t)},[r,n,t]),(0,Sf.useLayoutEffect)(()=>()=>{a.current&&(r(FO(a.current)),a.current=null)},[r]),null}var kD=te(ae()),_f=te(ae());function WW(e,t){return ZW(e)||XW(e,t)||GW(e,t)||KW()}function KW(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function GW(e,t){if(e){if(typeof e=="string")return CD(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?CD(e,t):void 0}}function CD(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r2&&arguments[2]!==void 0?arguments[2]:[],n=[];for(var a of r)n.push({status:"removed",prev:a});for(var i=0;ie[Math.floor(i*r)]);return Ww(n,t)}function RW(e,t){var r=t.map((n,a)=>e[a]);return Ww(r,t)}function eK(e,t){for(var r=new Map,n=0;n{var p=r(d,f);if(p!=null){var v=n.get(p);if(v!==void 0)return a.add(p),v}}),o=[];for(var l of n){var s=WW(l,2),u=s[0],c=s[1];a.has(u)||o.push(c)}return Ww(i,t,o)}function Nf(e,t,r){return t==null?null:e==null?t.map(n=>({status:"added",next:n})):r===Af?JW(e,t):r===QW?RW(e,t):tK(e,t,r)}var Ru=te(ae());function K0(e,t){var r=(0,Ru.useRef)(e),n=(0,Ru.useRef)(t.current),a=(0,Ru.useRef)(!0);r.current!==e&&(r.current=e,n.current=t.current,a.current=!1);var i=(0,Ru.useCallback)(function(o,l){var s=arguments.length>2&&arguments[2]!==void 0?arguments[2]:!0;if(l===0){a.current=!0;return}l===1&&(n.current=o),l>0&&a.current&&s&&(t.current=o)},[t]);return{startValue:n.current,syncStepValue:i}}function rK(e,t){return oK(e)||iK(e,t)||aK(e,t)||nK()}function nK(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function aK(e,t){if(e){if(typeof e=="string")return ED(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?ED(e,t):void 0}}function ED(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{typeof e=="function"&&e(),i(!0)},[e]),l=(0,_f.useCallback)(()=>{typeof t=="function"&&t(),i(!1)},[t]);return{isAnimating:a,handleAnimationStart:o,handleAnimationEnd:l}}function TD(e){var t,r=e.animationInput,n=e.animationIdPrefix,a=e.items,i=e.previousItemsRef,o=e.isAnimationActive,l=e.animationBegin,s=e.animationDuration,u=e.animationEasing,c=e.onAnimationStart,d=e.onAnimationEnd,f=e.animationInterpolateFn,p=e.animationMatchBy,v=e.shouldUpdatePreviousRef,h=e.children,b=e.layout,m=sh(r,n),g=K0(m,i),x=(t=g.startValue)!==null&&t!==void 0?t:null,A=Nf(x,a,p??Af);return kD.createElement(lh,{animationId:m,begin:l,duration:s,isActive:o,easing:u,onAnimationEnd:d,onAnimationStart:c,key:m},k=>{var _=x==null,M=a==null?a:f(A,k,b),E=v?v(k):k>0;return g.syncStepValue(M,k,E),M==null?null:h(M,k,_)})}var PD=te(ae()),Gw=te(ae());var G0=te(ae());var Kw;function lK(e,t){return dK(e)||cK(e,t)||uK(e,t)||sK()}function sK(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function uK(e,t){if(e){if(typeof e=="string")return MD(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?MD(e,t):void 0}}function MD(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{var e=G0.useState(()=>si("uid-")),t=lK(e,1),r=t[0];return r},X0=(Kw=G0.useId)!==null&&Kw!==void 0?Kw:fK;function DD(e,t){var r=X0();return t||(e?"".concat(e,"-").concat(r):r)}var pK=(0,Gw.createContext)(void 0),jD=e=>{var t=e.id,r=e.type,n=e.children,a=DD("recharts-".concat(r),t);return PD.createElement(pK.Provider,{value:a},n(a))};var Ml=te(ae());var mK={cartesianItems:[],polarItems:[]},ID=rt({name:"graphicalItems",initialState:mK,reducers:{addCartesianGraphicalItem:{reducer(e,t){e.cartesianItems.push($e(t.payload))},prepare:lt()},replaceCartesianGraphicalItem:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=Cr(e).cartesianItems.indexOf($e(n));i>-1&&(e.cartesianItems[i]=$e(a))},prepare:lt()},removeCartesianGraphicalItem:{reducer(e,t){var r=Cr(e).cartesianItems.indexOf($e(t.payload));r>-1&&e.cartesianItems.splice(r,1)},prepare:lt()},addPolarGraphicalItem:{reducer(e,t){e.polarItems.push($e(t.payload))},prepare:lt()},removePolarGraphicalItem:{reducer(e,t){var r=Cr(e).polarItems.indexOf($e(t.payload));r>-1&&e.polarItems.splice(r,1)},prepare:lt()},replacePolarGraphicalItem:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=Cr(e).polarItems.indexOf($e(n));i>-1&&(e.polarItems[i]=$e(a))},prepare:lt()}}}),ec=ID.actions,zD=ec.addCartesianGraphicalItem,LD=ec.replaceCartesianGraphicalItem,BD=ec.removeCartesianGraphicalItem,vK=ec.addPolarGraphicalItem,hK=ec.removePolarGraphicalItem,gK=ec.replacePolarGraphicalItem;var UD=ID.reducer;var yK=e=>{var t=Ve(),r=(0,Ml.useRef)(null);return(0,Ml.useLayoutEffect)(()=>{r.current===null?t(zD(e)):r.current!==e&&t(LD({prev:r.current,next:e})),r.current=e},[t,e]),(0,Ml.useLayoutEffect)(()=>()=>{r.current&&(t(BD(r.current)),r.current=null)},[t]),null},$D=(0,Ml.memo)(yK);var Cf=te(ae()),Q0=te(ae());var xK=["points"];function HD(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function Xw(e){for(var t=1;t{var m,g,x=Xw(Xw(Xw({r:3},o),f),{},{index:b,cx:(m=h.x)!==null&&m!==void 0?m:void 0,cy:(g=h.y)!==null&&g!==void 0?g:void 0,dataKey:i,value:h.value,payload:h.payload,points:t});return Cf.createElement(_K,{key:"dot-".concat(b),option:r,dotProps:x,className:a})}),v={};return l&&s!=null&&(v.clipPath="url(#clipPath-".concat(d?"":"dots-").concat(s,")")),Cf.createElement(Er,{zIndex:c},Cf.createElement(lr,Z0({className:n},v),p))}var Ef=te(ae()),R0=te(ae());function FD(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function J0(e){for(var t=1;t({top:e.top,bottom:e.bottom,left:e.left,right:e.right}));var t6=V([e6,Dr,Pr],(e,t,r)=>{if(!(!e||t==null||r==null))return{x:e.left,y:e.top,width:Math.max(0,t-e.left-e.right),height:Math.max(0,r-e.top-e.bottom)}});var tc=()=>Se(t6),r6=()=>Se(HM);function n6(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function Qw(e){for(var t=1;t{var t=e.point,r=e.childIndex,n=e.mainColor,a=e.activeDot,i=e.dataKey,o=e.clipPath;if(a===!1||t.x==null||t.y==null)return null;var l={index:r,dataKey:i,cx:t.x,cy:t.y,r:4,fill:n??"none",strokeWidth:2,stroke:"#fff",payload:t.payload,value:t.value},s=Qw(Qw(Qw({},l),so(a)),Rs(a)),u;return(0,R0.isValidElement)(a)?u=(0,R0.cloneElement)(a,s):typeof a=="function"?u=a(s):u=Ef.createElement(q0,s),Ef.createElement(lr,{className:"recharts-active-dot",clipPath:o},u)};function Jw(e){var t=e.points,r=e.mainColor,n=e.activeDot,a=e.itemDataKey,i=e.clipPath,o=e.zIndex,l=o===void 0?ct.activeDot:o,s=Se(El),u=r6();if(t==null||u==null)return null;var c=t.find(d=>u.includes(d.payload));return Mt(c)?null:Ef.createElement(Er,{zIndex:l},Ef.createElement(jK,{point:c,childIndex:Number(s),mainColor:r,dataKey:a,activeDot:n,clipPath:i}))}var a6=te(ae());var i6=e=>{var t=e.chartData,r=Ve(),n=xt();return(0,a6.useEffect)(()=>n?()=>{}:(r(Vw(t)),()=>{r(Vw(void 0))}),[t,r,n]),null};var o6={x:0,y:0,width:0,height:0,padding:{top:0,right:0,bottom:0,left:0}},l6=rt({name:"brush",initialState:o6,reducers:{setBrushSettings(e,t){return t.payload==null?o6:t.payload}}}),Ohe=l6.actions.setBrushSettings;var s6=l6.reducer;function IK(e){return(e%180+180)%180}var u6=function(t){var r=t.width,n=t.height,a=arguments.length>1&&arguments[1]!==void 0?arguments[1]:0,i=IK(a),o=i*Math.PI/180,l=Math.atan(n/r),s=o>l&&o{e.dots.push(t.payload)},removeDot:(e,t)=>{var r=Cr(e).dots.findIndex(n=>n===t.payload);r!==-1&&e.dots.splice(r,1)},addArea:(e,t)=>{e.areas.push(t.payload)},removeArea:(e,t)=>{var r=Cr(e).areas.findIndex(n=>n===t.payload);r!==-1&&e.areas.splice(r,1)},addLine:(e,t)=>{e.lines.push($e(t.payload))},removeLine:(e,t)=>{var r=Cr(e).lines.findIndex(n=>n===t.payload);r!==-1&&e.lines.splice(r,1)}}}),rc=c6.actions,jhe=rc.addDot,Ihe=rc.removeDot,zhe=rc.addArea,Lhe=rc.removeArea,Bhe=rc.addLine,Uhe=rc.removeLine;var d6=c6.reducer;var kf=te(ae()),Of=te(ae());function LK(e,t){return HK(e)||$K(e,t)||UK(e,t)||BK()}function BK(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function UK(e,t){if(e){if(typeof e=="string")return f6(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?f6(e,t):void 0}}function f6(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{var t=e.children,r=(0,Of.useState)("".concat(si("recharts"),"-clip")),n=LK(r,1),a=n[0],i=tc();if(i==null)return null;var o=i.x,l=i.y,s=i.width,u=i.height;return kf.createElement(VK.Provider,{value:a},kf.createElement("defs",null,kf.createElement("clipPath",{id:a},kf.createElement("rect",{x:o,y:l,height:u,width:s}))),t)};var Et=te(ae()),Lr=te(ae());function m6(){}function Rw(e){if(!e||typeof e!="object")return!1;let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype||Object.getPrototypeOf(t)===null?Object.prototype.toString.call(e)==="[object Object]":!1}function v6(e,t,r){return Tf(e,t,void 0,void 0,void 0,void 0,r)}function Tf(e,t,r,n,a,i,o){let l=o(e,t,r,n,a,i);if(l!==void 0)return l;if(typeof e==typeof t)switch(typeof e){case"bigint":case"string":case"boolean":case"symbol":case"undefined":return e===t;case"number":return e===t||Object.is(e,t);case"function":return e===t;case"object":return Mf(e,t,i,o)}return Mf(e,t,i,o)}function Mf(e,t,r,n){if(Object.is(e,t))return!0;let a=ui(e),i=ui(t);if(a==="[object Arguments]"&&(a=nu),i==="[object Arguments]"&&(i=nu),a!==i)return!1;switch(a){case eu:return e.toString()===t.toString();case tu:return fo(e.valueOf(),t.valueOf());case ru:case Gm:case Km:return Object.is(e.valueOf(),t.valueOf());case Ym:return e.source===t.source&&e.flags===t.flags;case PE:return e===t}r=r??new Map;let o=r.get(e),l=r.get(t);if(o!=null&&l!=null)return o===t;r.set(e,t),r.set(t,e);try{switch(a){case Xm:if(e.size!==t.size)return!1;for(let[s,u]of e.entries())if(!t.has(s)||!Tf(u,t.get(s),s,e,t,r,n))return!1;return!0;case Zm:{if(e.size!==t.size)return!1;let s=Array.from(e.values()),u=Array.from(t.values());for(let c=0;cTf(d,p,void 0,e,t,r,n));if(f===-1)return!1;u.splice(f,1)}return!0}case Qm:case ev:case tv:case rv:case nv:case IE:case av:case iv:case ov:case zE:case lv:case sv:if(gd(e)!==gd(t)||e.length!==t.length)return!1;for(let s=0;se*a)return!1;var i=r();return e*(t-e*i/2-n)>=0&&e*(t+e*i/2-a)<=0}function y6(e,t){return eg(e,t+1)}function x6(e,t,r,n,a){for(var i=(n||[]).slice(),o=t.start,l=t.end,s=0,u=1,c=o,d=function(){var v=n?.[s];if(v===void 0)return{v:eg(n,u)};var h=s,b,m=()=>(b===void 0&&(b=r(v,h)),b),g=v.coordinate,x=s===0||Dl(e,g,m,c,l);x||(s=0,c=o,u+=1),x&&(c=g+e*(m()/2+a),s+=u)},f;u<=i.length;)if(f=d(),f)return f.v;return[]}function b6(e,t,r,n,a){var i=(n||[]).slice(),o=i.length;if(o===0)return[];for(var l=t.start,s=t.end,u=1;u<=o;u++){for(var c=(o-1)%u,d=l,f=!0,p=function(){var A=n[h];if(A==null)return 0;var k=h,_,M=()=>(_===void 0&&(_=r(A,k)),_),E=A.coordinate,L=h===c||Dl(e,E,M,d,s);if(!L)return f=!1,1;L&&(d=E+e*(M()/2+a))},v,h=c;h(h===void 0&&(h=r(p,f)),h);if(f===o-1){var m=e*(v.coordinate+e*b()/2-s);i[f]=v=zr(zr({},v),{},{tickCoord:m>0?v.coordinate-m*e:v.coordinate})}else i[f]=v=zr(zr({},v),{},{tickCoord:v.coordinate});if(v.tickCoord!=null){var g=Dl(e,v.tickCoord,b,l,s);g&&(s=v.tickCoord-e*(b()/2+a),i[f]=zr(zr({},v),{},{isShow:!0}))}},c=o-1;c>=0;c--)u(c);return i}function KK(e,t,r,n,a,i){var o=(n||[]).slice(),l=o.length,s=t.start,u=t.end;if(i){var c=n[l-1];if(c!=null){var d=r(c,l-1),f=e*(c.coordinate+e*d/2-u);if(o[l-1]=c=zr(zr({},c),{},{tickCoord:f>0?c.coordinate-f*e:c.coordinate}),c.tickCoord!=null){var p=Dl(e,c.tickCoord,()=>d,s,u);p&&(u=c.tickCoord-e*(d/2+a),o[l-1]=zr(zr({},c),{},{isShow:!0}))}}}for(var v=i?l-1:l,h=function(g){var x=o[g];if(x==null)return 1;var A=x,k,_=()=>(k===void 0&&(k=r(x,g)),k);if(g===0){var M=e*(A.coordinate-e*_()/2-s);o[g]=A=zr(zr({},A),{},{tickCoord:M<0?A.coordinate-M*e:A.coordinate})}else o[g]=A=zr(zr({},A),{},{tickCoord:A.coordinate});if(A.tickCoord!=null){var E=Dl(e,A.tickCoord,_,s,u);E&&(s=A.tickCoord+e*(_()/2+a),o[g]=zr(zr({},A),{},{isShow:!0}))}},b=0;b{var M=typeof u=="function"?u(k.value,_):k.value;return v==="width"?h6(kl(M,{fontSize:t,letterSpacing:r}),h,d):kl(M,{fontSize:t,letterSpacing:r})[v]},m=a[0],g=a[1],x=a.length>=2&&m!=null&&g!=null?ur(g.coordinate-m.coordinate):1,A=g6(i,x,v);return s==="equidistantPreserveStart"?x6(x,A,b,a,o):s==="equidistantPreserveEnd"?b6(x,A,b,a,o):(s==="preserveStart"||s==="preserveStartEnd"?p=KK(x,A,b,a,o,s==="preserveStartEnd"):p=WK(x,A,b,a,o),p.filter(k=>k.isShow))}var S6=e=>{var t=e.ticks,r=e.label,n=e.labelGapWithTick,a=n===void 0?5:n,i=e.tickSize,o=i===void 0?0:i,l=e.tickMargin,s=l===void 0?0:l,u=0;if(t){Array.from(t).forEach(p=>{if(p){var v=p.getBoundingClientRect();v.width>u&&(u=v.width)}});var c=r?r.getBoundingClientRect().width:0,d=o+s,f=u+d+c+(r?a:0);return Math.round(f)}return 0};var A6=e=>{var t=e.ticks,r=e.label,n=e.labelGapWithTick,a=n===void 0?5:n,i=e.tickSize,o=i===void 0?0:i,l=e.tickMargin,s=l===void 0?0:l,u=0;if(t){Array.from(t).forEach(p=>{if(p){var v=p.getBoundingClientRect();v.height>u&&(u=v.height)}});var c=r?r.getBoundingClientRect().height:0,d=o+s,f=u+d+c+(r?a:0);return Math.round(f)}return 0};var GK={xAxis:{},yAxis:{}},N6=rt({name:"renderedTicks",initialState:GK,reducers:{setRenderedTicks:(e,t)=>{var r=t.payload,n=r.axisType,a=r.axisId,i=r.ticks;e[n][a]=$e(i)},removeRenderedTicks:(e,t)=>{var r=t.payload,n=r.axisType,a=r.axisId;delete e[n][a]}}}),_6=N6.actions,C6=_6.setRenderedTicks,E6=_6.removeRenderedTicks;var k6=N6.reducer;var XK=["axisLine","width","height","className","hide","ticks","axisType","axisId"];function O6(e,t){return RK(e)||JK(e,t)||QK(e,t)||ZK()}function ZK(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function QK(e,t){if(e){if(typeof e=="string")return T6(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?T6(e,t):void 0}}function T6(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{if(!(n==null||r==null)){var o=t.map(s=>({value:s.value,coordinate:s.coordinate,offset:s.offset,index:s.index})),l=i.current;l!=null&&l.axisId===n&&l.axisType===r&&eS(l.ticks,o)||(i.current={ticks:o,axisId:n,axisType:r},a(C6({ticks:o,axisId:n,axisType:r})))}},[a,t,n,r]),(0,Lr.useEffect)(()=>n==null||r==null?sa:()=>{a(E6({axisId:n,axisType:r}))},[a,n,r]),null}var dG=(0,Lr.forwardRef)((e,t)=>{var r=e.ticks,n=r===void 0?[]:r,a=e.tick,i=e.tickLine,o=e.stroke,l=e.tickFormatter,s=e.unit,u=e.padding,c=e.tickTextProps,d=e.orientation,f=e.mirror,p=e.x,v=e.y,h=e.width,b=e.height,m=e.tickSize,g=e.tickMargin,x=e.fontSize,A=e.letterSpacing,k=e.getTicksConfig,_=e.events,M=e.axisType,E=e.axisId,L=Df(Vt(Vt({},k),{},{ticks:n}),x,A),I=hr(k),j=so(a),z=H0(I.textAnchor)?I.textAnchor:lG(d,f),X=sG(d,f),R={};typeof i=="object"&&(R=i);var Q=Vt(Vt({},I),{},{fill:"none"},R),y=L.map(D=>Vt({entry:D},oG(D,p,v,h,b,d,m,f,g))),C=y.map(D=>{var N=D.entry,T=D.line;return Et.createElement(lr,{className:"recharts-cartesian-axis-tick",key:"tick-".concat(N.value,"-").concat(N.coordinate,"-").concat(N.tickCoord)},i&&Et.createElement("line",Pl({},Q,T,{className:Le("recharts-cartesian-axis-tick-line",bn(i,"className"))})))}),O=y.map((D,N)=>{var T,$,Y=D.entry,J=D.tick,fe=Vt(Vt(Vt(Vt({verticalAnchor:X},I),{},{textAnchor:z,stroke:"none",fill:o},J),{},{index:N,payload:Y,visibleTicksCount:L.length,tickFormatter:l,padding:u},c),{},{angle:(T=($=c?.angle)!==null&&$!==void 0?$:I.angle)!==null&&T!==void 0?T:0}),pe=Vt(Vt({},fe),j);return Et.createElement(lr,Pl({className:"recharts-cartesian-axis-tick-label",key:"tick-label-".concat(Y.value,"-").concat(Y.coordinate,"-").concat(Y.tickCoord)},EE(_,Y,N)),a&&Et.createElement(uG,{option:a,tickProps:pe,value:"".concat(typeof l=="function"?l(Y.value,N):Y.value).concat(s||"")}))});return Et.createElement("g",{className:"recharts-cartesian-axis-ticks recharts-".concat(M,"-ticks")},Et.createElement(cG,{ticks:L,axisId:E,axisType:M}),O.length>0&&Et.createElement(Er,{zIndex:ct.label},Et.createElement("g",{className:"recharts-cartesian-axis-tick-labels recharts-".concat(M,"-tick-labels"),ref:t},O)),C.length>0&&Et.createElement("g",{className:"recharts-cartesian-axis-tick-lines recharts-".concat(M,"-tick-lines")},C))}),fG=(0,Lr.forwardRef)((e,t)=>{var r=e.axisLine,n=e.width,a=e.height,i=e.className,o=e.hide,l=e.ticks,s=e.axisType,u=e.axisId,c=eG(e,XK),d=(0,Lr.useState)(""),f=O6(d,2),p=f[0],v=f[1],h=(0,Lr.useState)(""),b=O6(h,2),m=b[0],g=b[1],x=(0,Lr.useRef)(null);(0,Lr.useImperativeHandle)(t,()=>({getCalculatedWidth:()=>{var k;return S6({ticks:x.current,label:(k=e.labelRef)===null||k===void 0?void 0:k.current,labelGapWithTick:5,tickSize:e.tickSize,tickMargin:e.tickMargin})},getCalculatedHeight:()=>{var k;return A6({ticks:x.current,label:(k=e.labelRef)===null||k===void 0?void 0:k.current,labelGapWithTick:5,tickSize:e.tickSize,tickMargin:e.tickMargin})}}));var A=(0,Lr.useCallback)(k=>{if(k){var _=k.getElementsByClassName("recharts-cartesian-axis-tick-value");x.current=_;var M=_[0];if(M){var E=window.getComputedStyle(M),L=E.fontSize,I=E.letterSpacing;(L!==p||I!==m)&&(v(L),g(I))}}},[p,m]);return o||n!=null&&n<=0||a!=null&&a<=0?null:Et.createElement(Er,{zIndex:e.zIndex},Et.createElement(lr,{className:Le("recharts-cartesian-axis",i)},Et.createElement(iG,{x:e.x,y:e.y,width:n,height:a,orientation:e.orientation,mirror:e.mirror,axisLine:r,otherSvgProps:hr(e)}),Et.createElement(dG,{ref:A,axisType:s,events:c,fontSize:p,getTicksConfig:e,height:e.height,letterSpacing:m,mirror:e.mirror,orientation:e.orientation,padding:e.padding,stroke:e.stroke,tick:e.tick,tickFormatter:e.tickFormatter,tickLine:e.tickLine,tickMargin:e.tickMargin,tickSize:e.tickSize,tickTextProps:e.tickTextProps,ticks:l,unit:e.unit,width:e.width,x:e.x,y:e.y,axisId:u}),Et.createElement(sD,{x:e.x,y:e.y,width:e.width,height:e.height,lowerWidth:e.width,upperWidth:e.width},Et.createElement(cD,{label:e.label,labelRef:e.labelRef}),e.children)))}),Pf=Et.forwardRef((e,t)=>{var r=Dt(e,Qn);return Et.createElement(fG,Pl({},r,{ref:t}))});Pf.displayName="CartesianAxis";var Ft=te(ae());var tg=te(ae());var D6={grid:{stroke:"#ccc",fill:"none"}};var P6=(0,tg.createContext)(D6),F0e=P6.Provider,j6=()=>(0,tg.useContext)(P6);var pG=["x1","y1","x2","y2","key"],mG=["offset"],vG=["xAxisId","yAxisId"],hG=["xAxisId","yAxisId"];function I6(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function Br(e){for(var t=1;t{var t=e.fill;if(!t||t==="none")return null;var r=e.fillOpacity,n=e.x,a=e.y,i=e.width,o=e.height,l=e.ry;return Ft.createElement("rect",{x:n,y:a,ry:l,width:i,height:o,stroke:"none",fill:t,fillOpacity:r,className:"recharts-cartesian-grid-bg"})};function z6(e){var t=e.option,r=e.lineItemProps,n;if(Ft.isValidElement(t))n=Ft.cloneElement(t,r);else if(typeof t=="function")n=t(r);else{var a,i=r.x1,o=r.y1,l=r.x2,s=r.y2,u=r.key,c=rg(r,pG),d=(a=hr(c))!==null&&a!==void 0?a:{},f=d.offset,p=rg(d,mG),v=Array.isArray(p.strokeDasharray)?p.strokeDasharray.join(","):p.strokeDasharray;n=Ft.createElement("line",jl({},p,{strokeDasharray:v,x1:i,y1:o,x2:l,y2:s,fill:"none",key:u}))}return n}function SG(e){var t=e.x,r=e.width,n=e.horizontal,a=n===void 0?!0:n,i=e.horizontalPoints;if(!a||!i||!i.length)return null;var o=e.xAxisId,l=e.yAxisId,s=rg(e,vG),u=i.map((c,d)=>{var f=Br(Br({},s),{},{x1:t,y1:c,x2:t+r,y2:c,key:"line-".concat(d),index:d});return Ft.createElement(z6,{key:"line-".concat(d),option:a,lineItemProps:f})});return Ft.createElement("g",{className:"recharts-cartesian-grid-horizontal"},u)}function AG(e){var t=e.y,r=e.height,n=e.vertical,a=n===void 0?!0:n,i=e.verticalPoints;if(!a||!i||!i.length)return null;var o=e.xAxisId,l=e.yAxisId,s=rg(e,hG),u=i.map((c,d)=>{var f=Br(Br({},s),{},{x1:c,y1:t,x2:c,y2:t+r,key:"line-".concat(d),index:d});return Ft.createElement(z6,{option:a,lineItemProps:f,key:"line-".concat(d)})});return Ft.createElement("g",{className:"recharts-cartesian-grid-vertical"},u)}function NG(e){var t=e.horizontalFill,r=e.fillOpacity,n=e.x,a=e.y,i=e.width,o=e.height,l=e.horizontalPoints,s=e.horizontal,u=s===void 0?!0:s;if(!u||!t||!t.length||l==null)return null;var c=l.map(f=>Math.round(f+a-a)).sort((f,p)=>f-p);a!==c[0]&&c.unshift(0);var d=c.map((f,p)=>{var v=c[p+1],h=v==null,b=h?a+o-f:v-f;if(b<=0)return null;var m=p%t.length;return Ft.createElement("rect",{key:"react-".concat(p),y:f,x:n,height:b,width:i,stroke:"none",fill:t[m],fillOpacity:r,className:"recharts-cartesian-grid-bg"})});return Ft.createElement("g",{className:"recharts-cartesian-gridstripes-horizontal"},d)}function _G(e){var t=e.vertical,r=t===void 0?!0:t,n=e.verticalFill,a=e.fillOpacity,i=e.x,o=e.y,l=e.width,s=e.height,u=e.verticalPoints;if(!r||!n||!n.length)return null;var c=u.map(f=>Math.round(f+i-i)).sort((f,p)=>f-p);i!==c[0]&&c.unshift(0);var d=c.map((f,p)=>{var v=c[p+1],h=v==null,b=h?i+l-f:v-f;if(b<=0)return null;var m=p%n.length;return Ft.createElement("rect",{key:"react-".concat(p),x:f,y:o,width:b,height:s,stroke:"none",fill:n[m],fillOpacity:a,className:"recharts-cartesian-grid-bg"})});return Ft.createElement("g",{className:"recharts-cartesian-gridstripes-vertical"},d)}var CG=(e,t)=>{var r=e.xAxis,n=e.width,a=e.height,i=e.offset;return C1(Df(Br(Br(Br({},Qn),r),{},{ticks:E1(r,!0),viewBox:{x:0,y:0,width:n,height:a}})),i.left,i.left+i.width,t)},EG=(e,t)=>{var r=e.yAxis,n=e.width,a=e.height,i=e.offset;return C1(Df(Br(Br(Br({},Qn),r),{},{ticks:E1(r,!0),viewBox:{x:0,y:0,width:n,height:a}})),i.top,i.top+i.height,t)},kG={horizontal:!0,vertical:!0,horizontalPoints:[],verticalPoints:[],verticalFill:[],horizontalFill:[],xAxisId:0,yAxisId:0,syncWithTicks:!1,zIndex:ct.grid};function Il(e){var t,r,n,a,i,o,l=Jv(),s=Rv(),u=Qv(),c=Br(Br({},Dt(e,kG)),{},{x:ye(e.x)?e.x:u.left,y:ye(e.y)?e.y:u.top,width:ye(e.width)?e.width:u.width,height:ye(e.height)?e.height:u.height}),d=c.xAxisId,f=c.yAxisId,p=c.x,v=c.y,h=c.width,b=c.height,m=c.syncWithTicks,g=c.horizontalValues,x=c.verticalValues,A=xt(),k=Se(C=>gw(C,"xAxis",d,A)),_=Se(C=>gw(C,"yAxis",f,A)),M=j6(),E={stroke:(t=c.stroke)!==null&&t!==void 0?t:M.grid.stroke,strokeWidth:(r=c.strokeWidth)!==null&&r!==void 0?r:M.grid.strokeWidth,strokeOpacity:(n=c.strokeOpacity)!==null&&n!==void 0?n:M.grid.strokeOpacity,strokeDasharray:(a=c.strokeDasharray)!==null&&a!==void 0?a:M.grid.strokeDasharray};if(!rn(h)||!rn(b)||!ye(p)||!ye(v))return null;var L=c.verticalCoordinatesGenerator||CG,I=c.horizontalCoordinatesGenerator||EG,j=c.horizontalPoints,z=c.verticalPoints;if((!j||!j.length)&&typeof I=="function"){var X=g&&g.length,R=I({yAxis:_?Br(Br({},_),{},{ticks:X?g:_.ticks}):void 0,width:l??h,height:s??b,offset:u},X?!0:m);fu(Array.isArray(R),"horizontalCoordinatesGenerator should return Array but instead it returned [".concat(typeof R,"]")),Array.isArray(R)&&(j=R)}if((!z||!z.length)&&typeof L=="function"){var Q=x&&x.length,y=L({xAxis:k?Br(Br({},k),{},{ticks:Q?x:k.ticks}):void 0,width:l??h,height:s??b,offset:u},Q?!0:m);fu(Array.isArray(y),"verticalCoordinatesGenerator should return Array but instead it returned [".concat(typeof y,"]")),Array.isArray(y)&&(z=y)}return Ft.createElement(Er,{zIndex:c.zIndex},Ft.createElement("g",{className:"recharts-cartesian-grid"},Ft.createElement(wG,{fill:(i=c.fill)!==null&&i!==void 0?i:M.grid.fill,fillOpacity:(o=c.fillOpacity)!==null&&o!==void 0?o:M.grid.fillOpacity,x:c.x,y:c.y,width:c.width,height:c.height,ry:c.ry}),Ft.createElement(NG,jl({},c,{horizontalPoints:j})),Ft.createElement(_G,jl({},c,{verticalPoints:z})),Ft.createElement(SG,jl({},c,E,{offset:u,horizontalPoints:j,xAxis:k,yAxis:_})),Ft.createElement(AG,jl({},c,E,{offset:u,verticalPoints:z,xAxis:k,yAxis:_}))))}Il.displayName="CartesianGrid";var OG={},L6=rt({name:"errorBars",initialState:OG,reducers:{addErrorBar:(e,t)=>{var r=t.payload,n=r.itemId,a=r.errorBar;e[n]||(e[n]=[]),e[n].push(a)},replaceErrorBar:(e,t)=>{var r=t.payload,n=r.itemId,a=r.prev,i=r.next;e[n]&&(e[n]=e[n].map(o=>o.dataKey===a.dataKey&&o.direction===a.direction?i:o))},removeErrorBar:(e,t)=>{var r=t.payload,n=r.itemId,a=r.errorBar;e[n]&&(e[n]=e[n].filter(i=>i.dataKey!==a.dataKey||i.direction!==a.direction))}}}),tS=L6.actions,sge=tS.addErrorBar,uge=tS.replaceErrorBar,cge=tS.removeErrorBar;var B6=L6.reducer;var rS=te(ae());function nS(e,t){var r,n,a=Se(u=>ja(u,e)),i=Se(u=>Ia(u,t)),o=(r=a?.allowDataOverflow)!==null&&r!==void 0?r:Gt.allowDataOverflow,l=(n=i?.allowDataOverflow)!==null&&n!==void 0?n:Xt.allowDataOverflow,s=o||l;return{needClip:s,needClipX:o,needClipY:l}}function U6(e){var t=e.xAxisId,r=e.yAxisId,n=e.clipPathId,a=tc(),i=nS(t,r),o=i.needClipX,l=i.needClipY,s=i.needClip,u=Se(x=>cw(x,t,!1)),c=Se(x=>dw(x,r,!1));if(!s||!a)return null;var d=a.x,f=a.y,p=a.width,v=a.height,h=o&&u?Math.min(u[0],u[1]):d-p/2,b=l&&c?Math.min(c[0],c[1]):f-v/2,m=o&&u?Math.abs(u[1]-u[0]):p*2,g=l&&c?Math.abs(c[1]-c[0]):v*2;return rS.createElement("clipPath",{id:"clipPath-".concat(n)},rS.createElement("rect",{x:h,y:b,width:m,height:g}))}function $6(e){var t=so(e),r=3,n=2;if(t!=null){var a=t.r,i=t.strokeWidth,o=Number(a),l=Number(i);return(Number.isNaN(o)||o<0)&&(o=r),(Number.isNaN(l)||l<0)&&(l=n),{r:o,strokeWidth:l}}return{r,strokeWidth:n}}var Ze=te(ae()),zl=te(ae());function ng(e,t){var r,n;return(r=(n=e.graphicalItems.cartesianItems.find(a=>a.id===t))===null||n===void 0?void 0:n.xAxisId)!==null&&r!==void 0?r:Zw}function ag(e,t){var r,n;return(r=(n=e.graphicalItems.cartesianItems.find(a=>a.id===t))===null||n===void 0?void 0:n.yAxisId)!==null&&r!==void 0?r:Zw}var H6=(e,t,r)=>xw(e,"xAxis",ng(e,t),r),V6=(e,t,r)=>yw(e,"xAxis",ng(e,t),r),F6=(e,t,r)=>xw(e,"yAxis",ag(e,t),r),q6=(e,t,r)=>yw(e,"yAxis",ag(e,t),r),TG=V([ot,H6,F6,V6,q6],(e,t,r,n,a)=>an(e,"xAxis")?su(t,n,!1):su(r,a,!1)),MG=(e,t)=>t,aS=V([H2,MG],(e,t)=>e.filter(r=>r.type==="area").find(r=>r.id===t)),Y6=e=>{var t=ot(e),r=an(t,"xAxis");return r?"yAxis":"xAxis"},DG=(e,t)=>{var r=Y6(e);return r==="yAxis"?ag(e,t):ng(e,t)},W6=(e,t,r)=>X2(e,Y6(e),DG(e,t),r),PG=V([aS,W6],(e,t)=>{var r;if(!(e==null||t==null)){var n=e.stackId,a=wu(e);if(!(n==null||a==null)){var i=(r=t[n])===null||r===void 0?void 0:r.stackedData,o=i?.find(l=>l.key===a);if(o!=null)return o.map(l=>[l[0],l[1]])}}}),jG=V([aS,W6],(e,t)=>{if(!(e==null||e.stackId==null||t==null)){var r=t[e.stackId];if(r!=null)return r.graphicalItems.map(n=>n.dataKey).filter(cr)}}),K6=V([ot,H6,F6,V6,q6,PG,d3,TG,aS,A3,jG],(e,t,r,n,a,i,o,l,s,u,c)=>{var d=o.chartData,f=o.dataStartIndex,p=o.dataEndIndex;if(!(s==null||e!=="horizontal"&&e!=="vertical"||t==null||r==null||n==null||a==null||n.length===0||a.length===0||l==null)){var v=s.data,h;if(v&&v.length>0?h=v:h=d?.slice(f,p+1),h!=null)return G6({layout:e,xAxis:t,yAxis:r,xAxisTicks:n,yAxisTicks:a,dataStartIndex:f,areaSettings:s,stackedData:i,displayedData:h,chartBaseValue:u,bandSize:l,stackDataKeys:c})}});var Ur=te(ae());var IG=["animationElapsedTime","isAnimating","isEntrance","layout","isRange","stroke","connectNulls"],zG=["id","baseLine"];function jf(){return jf=Object.assign?Object.assign.bind():function(e){for(var t=1;td.y||0));return ye(a)?c=Math.max(a,c):a&&Array.isArray(a)&&a.length&&(c=Math.max(...a.map(d=>d.y||0),c)),ye(c)?Ur.createElement("rect",{x:ld.x||0));return ye(a)?c=Math.max(a,c):a&&Array.isArray(a)&&a.length&&(c=Math.max(...a.map(d=>d.x||0),c)),ye(c)?Ur.createElement("rect",{x:0,y:le==null?[]:t===1?e.flatMap(r=>r.status==="removed"?[]:[r.next]):e.flatMap(r=>r.status==="matched"?[nc(nc({},r.next),{},{x:Fn(r.prev.x,r.next.x,t),y:Fn(r.prev.y,r.next.y,t)})]:r.status==="added"?[r.next]:[]),R6={activeDot:!0,animationBegin:0,animationDuration:1500,animationEasing:"ease",animationMatchBy:Af,animationInterpolateFn:KG,connectNulls:!1,dot:!1,fill:"#3182bd",fillOpacity:.6,hide:!1,isAnimationActive:"auto",legendType:"line",stroke:"#3182bd",strokeWidth:1,type:"linear",label:!1,shape:Z6,xAxisId:0,yAxisId:0,zIndex:ct.area};function og(e,t){return e&&e!=="none"?e:t}var GG=e=>{var t=e.dataKey,r=e.name,n=e.stroke,a=e.fill,i=e.legendType,o=e.hide;return[{inactive:o,dataKey:t,type:i,color:og(n,a),value:D1(r,t),payload:e}]},XG=Ze.memo(e=>{var t=e.dataKey,r=e.data,n=e.stroke,a=e.strokeWidth,i=e.fill,o=e.name,l=e.hide,s=e.unit,u=e.formatter,c=e.tooltipType,d=e.id,f={dataDefinedOnItem:r,getPosition:sa,settings:{stroke:n,strokeWidth:a,fill:i,dataKey:t,nameKey:void 0,name:D1(o,t),hide:l,type:c,color:og(n,i),unit:s,formatter:u,graphicalItemId:d}};return Ze.createElement(ND,{tooltipEntrySettings:f})});function ZG(e){var t=e.clipPathId,r=e.points,n=e.props,a=n.needClip,i=n.dot,o=n.dataKey,l=hr(n);return Ze.createElement(VD,{points:r,dot:i,className:"recharts-area-dots",dotClassName:"recharts-area-dot",dataKey:o,baseProps:l,needClip:a,clipPathId:t})}function QG(e){var t=e.showLabels,r=e.children,n=e.points,a=n.map(i=>{var o,l,s={x:(o=i.x)!==null&&o!==void 0?o:0,y:(l=i.y)!==null&&l!==void 0?l:0,width:0,lowerWidth:0,upperWidth:0,height:0};return nc(nc({},s),{},{value:i.value,payload:i.payload,parentViewBox:void 0,viewBox:s,fill:void 0})});return Ze.createElement(pD,{value:t?a:void 0},r)}function JG(e){var t=e.points,r=e.baseLine,n=e.needClip,a=e.clipPathId,i=e.props,o=e.animationElapsedTime,l=e.isAnimating,s=e.isEntrance,u=i.layout,c=i.type,d=i.stroke,f=i.connectNulls,p=i.isRange,v=i.shape,h=i.id,b=J6(i,HG),m=Wt(b),g=nc(nc({},m),{},{id:h,points:t,connectNulls:f,type:c,baseLine:r,layout:u,stroke:d,isRange:p,animationElapsedTime:o,isAnimating:l,isEntrance:s});return Ze.createElement(Ze.Fragment,null,t?.length>1&&Ze.createElement(lr,{clipPath:n?"url(#clipPath-".concat(a,")"):void 0},Ze.createElement(AD,{option:v,DefaultShape:R6.shape,shapeProps:g})),Ze.createElement(ZG,{points:t,props:b,clipPathId:a}))}function RG(e,t,r){if(ye(e)){var n=ye(t)?t:void 0;return Fn(n,e,r)}if(Mt(e)||Tr(e)){var a=ye(t)?t:void 0;return Fn(a,0,r)}return e}function eX(e){var t=e.needClip,r=e.clipPathId,n=e.props,a=e.previousPointsRef,i=e.previousBaselineRef,o=n.points,l=n.baseLine,s=n.isAnimationActive,u=n.animationBegin,c=n.animationDuration,d=n.animationEasing,f=n.animationMatchBy,p=n.animationInterpolateFn,v=(0,zl.useMemo)(()=>({points:o,baseLine:l}),[o,l]),h=K0(v,i),b=mu(),m=OD(n.onAnimationStart,n.onAnimationEnd),g=m.isAnimating,x=m.handleAnimationStart,A=m.handleAnimationEnd,k=h.startValue;if(b==null)return null;var _;return Array.isArray(l)&&Array.isArray(k)?_=Nf(k,l,f):Array.isArray(l)?_=Nf(null,l,f):_=null,Ze.createElement(TD,{animationInput:v,animationIdPrefix:"recharts-area-",items:o,previousItemsRef:a,isAnimationActive:s,animationBegin:u,animationDuration:c,animationEasing:d,onAnimationStart:x,onAnimationEnd:A,animationInterpolateFn:p,animationMatchBy:f,layout:b},(M,E,L)=>{var I;return E===1?I=l:Array.isArray(l)?I=p(_,E,b):I=L?l:RG(l,k,E),h.syncStepValue(I,E),Ze.createElement(QG,{showLabels:!g,points:o},n.children,Ze.createElement(JG,{points:M,baseLine:I,needClip:t,clipPathId:r,props:n,animationElapsedTime:E,isAnimating:g||E<1,isEntrance:L}),Ze.createElement(vD,{label:n.label}))})}function tX(e){var t=e.needClip,r=e.clipPathId,n=e.props,a=(0,zl.useRef)(null),i=(0,zl.useRef)();return Ze.createElement(eX,{needClip:t,clipPathId:r,props:n,previousPointsRef:a,previousBaselineRef:i})}var iS=class extends zl.PureComponent{render(){var t=this.props,r=t.hide,n=t.dot,a=t.points,i=t.className,o=t.top,l=t.left,s=t.needClip,u=t.xAxisId,c=t.yAxisId,d=t.width,f=t.height,p=t.id,v=t.baseLine,h=t.zIndex;if(r)return null;var b=Le("recharts-area",i),m=p,g=$6(n),x=g.r,A=g.strokeWidth,k=W0(n),_=x*2+A,M=s?"url(#clipPath-".concat(k?"":"dots-").concat(m,")"):void 0;return Ze.createElement(Er,{zIndex:h},Ze.createElement(lr,{className:b},s&&Ze.createElement("defs",null,Ze.createElement(U6,{clipPathId:m,xAxisId:u,yAxisId:c}),!k&&Ze.createElement("clipPath",{id:"clipPath-dots-".concat(m)},Ze.createElement("rect",{x:l-_/2,y:o-_/2,width:d+_,height:f+_}))),Ze.createElement(tX,{needClip:s,clipPathId:m,props:this.props})),Ze.createElement(Jw,{points:a,mainColor:og(this.props.stroke,this.props.fill),itemDataKey:this.props.dataKey,activeDot:this.props.activeDot,clipPath:M}),this.props.isRange&&Array.isArray(v)&&Ze.createElement(Jw,{points:v,mainColor:og(this.props.stroke,this.props.fill),itemDataKey:this.props.dataKey,activeDot:this.props.activeDot,clipPath:M}))}};function rX(e){var t,r=e.activeDot,n=e.animationBegin,a=e.animationDuration,i=e.animationEasing,o=e.connectNulls,l=e.dot,s=e.fill,u=e.fillOpacity,c=e.hide,d=e.isAnimationActive,f=e.legendType,p=e.stroke,v=e.xAxisId,h=e.yAxisId,b=J6(e,VG),m=Ea(),g=O0(),x=nS(v,h),A=x.needClip,k=xt(),_=(t=Se(Q=>K6(Q,e.id,k)))!==null&&t!==void 0?t:{},M=_.points,E=_.isRange,L=_.baseLine,I=tc();if(m!=="horizontal"&&m!=="vertical"||I==null||g!=="AreaChart"&&g!=="ComposedChart")return null;var j=I.height,z=I.width,X=I.x,R=I.y;return!M||!M.length?null:Ze.createElement(iS,ig({},b,{activeDot:r,animationBegin:n,animationDuration:a,animationEasing:i,baseLine:L,connectNulls:o,dot:l,fill:s,fillOpacity:u,height:j,hide:c,layout:m,isAnimationActive:d,isRange:E,legendType:f,needClip:A,points:M,stroke:p,width:z,left:X,top:R,xAxisId:v,yAxisId:h}))}var nX=(e,t,r,n,a)=>{var i=r??t;if(ye(i))return i;var o=e==="horizontal"?a:n,l=o.scale.domain();if(o.type==="number"){var s=Math.max(l[0],l[1]),u=Math.min(l[0],l[1]);return i==="dataMin"?u:i==="dataMax"||s<0?s:Math.max(Math.min(l[0],l[1]),0)}return i==="dataMin"?l[0]:i==="dataMax"?l[1]:l[0]};function G6(e){var t=e.areaSettings,r=t.connectNulls,n=t.baseValue,a=t.dataKey,i=e.stackedData,o=e.layout,l=e.chartBaseValue,s=e.xAxis,u=e.yAxis,c=e.displayedData,d=e.dataStartIndex,f=e.xAxisTicks,p=e.yAxisTicks,v=e.bandSize,h=e.stackDataKeys,b=i&&i.length,m=nX(o,l,n,s,u),g=o==="horizontal",x=!1,A=c.map((_,M)=>{var E,L,I,j;if(b)j=i[d+M];else{var z=_t(_,a);Array.isArray(z)?(j=z,x=!0):j=[m,z]}var X=(E=(L=j)===null||L===void 0?void 0:L[1])!==null&&E!==void 0?E:null,R=_t(_,a),Q=b&&R==null&&h!=null&&h.length>0&&h.every(O=>_t(_,O)==null),y=X==null||b&&!r&&R==null||Q;if(g){var C;return{x:k1({axis:s,ticks:f,bandSize:v,entry:_,index:M}),y:y?null:(C=u.scale.map(X))!==null&&C!==void 0?C:null,value:j,payload:_}}return{x:y?null:(I=s.scale.map(X))!==null&&I!==void 0?I:null,y:k1({axis:u,ticks:p,bandSize:v,entry:_,index:M}),value:j,payload:_}}),k;return b||x?k=A.map(_=>{var M,E=Array.isArray(_.value)?_.value[0]:null;if(g){var L;return{x:_.x,y:E!=null&&_.y!=null&&(L=u.scale.map(E))!==null&&L!==void 0?L:null,payload:_.payload}}return{x:E!=null&&(M=s.scale.map(E))!==null&&M!==void 0?M:null,y:_.y,payload:_.payload}}):k=g?u.scale.map(m):s.scale.map(m),{points:A,baseLine:k??0,isRange:x}}function aX(e){var t=Dt(e,R6),r=xt();return Ze.createElement(jD,{id:t.id,type:"area"},n=>Ze.createElement(Ze.Fragment,null,Ze.createElement(_D,{legendPayload:GG(t)}),Ze.createElement(XG,{dataKey:t.dataKey,data:t.data,stroke:t.stroke,strokeWidth:t.strokeWidth,fill:t.fill,name:t.name,hide:t.hide,unit:t.unit,formatter:t.formatter,tooltipType:t.tooltipType,id:n}),Ze.createElement($D,{type:"area",id:n,data:t.data,dataKey:t.dataKey,xAxisId:t.xAxisId,yAxisId:t.yAxisId,zAxisId:0,stackId:bO(t.stackId),hide:t.hide,barSize:void 0,baseValue:t.baseValue,isPanorama:r,connectNulls:t.connectNulls}),Ze.createElement(rX,ig({},t,{id:n}))))}var Ll=Ze.memo(aX,po);Ll.displayName="Area";var _i=te(ae()),Jn=te(ae());var iX=["domain","range"],oX=["domain","range"];function eP(e,t){if(e==null)return{};var r,n,a=lX(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n{if(o!=null)return nP(nP({},i),{},{type:o})},[i,o]);return(0,Jn.useLayoutEffect)(()=>{l!=null&&(r.current===null?t(YD(l)):r.current!==l&&t(WD({prev:r.current,next:l})),r.current=l)},[l,t]),(0,Jn.useLayoutEffect)(()=>()=>{r.current&&(t(KD(r.current)),r.current=null)},[t]),null}var hX=e=>{var t=e.xAxisId,r=e.className,n=e.height,a=e.label,i=(0,Jn.useRef)(null),o=(0,Jn.useRef)(null),l=Se(Gv),s=xt(),u=Ve(),c="xAxis",d=Se(_=>y0(_,c,t,s)),f=Se(_=>mM(_,t)),p=Se(_=>vM(_,t)),v=Se(_=>z2(_,t));if((0,Jn.useLayoutEffect)(()=>{if(!(n!=="auto"||!f||Tl(a)||(0,Jn.isValidElement)(a)||v==null)){var _=i.current;if(_){var M=_.getCalculatedHeight();Math.round(f.height)!==Math.round(M)&&u(JD({id:t,height:M}))}}},[d,f,u,a,t,n,v]),f==null||p==null||v==null)return null;var h=e.dangerouslySetInnerHTML,b=e.ticks,m=e.scale,g=lS(e,uX),x=v.id,A=v.scale,k=lS(v,cX);return _i.createElement(Pf,oS({},g,k,{ref:i,labelRef:o,x:p.x,y:p.y,width:f.width,height:f.height,className:Le("recharts-".concat(c," ").concat(c),r),viewBox:l,ticks:d,axisType:c,axisId:t}))},gX={allowDataOverflow:Gt.allowDataOverflow,allowDecimals:Gt.allowDecimals,allowDuplicatedCategory:Gt.allowDuplicatedCategory,angle:Gt.angle,axisLine:Qn.axisLine,height:Gt.height,hide:!1,includeHidden:Gt.includeHidden,interval:Gt.interval,label:!1,minTickGap:Gt.minTickGap,mirror:Gt.mirror,orientation:Gt.orientation,padding:Gt.padding,reversed:Gt.reversed,scale:Gt.scale,tick:Gt.tick,tickCount:Gt.tickCount,tickLine:Qn.tickLine,tickSize:Qn.tickSize,type:Gt.type,niceTicks:Gt.niceTicks,xAxisId:0},yX=e=>{var t=Dt(e,gX);return _i.createElement(_i.Fragment,null,_i.createElement(vX,{allowDataOverflow:t.allowDataOverflow,allowDecimals:t.allowDecimals,allowDuplicatedCategory:t.allowDuplicatedCategory,angle:t.angle,dataKey:t.dataKey,domain:t.domain,height:t.height,hide:t.hide,id:t.xAxisId,includeHidden:t.includeHidden,interval:t.interval,minTickGap:t.minTickGap,mirror:t.mirror,name:t.name,orientation:t.orientation,padding:t.padding,reversed:t.reversed,scale:t.scale,tick:t.tick,tickCount:t.tickCount,tickFormatter:t.tickFormatter,ticks:t.ticks,type:t.type,unit:t.unit,niceTicks:t.niceTicks}),_i.createElement(hX,t))},Bl=_i.memo(yX,lg);Bl.displayName="XAxis";var Ci=te(ae()),Rn=te(ae());var xX=["type"],bX=["dangerouslySetInnerHTML","ticks","scale"],wX=["id","scale"];function sS(){return sS=Object.assign?Object.assign.bind():function(e){for(var t=1;t{if(o!=null)return iP(iP({},i),{},{type:o})},[o,i]);return(0,Rn.useLayoutEffect)(()=>{l!=null&&(r.current===null?t(GD(l)):r.current!==l&&t(XD({prev:r.current,next:l})),r.current=l)},[l,t]),(0,Rn.useLayoutEffect)(()=>()=>{r.current&&(t(ZD(r.current)),r.current=null)},[t]),null}function EX(e){var t=e.yAxisId,r=e.className,n=e.width,a=e.label,i=(0,Rn.useRef)(null),o=(0,Rn.useRef)(null),l=Se(Gv),s=xt(),u=Ve(),c="yAxis",d=Se(_=>gM(_,t)),f=Se(_=>hM(_,t)),p=Se(_=>y0(_,c,t,s)),v=Se(_=>L2(_,t));if((0,Rn.useLayoutEffect)(()=>{if(!(n!=="auto"||!d||Tl(a)||(0,Rn.isValidElement)(a)||v==null)){var _=i.current;if(_){var M=_.getCalculatedWidth();Math.round(d.width)!==Math.round(M)&&u(QD({id:t,width:M}))}}},[p,d,u,a,t,n,v]),d==null||f==null||v==null)return null;var h=e.dangerouslySetInnerHTML,b=e.ticks,m=e.scale,g=uS(e,bX),x=v.id,A=v.scale,k=uS(v,wX);return Ci.createElement(Pf,sS({},g,k,{ref:i,labelRef:o,x:f.x,y:f.y,tickTextProps:n==="auto"?{width:void 0}:{width:n},width:d.width,height:d.height,className:Le("recharts-".concat(c," ").concat(c),r),viewBox:l,ticks:p,axisType:c,axisId:t}))}var kX={allowDataOverflow:Xt.allowDataOverflow,allowDecimals:Xt.allowDecimals,allowDuplicatedCategory:Xt.allowDuplicatedCategory,angle:Xt.angle,axisLine:Qn.axisLine,hide:!1,includeHidden:Xt.includeHidden,interval:Xt.interval,label:!1,minTickGap:Xt.minTickGap,mirror:Xt.mirror,orientation:Xt.orientation,padding:Xt.padding,reversed:Xt.reversed,scale:Xt.scale,tick:Xt.tick,tickCount:Xt.tickCount,tickLine:Qn.tickLine,tickSize:Qn.tickSize,type:Xt.type,niceTicks:Xt.niceTicks,width:Xt.width,yAxisId:0},OX=e=>{var t=Dt(e,kX);return Ci.createElement(Ci.Fragment,null,Ci.createElement(CX,{interval:t.interval,id:t.yAxisId,scale:t.scale,type:t.type,domain:t.domain,allowDataOverflow:t.allowDataOverflow,dataKey:t.dataKey,allowDuplicatedCategory:t.allowDuplicatedCategory,allowDecimals:t.allowDecimals,tickCount:t.tickCount,padding:t.padding,includeHidden:t.includeHidden,reversed:t.reversed,ticks:t.ticks,width:t.width,orientation:t.orientation,mirror:t.mirror,hide:t.hide,unit:t.unit,name:t.name,angle:t.angle,minTickGap:t.minTickGap,tick:t.tick,tickFormatter:t.tickFormatter,niceTicks:t.niceTicks}),Ci.createElement(EX,t))},Ul=Ci.memo(OX,lg);Ul.displayName="YAxis";var Yl=te(ae()),IP=te(ae());var xP=te(ae()),bP=te(ae());var TX=(e,t)=>t,If=V([TX,ot,Ch,Kt,_w,sn,ZM,Ct],r8);function MX(e){return"getBBox"in e.currentTarget&&typeof e.currentTarget.getBBox=="function"}function zf(e){var t=e.currentTarget.getBoundingClientRect(),r,n;if(MX(e)){var a=e.currentTarget.getBBox();r=a.width>0?t.width/a.width:1,n=a.height>0?t.height/a.height:1}else{var i=e.currentTarget;r=i.offsetWidth>0?t.width/i.offsetWidth:1,n=i.offsetHeight>0?t.height/i.offsetHeight:1}var o=(l,s)=>({relativeX:Math.round((l-t.left)/r),relativeY:Math.round((s-t.top)/n)});return"touches"in e?Array.from(e.touches).map(l=>o(l.clientX,l.clientY)):o(e.clientX,e.clientY)}var dS=tr("mouseClick"),fS=pi();fS.startListening({actionCreator:dS,effect:(e,t)=>{var r=e.payload,n=If(t.getState(),zf(r));n?.activeIndex!=null&&t.dispatch(EM({activeIndex:n.activeIndex,activeDataKey:void 0,activeCoordinate:n.activeCoordinate}))}});var sg=tr("mouseMove"),pS=pi(),ac=null,$l=null,cS=null;pS.startListening({actionCreator:sg,effect:(e,t)=>{var r=e.payload,n=t.getState(),a=n.eventSettings,i=a.throttleDelay,o=a.throttledEvents,l=o==="all"||o?.includes("mousemove");ac!==null&&(cancelAnimationFrame(ac),ac=null),$l!==null&&(typeof i!="number"||!l)&&(clearTimeout($l),$l=null),cS=zf(r);var s=()=>{var u=t.getState(),c=So(u,u.tooltip.settings.shared);if(!cS){ac=null,$l=null;return}if(c==="axis"){var d=If(u,cS);d?.activeIndex!=null?t.dispatch(w0({activeIndex:d.activeIndex,activeDataKey:void 0,activeCoordinate:d.activeCoordinate})):t.dispatch(b0())}ac=null,$l=null};if(!l){s();return}i==="raf"?ac=requestAnimationFrame(s):typeof i=="number"&&$l===null&&($l=setTimeout(s,i))}});function oP(e,t){return t instanceof HTMLElement?"HTMLElement <".concat(t.tagName,' class="').concat(t.className,'">'):t===window?"global.window":e==="children"&&typeof t=="object"&&t!==null?"<>":t}var lP={accessibilityLayer:!0,barCategoryGap:"10%",barGap:4,barSize:void 0,className:void 0,maxBarSize:void 0,stackOffset:"none",syncId:void 0,syncMethod:"index",baseValue:void 0,reverseStackOrder:!1},sP=rt({name:"rootProps",initialState:lP,reducers:{updateOptions:(e,t)=>{var r;e.accessibilityLayer=t.payload.accessibilityLayer,e.barCategoryGap=t.payload.barCategoryGap,e.barGap=(r=t.payload.barGap)!==null&&r!==void 0?r:lP.barGap,e.barSize=t.payload.barSize,e.maxBarSize=t.payload.maxBarSize,e.stackOffset=t.payload.stackOffset,e.syncId=t.payload.syncId,e.syncMethod=t.payload.syncMethod,e.className=t.payload.className,e.baseValue=t.payload.baseValue,e.reverseStackOrder=t.payload.reverseStackOrder}}}),uP=sP.reducer,cP=sP.actions.updateOptions;var DX=null,PX={updatePolarOptions:(e,t)=>e===null?t.payload:(e.startAngle=t.payload.startAngle,e.endAngle=t.payload.endAngle,e.cx=t.payload.cx,e.cy=t.payload.cy,e.innerRadius=t.payload.innerRadius,e.outerRadius=t.payload.outerRadius,e)},dP=rt({name:"polarOptions",initialState:DX,reducers:PX}),uxe=dP.actions.updatePolarOptions;var fP=dP.reducer;var mS=tr("keyDown"),vS=tr("focus"),hS=tr("blur"),Lf=pi(),ic=null,Hl=null,ug=null;Lf.startListening({actionCreator:mS,effect:(e,t)=>{ug=e.payload,ic!==null&&(cancelAnimationFrame(ic),ic=null);var r=t.getState(),n=r.eventSettings,a=n.throttleDelay,i=n.throttledEvents,o=i==="all"||i.includes("keydown");Hl!==null&&(typeof a!="number"||!o)&&(clearTimeout(Hl),Hl=null);var l=()=>{try{var s=t.getState(),u=s.rootProps.accessibilityLayer!==!1;if(!u)return;var c=s.tooltip.keyboardInteraction,d=ug;if(d!=="ArrowRight"&&d!=="ArrowLeft"&&d!=="Enter")return;var f=Ao(c,wi(s),yi(s),_o(s)),p=f==null?-1:Number(f),v=!Number.isFinite(p)||p<0,h=sn(s),b=wi(s),m=So(s,s.tooltip.settings.shared);if(d==="Enter"){if(v)return;var g=hf(s,m,"hover",String(c.index));t.dispatch(mf({active:!c.active,activeIndex:c.index,activeCoordinate:g}));return}var x=yM(s),A=x==="left-to-right"?1:-1,k=d==="ArrowRight"?1:-1,_;if(v){var M=yi(s),E=_o(s),L=k*A,I=Q=>({active:!1,index:String(Q),dataKey:void 0,graphicalItemId:void 0,coordinate:void 0});if(_=-1,L>0){for(var j=0;j=0;z--)if(Ao(I(z),b,M,E)!=null){_=z;break}if(_<0)return}else{_=p+k*A;var X=h?.length||b.length;if(X===0||_>=X||_<0)return}var R=hf(s,m,"hover",String(_));t.dispatch(mf({active:!0,activeIndex:_.toString(),activeCoordinate:R}))}finally{ic=null,Hl=null}};if(!o){l();return}a==="raf"?ic=requestAnimationFrame(l):typeof a=="number"&&Hl===null&&(l(),ug=null,Hl=setTimeout(()=>{ug?l():(Hl=null,ic=null)},a))}});Lf.startListening({actionCreator:vS,effect:(e,t)=>{var r=t.getState(),n=r.rootProps.accessibilityLayer!==!1;if(n){var a=r.tooltip.keyboardInteraction;if(!a.active&&a.index==null){var i="0",o=So(r,r.tooltip.settings.shared),l=hf(r,o,"hover",String(i));t.dispatch(mf({active:!0,activeIndex:i,activeCoordinate:l}))}}}});Lf.startListening({actionCreator:hS,effect:(e,t)=>{var r=t.getState(),n=r.rootProps.accessibilityLayer!==!1;if(n){var a=r.tooltip.keyboardInteraction;a.active&&t.dispatch(mf({active:!1,activeIndex:a.index,activeCoordinate:a.coordinate}))}}});function cg(e){e.persist();var t=e.currentTarget;return new Proxy(e,{get:(r,n)=>{if(n==="currentTarget")return t;var a=Reflect.get(r,n);return typeof a=="function"?a.bind(r):a}})}var Tn=tr("externalEvent"),yS=pi(),dg=new Map,Bf=new Map,gS=new Map;yS.startListening({actionCreator:Tn,effect:(e,t)=>{var r=e.payload,n=r.handler,a=r.reactEvent;if(n!=null){var i=a.type,o=cg(a);gS.set(i,{handler:n,reactEvent:o});var l=dg.get(i);l!==void 0&&(cancelAnimationFrame(l),dg.delete(i));var s=t.getState(),u=s.eventSettings,c=u.throttleDelay,d=u.throttledEvents,f=d,p=f==="all"||f?.includes(i),v=Bf.get(i);v!==void 0&&(typeof c!="number"||!p)&&(clearTimeout(v),Bf.delete(i));var h=()=>{var g=gS.get(i);try{if(!g)return;var x=g.handler,A=g.reactEvent,k=t.getState(),_={activeCoordinate:Ow(k),activeDataKey:BM(k),activeIndex:El(k),activeLabel:k0(k),activeTooltipIndex:El(k),isTooltipActive:Tw(k)};x&&x(_,A)}finally{dg.delete(i),Bf.delete(i),gS.delete(i)}};if(!p){h();return}if(c==="raf"){var b=requestAnimationFrame(h);dg.set(i,b)}else if(typeof c=="number"){if(!Bf.has(i)){h();var m=setTimeout(h,c);Bf.set(i,m)}}else h()}}});var jX=V([bi],e=>e.tooltipItemPayloads),pP=V([jX,(e,t)=>t,(e,t,r)=>r],(e,t,r)=>{if(t!=null){var n=e.find(i=>i.settings.graphicalItemId===r);if(n!=null){var a=n.getPosition;if(a!=null)return a(t)}}});var xS=tr("touchMove"),bS=pi(),Vl=null,Eo=null,mP=null,Uf=null;bS.startListening({actionCreator:xS,effect:(e,t)=>{var r=e.payload;if(!(r.touches==null||r.touches.length===0)){Uf=cg(r);var n=t.getState(),a=n.eventSettings,i=a.throttleDelay,o=a.throttledEvents,l=o==="all"||o.includes("touchmove");Vl!==null&&(cancelAnimationFrame(Vl),Vl=null),Eo!==null&&(typeof i!="number"||!l)&&(clearTimeout(Eo),Eo=null),mP=Array.from(r.touches).map(u=>zf({clientX:u.clientX,clientY:u.clientY,currentTarget:r.currentTarget}));var s=()=>{if(Uf!=null){var u=t.getState(),c=So(u,u.tooltip.settings.shared);if(c==="axis"){var d,f=(d=mP)===null||d===void 0?void 0:d[0];if(f==null){Vl=null,Eo=null;return}var p=If(u,f);p?.activeIndex!=null&&t.dispatch(w0({activeIndex:p.activeIndex,activeDataKey:void 0,activeCoordinate:p.activeCoordinate}))}else if(c==="item"){var v,h=Uf.touches[0];if(document.elementFromPoint==null||h==null)return;var b=document.elementFromPoint(h.clientX,h.clientY);if(!b||!b.getAttribute)return;var m=b.getAttribute(_O),g=(v=b.getAttribute(CO))!==null&&v!==void 0?v:void 0,x=No(u).find(_=>_.id===g);if(m==null||x==null||g==null)return;var A=x.dataKey,k=pP(u,m,g);t.dispatch(CM({activeDataKey:A,activeIndex:m,activeCoordinate:k,activeGraphicalItemId:g}))}Vl=null,Eo=null}};if(!l){s();return}i==="raf"?Vl=requestAnimationFrame(s):typeof i=="number"&&Eo===null&&(s(),Uf=null,Eo=setTimeout(()=>{Uf?s():(Eo=null,Vl=null)},i))}}});var wS={throttleDelay:"raf",throttledEvents:["mousemove","touchmove","pointermove","scroll","wheel"]},vP=rt({name:"eventSettings",initialState:wS,reducers:{setEventSettings:(e,t)=>{t.payload.throttleDelay!=null&&(e.throttleDelay=t.payload.throttleDelay),t.payload.throttledEvents!=null&&(e.throttledEvents=$e(t.payload.throttledEvents))}}}),hP=vP.actions.setEventSettings;var gP=vP.reducer;var IX=wv({brush:s6,cartesianAxis:RD,chartData:E8,errorBars:B6,eventSettings:gP,graphicalItems:UD,layout:hO,legend:qO,options:S8,polarAxis:yD,polarOptions:fP,referenceElements:d6,renderedTicks:k6,rootProps:uP,tooltip:kM,zIndex:d8}),yP=function(t){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:"Chart";return Jk({reducer:IX,preloadedState:t,middleware:n=>{var a;return n({serializableCheck:!1,immutableCheck:!["commonjs","es6","production"].includes((a="es6")!==null&&a!==void 0?a:"")}).concat([fS.middleware,pS.middleware,Lf.middleware,yS.middleware,bS.middleware])},enhancers:n=>{var a=n;return typeof n=="function"&&(a=n()),a.concat(S1({type:"raf"}))},devTools:Yn.devToolsEnabled&&{serialize:{replacer:oP},name:"recharts-".concat(r)}})};function wP(e){var t=e.preloadedState,r=e.children,n=e.reduxStoreName,a=xt(),i=(0,bP.useRef)(null);if(a)return r;i.current==null&&(i.current=yP(t,n));var o=yd;return xP.createElement(JO,{context:o,store:i.current},r)}var fg=te(ae());function zX(e){var t=e.layout,r=e.margin,n=Ve(),a=xt();return(0,fg.useEffect)(()=>{a||(n(pO(t)),n(_1(r)))},[n,a,t,r]),null}var SP=(0,fg.memo)(zX,po);var AP=te(ae());function NP(e){var t=Ve();return(0,AP.useEffect)(()=>{t(cP(e))},[t,e]),null}var pg=te(ae());var LX=e=>{var t=Ve();return(0,pg.useEffect)(()=>{t(hP(e))},[t,e]),null},_P=(0,pg.memo)(LX,po);var Ei=te(ae()),DP=te(ae());var ql=te(ae()),AS=te(ae());var Fl=te(ae()),mg=te(ae());function CP(e){var t=e.zIndex,r=e.isPanorama,n=(0,mg.useRef)(null),a=Ve();return(0,mg.useLayoutEffect)(()=>(n.current&&a(u8({zIndex:t,element:n.current,isPanorama:r})),()=>{a(c8({zIndex:t,isPanorama:r}))}),[a,t,r]),Fl.createElement("g",{tabIndex:-1,ref:n,className:"recharts-zIndex-layer_".concat(t)})}function SS(e){var t=e.children,r=e.isPanorama,n=Se(a8);if(!n||n.length===0)return t;var a=n.filter(o=>o<0),i=n.filter(o=>o>0);return Fl.createElement(Fl.Fragment,null,a.map(o=>Fl.createElement(CP,{key:o,zIndex:o,isPanorama:r})),t,i.map(o=>Fl.createElement(CP,{key:o,zIndex:o,isPanorama:r})))}var BX=["children"];function UX(e,t){if(e==null)return{};var r,n,a=$X(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n{var r=Jv(),n=Rv(),a=rh();if(!rn(r)||!rn(n))return null;var i=e.children,o=e.otherAttributes,l=e.title,s=e.desc,u,c;return o!=null&&(typeof o.tabIndex=="number"?u=o.tabIndex:u=a?0:void 0,typeof o.role=="string"?c=o.role:c=a?"application":void 0),ql.createElement(Cb,vg({},o,{title:l,desc:s,role:c,tabIndex:u,width:r,height:n,style:HX,ref:t}),i)}),FX=e=>{var t=e.children,r=Se(il);if(!r)return null;var n=r.width,a=r.height,i=r.y,o=r.x;return ql.createElement(Cb,{width:n,height:a,x:o,y:i},t)},NS=(0,AS.forwardRef)((e,t)=>{var r=e.children,n=UX(e,BX),a=xt();return a?ql.createElement(FX,null,ql.createElement(SS,{isPanorama:!0},r)):ql.createElement(VX,vg({ref:t},n),ql.createElement(SS,{isPanorama:!1},r))});var jt=te(ae()),We=te(ae());var hg=te(ae());function qX(e,t){return GX(e)||KX(e,t)||WX(e,t)||YX()}function YX(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function WX(e,t){if(e){if(typeof e=="string")return EP(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?EP(e,t):void 0}}function EP(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{if(n!=null){var o=n.getBoundingClientRect(),l=o.width/n.offsetWidth;Ie(l)&&l!==i&&e(vO(l))}},[n,e,i]),a}function OP(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),r.push.apply(r,n)}return r}function XX(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r(O8(),null);function yg(e){if(typeof e=="number")return e;if(typeof e=="string"){var t=parseFloat(e);if(!Number.isNaN(t))return t}return 0}var aZ=(0,We.forwardRef)((e,t)=>{var r,n,a=(0,We.useRef)(null),i=(0,We.useState)({containerWidth:yg((r=e.style)===null||r===void 0?void 0:r.width),containerHeight:yg((n=e.style)===null||n===void 0?void 0:n.height)}),o=gg(i,2),l=o[0],s=o[1],u=(0,We.useCallback)((d,f)=>{s(p=>{var v=Math.round(d),h=Math.round(f);return p.containerWidth===v&&p.containerHeight===h?p:{containerWidth:v,containerHeight:h}})},[]),c=(0,We.useCallback)(d=>{if(typeof t=="function"&&t(d),a.current!=null&&(a.current.disconnect(),a.current=null),d!=null&&typeof ResizeObserver<"u"){var f=d.getBoundingClientRect(),p=f.width,v=f.height;u(p,v);var h=m=>{var g=m[0];if(g!=null){var x=g.contentRect,A=x.width,k=x.height;u(A,k)}},b=new ResizeObserver(h);b.observe(d),a.current=b}},[t,u]);return(0,We.useEffect)(()=>()=>{var d=a.current;d?.disconnect()},[u]),jt.createElement(jt.Fragment,null,jt.createElement(sl,{width:l.containerWidth,height:l.containerHeight}),jt.createElement("div",ko({ref:c},e)))}),iZ=(0,We.forwardRef)((e,t)=>{var r=e.width,n=e.height,a=(0,We.useState)({containerWidth:yg(r),containerHeight:yg(n)}),i=gg(a,2),o=i[0],l=i[1],s=(0,We.useCallback)((c,d)=>{l(f=>{var p=Math.round(c),v=Math.round(d);return f.containerWidth===p&&f.containerHeight===v?f:{containerWidth:p,containerHeight:v}})},[]),u=(0,We.useCallback)(c=>{if(typeof t=="function"&&t(c),c!=null){var d=c.getBoundingClientRect(),f=d.width,p=d.height;s(f,p)}},[t,s]);return jt.createElement(jt.Fragment,null,jt.createElement(sl,{width:o.containerWidth,height:o.containerHeight}),jt.createElement("div",ko({ref:u},e)))}),oZ=(0,We.forwardRef)((e,t)=>{var r=e.width,n=e.height;return jt.createElement(jt.Fragment,null,jt.createElement(sl,{width:r,height:n}),jt.createElement("div",ko({ref:t},e)))}),lZ=(0,We.forwardRef)((e,t)=>{var r=e.width,n=e.height;return typeof r=="string"||typeof n=="string"?jt.createElement(iZ,ko({},e,{ref:t})):typeof r=="number"&&typeof n=="number"?jt.createElement(oZ,ko({},e,{width:r,height:n,ref:t})):jt.createElement(jt.Fragment,null,jt.createElement(sl,{width:r,height:n}),jt.createElement("div",ko({ref:t},e)))});function sZ(e){return e?aZ:lZ}var MP=(0,We.forwardRef)((e,t)=>{var r=e.children,n=e.className,a=e.height,i=e.onClick,o=e.onContextMenu,l=e.onDoubleClick,s=e.onMouseDown,u=e.onMouseEnter,c=e.onMouseLeave,d=e.onMouseMove,f=e.onMouseUp,p=e.onTouchEnd,v=e.onTouchMove,h=e.onTouchStart,b=e.style,m=e.width,g=e.responsive,x=e.dispatchTouchEvents,A=x===void 0?!0:x,k=(0,We.useRef)(null),_=Ve(),M=(0,We.useState)(null),E=gg(M,2),L=E[0],I=E[1],j=(0,We.useState)(null),z=gg(j,2),X=z[0],R=z[1],Q=kP(),y=kd(),C=y?.width>0?y.width:m,O=y?.height>0?y.height:a,D=(0,We.useCallback)(ie=>{Q(ie),typeof t=="function"&&t(ie),I(ie),R(ie),ie!=null&&(k.current=ie)},[Q,t,I,R]),N=(0,We.useCallback)(ie=>{_(dS(ie)),_(Tn({handler:i,reactEvent:ie}))},[_,i]),T=(0,We.useCallback)(ie=>{_(sg(ie)),_(Tn({handler:u,reactEvent:ie}))},[_,u]),$=(0,We.useCallback)(ie=>{_(b0()),_(Tn({handler:c,reactEvent:ie}))},[_,c]),Y=(0,We.useCallback)(ie=>{_(sg(ie)),_(Tn({handler:d,reactEvent:ie}))},[_,d]),J=(0,We.useCallback)(()=>{_(vS())},[_]),fe=(0,We.useCallback)(()=>{_(hS())},[_]),pe=(0,We.useCallback)(ie=>{_(mS(ie.key))},[_]),ge=(0,We.useCallback)(ie=>{_(Tn({handler:o,reactEvent:ie}))},[_,o]),ze=(0,We.useCallback)(ie=>{_(Tn({handler:l,reactEvent:ie}))},[_,l]),se=(0,We.useCallback)(ie=>{_(Tn({handler:s,reactEvent:ie}))},[_,s]),Ne=(0,We.useCallback)(ie=>{_(Tn({handler:f,reactEvent:ie}))},[_,f]),F=(0,We.useCallback)(ie=>{_(Tn({handler:h,reactEvent:ie}))},[_,h]),H=(0,We.useCallback)(ie=>{A&&_(xS(ie)),_(Tn({handler:v,reactEvent:ie}))},[_,A,v]),q=(0,We.useCallback)(ie=>{_(Tn({handler:p,reactEvent:ie}))},[_,p]),ee=sZ(g);return jt.createElement(Bw.Provider,{value:L},jt.createElement(dE.Provider,{value:X},jt.createElement(ee,{width:C??b?.width,height:O??b?.height,className:Le("recharts-wrapper",n),style:XX({position:"relative",cursor:"default",width:C,height:O},b),onClick:N,onContextMenu:ge,onDoubleClick:ze,onFocus:J,onBlur:fe,onKeyDown:pe,onMouseDown:se,onMouseEnter:T,onMouseLeave:$,onMouseMove:Y,onMouseUp:Ne,onTouchEnd:q,onTouchMove:H,onTouchStart:F,ref:D},jt.createElement(nZ,null),r)))});var uZ=["width","height","responsive","children","className","style","compact","title","desc"];function cZ(e,t){if(e==null)return{};var r,n,a=dZ(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n{var r=e.width,n=e.height,a=e.responsive,i=e.children,o=e.className,l=e.style,s=e.compact,u=e.title,c=e.desc,d=cZ(e,uZ),f=hr(d);return s?Ei.createElement(Ei.Fragment,null,Ei.createElement(sl,{width:r,height:n}),Ei.createElement(NS,{otherAttributes:f,title:u,desc:c},i)):Ei.createElement(MP,{className:o,style:l,width:r,height:n,responsive:a??!1,onClick:e.onClick,onMouseLeave:e.onMouseLeave,onMouseEnter:e.onMouseEnter,onMouseMove:e.onMouseMove,onMouseDown:e.onMouseDown,onMouseUp:e.onMouseUp,onContextMenu:e.onContextMenu,onDoubleClick:e.onDoubleClick,onTouchStart:e.onTouchStart,onTouchMove:e.onTouchMove,onTouchEnd:e.onTouchEnd},Ei.createElement(NS,{otherAttributes:f,title:u,desc:c,ref:t},Ei.createElement(p6,null,i)))});function _S(){return _S=Object.assign?Object.assign.bind():function(e){for(var t=1;tLP.createElement(zP,{chartName:"AreaChart",defaultTooltipEventType:"axis",validateTooltipEventTypes:yZ,tooltipPayloadSearcher:b8,categoricalChartProps:e,ref:t}));var _e=te(ae());var ea=te(ae()),xZ=/(\*\*[^*]+\*\*|\*[^*\n]+\*|`[^`]+`|\[[^\]]+\]\((?:https?:\/\/|\/)[^)\s]+\))/g,CS=(e,t)=>e.split(xZ).filter(Boolean).map((r,n)=>{let a=`${t}-${n}`;if(r.startsWith("**")&&r.endsWith("**"))return ea.default.createElement("strong",{key:a},r.slice(2,-2));if(r.startsWith("*")&&r.endsWith("*")&&r.length>2)return ea.default.createElement("em",{key:a},r.slice(1,-1));if(r.startsWith("`")&&r.endsWith("`"))return ea.default.createElement("code",{key:a,className:"px-1 py-0.5 rounded text-[0.9em]",style:{background:"rgba(127,127,127,0.15)"}},r.slice(1,-1));let i=r.match(/^\[([^\]]+)\]\(((?:https?:\/\/|\/)[^)\s]+)\)$/);return i?ea.default.createElement("a",{key:a,href:i[2],target:"_blank",rel:"noopener noreferrer",className:"underline text-blue-500"},i[1]):r});function ES({text:e}){if(!e)return null;let t=[],r=String(e).split(` +`),n=0,a=0;for(;nea.default.createElement("li",{key:u},CS(s,`li${a}-${u}`)))));continue}if(i.trim()===""){t.push(ea.default.createElement("div",{key:a++,className:"h-2"})),n+=1;continue}t.push(ea.default.createElement("div",{key:a++},CS(i,`p${a}`))),n+=1}return ea.default.createElement("div",{className:"break-words"},t)}var un=te(ae()),bZ=e=>{let t=e.mcp_server||e.server;return t?`MCP server \xB7 ${t}`:typeof e.name=="string"&&e.name.startsWith("mcp__")?`MCP server \xB7 ${e.name.split("__")[1]}`:e.gem?`gem \xB7 ${e.gem}`:e.source?String(e.source):"agent-defined \xB7 method on the agent class"},wZ=e=>Array.isArray(e.parameters)?e.parameters:e.parameters&&typeof e.parameters=="object"?Object.keys(e.parameters.properties||e.parameters):[];function kS({tool:e,darkMode:t}){let r=wZ(e);return un.default.createElement("div",{style:{display:"grid",gap:"4px"}},e.description&&un.default.createElement("div",{className:"text-sm",style:{color:t?"rgba(255,255,255,0.75)":"#4b5563"}},e.description),un.default.createElement("div",{className:"text-xs font-mono",style:{color:t?"rgba(255,255,255,0.45)":"#9ca3af",display:"flex",flexWrap:"wrap",columnGap:"16px",rowGap:"4px"}},un.default.createElement("span",null,"params: ",r.length>0?r.join(", "):"none"),un.default.createElement("span",null,"source: ",bZ(e))))}function OS({tools:e,darkMode:t}){let[r,n]=(0,un.useState)(null),a=(e||[]).filter(l=>l&&l.name);if(a.length===0)return null;let i=a.find(l=>l.name===r),o=l=>({background:t?"rgba(34,197,94,0.12)":"#f0fdf4",color:t?"#86efac":"#15803d",border:`1px solid ${l?t?"#4ade80":"#22c55e":"transparent"}`,borderRadius:"6px",padding:"2px 8px",fontSize:"11px",cursor:"pointer"});return un.default.createElement("div",{style:{marginTop:"8px"}},un.default.createElement("div",{className:"flex items-center gap-1.5 flex-wrap font-mono"},un.default.createElement("span",{className:"text-xs uppercase",style:{letterSpacing:"0.05em",color:t?"rgba(255,255,255,0.45)":"#9ca3af"}},"tools"),a.map(l=>un.default.createElement("button",{key:l.name,onClick:s=>{s.stopPropagation(),n(r===l.name?null:l.name)},title:r===l.name?"Hide definition":"Show definition",style:o(r===l.name)},"\u2699 ",l.name))),i&&un.default.createElement("div",{onClick:l=>l.stopPropagation(),style:{marginTop:"6px",padding:"8px 10px",borderRadius:"8px",background:t?"rgba(34,197,94,0.06)":"#f7fef9",border:`1px solid ${t?"rgba(34,197,94,0.25)":"#bbf7d0"}`}},un.default.createElement(kS,{tool:i,darkMode:t})))}var UP=e=>e==null?null:e<1e3?`${Math.round(e)}ms`:`${(e/1e3).toFixed(2)}s`,$f=e=>{if(e==null)return null;if(typeof e=="object")return JSON.stringify(e,null,2);try{return JSON.stringify(JSON.parse(e),null,2)}catch{return null}},SZ=["text","output","body","content"],$P=e=>{if(e==null)return null;if(typeof e=="object")return e;try{return JSON.parse(e)}catch{return null}},AZ=e=>{let t=$P(e.tool_result)??$P(e.content);if(!t||typeof t!="object"||Array.isArray(t))return null;let r=SZ.find(i=>typeof t[i]=="string"&&t[i].trim().length>0);if(!r)return null;let n=[t.error&&"error",t.status!=null&&`status ${t.status}`,typeof t.url=="string"&&t.url.replace(/^https?:\/\//,""),typeof t.agent=="string"&&`\u2192 ${t.agent}`,t.truncated&&"truncated"].filter(Boolean).join(" \xB7 "),a=Object.fromEntries(Object.entries(t).filter(([i])=>i!==r));return{field:r,meta:n,body:t[r],rest:a}},NZ=e=>!!(e.tool_name||e.tool_call_id||e.tool_arguments||e.tool_result||(e.tool_calls||[]).length>0||e.duration_ms!=null||$f(e.content)),VP=280,_Z=["tool","system","developer"],Hf=(e,t=VP)=>e.replace(/\s+/g," ").trim().slice(0,t),TS=e=>{if(e==null)return null;if(typeof e=="object")return JSON.stringify(e);try{return JSON.stringify(JSON.parse(e))}catch{return String(e)}},FP=e=>{let t=e.tool_calls;return t?Array.isArray(t)?t.map(r=>({name:r.function?.name||r.name||e.tool_name,args:TS(r.function?.arguments??r.arguments??r.input)})):[{name:e.tool_name,args:TS(t)}]:[]},qP=e=>!!(e&&String(e).trim()&&String(e).trim()!=="\u2014"),CZ=e=>{let t=(e||[]).filter(Boolean),r=new Map,n=new Set;return t.forEach((a,i)=>{if(a.role!=="assistant"||qP(a.content))return;let o=FP(a);if(o.length===0)return;let l=new Set;Array.isArray(a.tool_calls)?a.tool_calls.forEach(u=>u.id&&l.add(u.id)):a.tool_call_id&&l.add(a.tool_call_id);let s=[];for(let u=i+1;u0&&c.tool_call_id&&!l.has(c.tool_call_id))break;s.push(u)}s.length!==0&&(s.forEach((u,c)=>{let d=t[u],f=o.find(p=>p.name&&p.name===d.tool_name)||o[Math.min(c,o.length-1)];f&&f.args&&!d.tool_arguments&&r.set(u,{tool_arguments:f.args,created_at:a.created_at||d.created_at})}),n.add(i))}),t.map((a,i)=>r.has(i)?{...a,...r.get(i)}:a).filter((a,i)=>!n.has(i))},HP=e=>_Z.includes(e.role)&&typeof e.content=="string"&&Hf(e.content).length>=VP,EZ=({message:e,darkMode:t})=>{let r=e.trace_duration_ms||0;if(!r||e.span_duration_ms==null)return null;let n=Math.min((e.span_start_ms||0)/r*100,98),a=Math.max(e.span_duration_ms/r*100,2),i=e.role==="tool"?"#22c55e":e.role==="assistant"?"#ef4444":"#60a5fa";return _e.default.createElement("span",{title:`${Math.round(e.span_duration_ms)}ms \xB7 starts at ${Math.round(e.span_start_ms||0)}ms of ${Math.round(r)}ms`,style:{position:"relative",width:"96px",height:"5px",borderRadius:"999px",background:t?"rgba(255,255,255,0.08)":"#f3f4f6",flexShrink:0,marginTop:"8px",display:"inline-block"}},_e.default.createElement("span",{style:{position:"absolute",top:0,bottom:0,left:`${n}%`,width:`${Math.min(a,100-n)}%`,borderRadius:"999px",background:i}}))},MS=e=>({background:e?"rgba(0,0,0,0.35)":"#f3f4f6",color:e?"#ffffff":"#111827",borderRadius:"6px",padding:"8px 10px",fontSize:"12px",overflowX:"auto",margin:0}),ta=(e,t)=>{switch(e){case"user":return t?{background:"rgba(59,130,246,0.15)",color:"#93c5fd",label:"User"}:{background:"#eff6ff",color:"#1d4ed8",label:"User"};case"assistant":return t?{background:"rgba(239,68,68,0.12)",color:"#fca5a5",label:"Assistant"}:{background:"#fef2f2",color:"#b91c1c",label:"Assistant"};case"tool":return t?{background:"rgba(245,158,11,0.12)",color:"#fcd34d",label:"Tool"}:{background:"#fffbeb",color:"#b45309",label:"Tool"};case"system":return t?{background:"rgba(139,92,246,0.15)",color:"#c4b5fd",label:"System"}:{background:"#f5f3ff",color:"#6d28d9",label:"System"};case"developer":return t?{background:"rgba(20,184,166,0.15)",color:"#5eead4",label:"Dev"}:{background:"#f0fdfa",color:"#0f766e",label:"Dev"};default:return t?{background:"rgba(255,255,255,0.08)",color:"rgba(255,255,255,0.7)",label:e}:{background:"#f3f4f6",color:"#374151",label:e}}};function ki({messages:e,darkMode:t,tools:r}){let[n,a]=(0,_e.useState)({}),i=s=>a(u=>({...u,[s]:!u[s]})),o={textPrimary:t?"#ffffff":"#111827",textSecondary:t?"rgba(255,255,255,0.6)":"#6b7280",textMuted:t?"rgba(255,255,255,0.4)":"#9ca3af"},l=CZ(e);return _e.default.createElement("div",{className:"space-y-3"},l.map(s=>{let u=ta(s.role,t),c=!!n[s.id],d=HP(s)&&!c,f=NZ(s)||HP(s),p=$f(s.tool_arguments),v=$f(s.tool_result)||$f(s.content),h=s.tool_calls&&(!Array.isArray(s.tool_calls)||s.tool_calls.length>0)?$f(s.tool_calls):null,b=s.role==="tool"?AZ(s):null,m=s.tool_name?(r||[]).find(L=>L&&L.name===s.tool_name):null,g=s.role==="tool"?TS(s.tool_arguments):null,x=s.role==="tool"&&!!g,A=ta("assistant",t),k=ta("tool",t),_=s.role==="tool"&&!s.tool_arguments&&l.some(L=>L&&L.role==="assistant"&&(s.tool_call_id&&L.tool_call_id===s.tool_call_id||Array.isArray(L.tool_calls)&&L.tool_calls.some(I=>I.id&&I.id===s.tool_call_id))),M=s.role==="assistant"&&!qP(s.content)?FP(s):[],E=MS(t);return _e.default.createElement("div",{key:s.id},_e.default.createElement("div",{className:`flex gap-3 items-start rounded-lg -mx-2 px-2 py-1 ${f?"cursor-pointer":""}`,onClick:f?()=>i(s.id):void 0,style:c?{background:t?"rgba(255,255,255,0.04)":"rgba(0,0,0,0.03)"}:{}},_e.default.createElement("span",{className:"flex flex-col gap-1 flex-shrink-0 mt-0.5",style:{minWidth:"72px"}},(x?[A,k]:[u]).map(L=>_e.default.createElement("span",{key:L.label,className:"px-2 py-0.5 rounded text-xs font-medium",style:{background:L.background,color:L.color,textAlign:"center"}},L.label))),_e.default.createElement("div",{className:"min-w-0 flex-1"},_e.default.createElement("div",{className:"text-sm break-words",style:{color:o.textPrimary}},s.role==="tool"?_e.default.createElement("span",{style:{display:"grid",gap:"2px"}},g&&!_&&_e.default.createElement("span",{className:"font-mono text-xs break-words"},s.tool_name&&_e.default.createElement("span",{style:{color:k.color}},"\u2699 ",s.tool_name," "),_e.default.createElement("span",{style:{color:x?A.color:o.textMuted}},"in:")," ",Hf(g,180),g.length>180?"\u2026":""),b?_e.default.createElement("span",null,s.tool_name&&!(g&&!_)&&_e.default.createElement("span",{className:"font-mono text-xs mr-1",style:{color:k.color}},"\u2699 ",s.tool_name),(g||_)&&_e.default.createElement("span",{className:"font-mono text-xs mr-1",style:{color:x?k.color:o.textMuted}},"out:"),b.meta&&_e.default.createElement("span",{className:"font-mono text-xs mr-2",style:{color:o.textMuted}},b.meta),"\u201C",b.body.replace(/\s+/g," ").trim().slice(0,180),b.body.length>180?"\u2026":"","\u201D"):s.content?_e.default.createElement("span",null,s.tool_name&&!(g&&!_)&&_e.default.createElement("span",{className:"font-mono text-xs mr-1",style:{color:k.color}},"\u2699 ",s.tool_name),(g||_)&&_e.default.createElement("span",{className:"font-mono text-xs mr-1",style:{color:x?k.color:o.textMuted}},"out:"),"\u201C",Hf(s.content,180),s.content.replace(/\s+/g," ").trim().length>180?"\u2026":"","\u201D"):_e.default.createElement("span",{className:"whitespace-pre-wrap"},s.tool_name?`\u2192 ${s.tool_name}(...)`:"\u2014")):d?_e.default.createElement("span",{style:{color:o.textSecondary}},Hf(s.content),"\u2026"):M.length>0?_e.default.createElement("span",{style:{display:"grid",gap:"2px"}},M.map((L,I)=>_e.default.createElement("span",{key:I,className:"font-mono text-xs break-words"},L.name&&_e.default.createElement("span",{style:{color:o.textMuted}},L.name," "),_e.default.createElement("span",{style:{color:o.textMuted}},"in:")," ",L.args?_e.default.createElement(_e.default.Fragment,null,Hf(L.args,180),L.args.length>180?"\u2026":""):"\u2014"))):_e.default.createElement(ES,{text:s.content||"\u2014"})),_e.default.createElement("div",{className:"text-xs mt-0.5 font-mono flex items-center gap-2 flex-wrap",style:{color:o.textMuted}},s.created_at&&_e.default.createElement("span",null,new Date(s.created_at).toLocaleTimeString()),s.tool_name&&s.role!=="tool"&&_e.default.createElement("span",{className:"px-1.5 py-0.5 rounded",style:{background:u.background,color:u.color}},"\u2699 ",s.tool_name),s.duration_ms!=null&&_e.default.createElement("span",null,UP(s.duration_ms)),(s.tool_calls||[]).length>0&&_e.default.createElement("span",null,s.tool_calls.length," tool call",s.tool_calls.length>1?"s":""),s.content_checksum&&_e.default.createElement("span",{title:"Content fingerprint"},"\u{1F512} ",s.content_checksum.slice(0,8)))),_e.default.createElement(EZ,{message:s,darkMode:t}),f&&_e.default.createElement("svg",{className:`w-3.5 h-3.5 mt-1 flex-shrink-0 transition-transform ${c?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",style:{color:o.textMuted}},_e.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"}))),c&&_e.default.createElement("div",{className:"ml-3 mt-1 mb-2 pl-4 space-y-2 border-l-2",style:{borderColor:u.color+"55"}},_e.default.createElement("div",{className:"flex flex-wrap gap-x-5 gap-y-1 text-xs font-mono",style:{color:o.textSecondary}},s.created_at&&_e.default.createElement("span",null,new Date(s.created_at).toLocaleString()),s.tool_call_id&&_e.default.createElement("span",null,"call: ",s.tool_call_id),s.duration_ms!=null&&_e.default.createElement("span",null,"took ",UP(s.duration_ms)),s.content_checksum&&_e.default.createElement("span",null,"\u{1F512} ",s.content_checksum)),p&&_e.default.createElement("div",null,_e.default.createElement("div",{className:"text-xs uppercase tracking-wide mb-1",style:{color:x?A.color:o.textMuted}},"Arguments",x?" \xB7 from the agent":""),_e.default.createElement("pre",{style:E},p)),b?_e.default.createElement("div",null,_e.default.createElement("div",{className:"text-xs uppercase tracking-wide mb-1",style:{color:x?k.color:o.textMuted}},"Result",x?" \xB7 from the tool":""),Object.keys(b.rest).length>0&&_e.default.createElement("pre",{style:E},JSON.stringify(b.rest,null,2)),_e.default.createElement("div",{className:"text-xs uppercase tracking-wide mt-2 mb-1",style:{color:o.textMuted}},b.field," \xB7 ",b.body.length.toLocaleString()," chars"),_e.default.createElement("pre",{style:{...E,whiteSpace:"pre-wrap",maxHeight:"320px",overflowY:"auto"}},b.body.slice(0,8e3),b.body.length>8e3?` +\u2026`:"")):v?_e.default.createElement("div",null,_e.default.createElement("div",{className:"text-xs uppercase tracking-wide mb-1",style:{color:o.textMuted}},s.role==="tool"?"Result":"Content (parsed)"),_e.default.createElement("pre",{style:E},v)):s.role==="tool"&&s.content?_e.default.createElement("div",null,_e.default.createElement("div",{className:"text-xs uppercase tracking-wide mb-1",style:{color:x?k.color:o.textMuted}},"Result",x?" \xB7 from the tool":""," \xB7 ",s.content.length.toLocaleString()," chars"),_e.default.createElement("pre",{style:{...E,whiteSpace:"pre-wrap",maxHeight:"320px",overflowY:"auto"}},s.content.slice(0,8e3),s.content.length>8e3?` +\u2026`:"")):null,h&&_e.default.createElement("div",null,_e.default.createElement("div",{className:"text-xs uppercase tracking-wide mb-1",style:{color:o.textMuted}},"Requested tool calls"),_e.default.createElement("pre",{style:E},h)),m&&_e.default.createElement("div",null,_e.default.createElement("div",{className:"text-xs uppercase tracking-wide mb-1",style:{color:o.textMuted}},"Tool definition"),_e.default.createElement(kS,{tool:m,darkMode:t}))))}))}var gt=te(ae()),kZ={messages:"#2563eb",tool_results:"#5b84ec",instructions:"#85a3f0",tool_schemas:"#a6bcf4",mcp_schemas:"#c3d2f8",memory:"#dce4fb",output:"#7c3aed"},Vf=e=>{let t=(e||"").toLowerCase();return t.includes("claude")?2e5:t.includes("gemini")?1e6:t.includes("llama")?131072:t.includes("gpt-4o")||t.includes("gpt-4-turbo")||t.includes("gpt-4.1")?128e3:t.includes("gpt-5")?4e5:128e3},za=e=>{if(e==null)return 0;let t=typeof e=="string"?e:JSON.stringify(e);return Math.round(t.length/4)},lc=e=>e==null?"\u2014":e>=1e6?`${(e/1e6).toFixed(1).replace(/\.0$/,"")}M`:e>=1e3?`${(e/1e3).toFixed(1).replace(/\.0$/,"")}k`:String(Math.round(e));function Oi({used:e,limit:t=2e5,segments:r=[],cached:n=0,thinking:a=0,compact:i=!1,defaultOpen:o=!1,label:l="Context window",estimated:s=!1,darkMode:u=!1}){let[c,d]=(0,gt.useState)(o),f=r.filter(M=>M.tokens>0),p=e??f.reduce((M,E)=>M+E.tokens,0),v=t>0?p/t:0,h=v>=.9?"#dc2626":v>=.75?"#eab308":null,b=u?"rgba(255,255,255,0.4)":"#9ca3af",m=u?"rgba(255,255,255,0.6)":"#6b7280",g=u?"rgba(255,255,255,0.75)":"#4b5563",x=u?"rgba(255,255,255,0.1)":"#f3f4f6",A=`${lc(p)} / ${lc(t)} (${Math.round(v*100)}%)`,k=M=>M.key==="messages"&&h?h:M.color||kZ[M.key]||"#2563eb",_=M=>gt.default.createElement("div",{style:{display:"flex",height:M,borderRadius:"999px",overflow:"hidden",background:x,flex:1}},f.map(E=>gt.default.createElement("div",{key:E.key,title:`${E.label} \xB7 ${lc(E.tokens)}`,style:{width:`${Math.min(E.tokens/t*100,100)}%`,background:k(E)}})));return i?gt.default.createElement("span",{title:`${l}: ${A}`,style:{display:"inline-flex",alignItems:"center",gap:"6px",width:"132px",verticalAlign:"middle"}},_("6px"),gt.default.createElement("span",{style:{fontSize:"11px",fontFamily:"'SF Mono', 'Fira Code', monospace",color:h||b,whiteSpace:"nowrap"}},Math.round(v*100),"%")):gt.default.createElement("div",{style:{background:u?"rgba(255,255,255,0.03)":"#ffffff",border:`1px solid ${h?h+"66":u?"rgba(255,255,255,0.1)":"#e5e7eb"}`,borderRadius:"12px",padding:"12px 14px"}},gt.default.createElement("div",{onClick:M=>{M.stopPropagation(),d(!c)},style:{display:"flex",alignItems:"center",justifyContent:"space-between",cursor:"pointer"}},gt.default.createElement("span",{style:{fontFamily:"'SF Mono', 'Fira Code', monospace",fontSize:"11px",fontWeight:600,letterSpacing:"0.06em",textTransform:"uppercase",color:m}},l,s&>.default.createElement("span",{style:{color:b,fontWeight:400}}," \xB7 estimated")),gt.default.createElement("span",{style:{display:"inline-flex",alignItems:"center",gap:"8px"}},gt.default.createElement("span",{style:{fontFamily:"'SF Mono', 'Fira Code', monospace",fontSize:"12px",fontWeight:600,color:h||g}},A),gt.default.createElement("span",{style:{fontFamily:"'SF Mono', 'Fira Code', monospace",fontSize:"11px",color:b,display:"inline-block",transform:c?"rotate(90deg)":"none",transition:"transform 0.15s ease"}},">"))),gt.default.createElement("div",{style:{display:"flex",marginTop:"8px"}},_("9px")),v>=.9&>.default.createElement("div",{style:{marginTop:"6px",fontFamily:"'SF Mono', 'Fira Code', monospace",fontSize:"11px",color:"#dc2626"}},"[!] compaction imminent \u2014 next turn may drop the oldest messages"),c&>.default.createElement("div",{style:{marginTop:"10px",display:"grid",gap:"5px"}},[...f,{key:"__free",label:"Free space",tokens:Math.max(t-p,0)}].map(M=>gt.default.createElement("div",{key:M.key,style:{display:"flex",alignItems:"center",gap:"8px"}},gt.default.createElement("span",{style:{width:"9px",height:"9px",borderRadius:"2px",flexShrink:0,background:M.key==="__free"?x:k(M),border:M.key==="__free"?`1px solid ${u?"rgba(255,255,255,0.25)":"#d1d5db"}`:"none"}}),gt.default.createElement("span",{style:{fontSize:"13px",color:g,flex:1}},M.label),gt.default.createElement("span",{style:{fontFamily:"'SF Mono', 'Fira Code', monospace",fontSize:"12px",color:m,width:"56px",textAlign:"right"}},lc(M.tokens)),gt.default.createElement("span",{style:{fontFamily:"'SF Mono', 'Fira Code', monospace",fontSize:"12px",color:b,width:"46px",textAlign:"right"}},t>0?`${(M.tokens/t*100).toFixed(1)}%`:"\u2014"))),(n>0||a>0)&>.default.createElement("div",{style:{marginTop:"4px",paddingTop:"6px",borderTop:`1px solid ${u?"rgba(255,255,255,0.08)":"#f3f4f6"}`,display:"flex",gap:"16px",fontFamily:"'SF Mono', 'Fira Code', monospace",fontSize:"11px",color:b}},n>0&>.default.createElement("span",null,gt.default.createElement("span",{style:{color:"#2563eb"}},"[=]")," cached prefix ",lc(n)),a>0&>.default.createElement("span",null,gt.default.createElement("span",{style:{color:"#7c3aed"}},"[~]")," thinking ",lc(a)))))}var ra=te(ae()),sc=[{id:"5m",label:"5m",minutes:5,bucketSeconds:10},{id:"10m",label:"10m",minutes:10,bucketSeconds:20},{id:"15m",label:"15m",minutes:15,bucketSeconds:30},{id:"30m",label:"30m",minutes:30,bucketSeconds:60},{id:"45m",label:"45m",minutes:45,bucketSeconds:60},{id:"1h",label:"1h",minutes:60,bucketSeconds:120},{id:"2h",label:"2h",minutes:120,bucketSeconds:240},{id:"3h",label:"3h",minutes:180,bucketSeconds:300},{id:"6h",label:"6h",minutes:360,bucketSeconds:600},{id:"12h",label:"12h",minutes:720,bucketSeconds:1200},{id:"1d",label:"1d",minutes:1440,bucketSeconds:1800},{id:"3d",label:"3d",minutes:4320,bucketSeconds:7200},{id:"7d",label:"7d",minutes:10080,bucketSeconds:14400},{id:"30d",label:"30d",minutes:43200,bucketSeconds:86400},{id:"90d",label:"90d",minutes:129600,bucketSeconds:259200}],YP="30m",WP="activeagents.timeWindow",KP=(0,ra.createContext)(null),DS=e=>sc.find(t=>t.id===e);function GP({children:e}){let[t,r]=(0,ra.useState)(()=>{try{let i=localStorage.getItem(WP);if(i&&DS(i))return i}catch{}return YP}),n=(0,ra.useCallback)(i=>{if(DS(i)){r(i);try{localStorage.setItem(WP,i)}catch{}}},[]),a=(0,ra.useMemo)(()=>{let i=sc.findIndex(l=>l.id===t),o=sc[i]||DS(YP);return{timeWindow:o,windowIndex:i,windows:sc,setWindow:n,zoomTo:l=>{let s=sc[Math.min(Math.max(l,0),sc.length-1)];s&&n(s.id)},days:Math.max(1,Math.ceil(o.minutes/1440))}},[t,n]);return ra.default.createElement(KP.Provider,{value:a},e)}function La(){let e=(0,ra.useContext)(KP);if(!e)throw new Error("useTimeWindow must be used within a TimeWindowProvider");return e}var uc=te(ae());function Ba({compact:e=!1}){let{windows:t,windowIndex:r,timeWindow:n,setWindow:a,zoomTo:i}=La(),{darkMode:o}=pt(),l=qs(o),s={color:l.textSecondary};return uc.default.createElement("div",{className:"flex items-center rounded-lg p-1",style:{background:l.mutedBg}},uc.default.createElement("button",{onClick:()=>i(r-1),disabled:r<=0,title:"Zoom in (shorter window)","aria-label":"Zoom in",className:"aa-zoom px-2 py-1 text-sm rounded-md disabled:opacity-30 disabled:cursor-not-allowed",style:s},"\u2212"),uc.default.createElement("select",{value:n.id,onChange:u=>a(u.target.value),"aria-label":"Time window",className:`aa-field px-2 py-1 mx-1 text-sm rounded-md shadow ${e?"":"min-w-[4.5rem]"}`,style:{background:l.cardBg,color:l.textPrimary,border:`1px solid ${l.cardBorder}`}},t.map(u=>uc.default.createElement("option",{key:u.id,value:u.id},u.label))),uc.default.createElement("button",{onClick:()=>i(r+1),disabled:r>=t.length-1,title:"Zoom out (longer window)","aria-label":"Zoom out",className:"aa-zoom px-2 py-1 text-sm rounded-md disabled:opacity-30 disabled:cursor-not-allowed",style:s},"+"))}var XP=["#6366f1","#10b981","#f59e0b","#ec4899","#3b82f6","#8b5cf6","#14b8a6","#f97316"],OZ=3e4,TZ=(e,t)=>{let r=Math.max(e||0,1),n=Math.max(t||1,1);if(n<=1)return 100;let a=Math.log(r)/Math.log(n);return Math.min(100,Math.max(4,a*100))},MZ=e=>{let t={};return e.forEach((r,n)=>{t[r]=XP[n%XP.length]}),t},DZ=e=>{let t={};return e.forEach(r=>{(r.spans||[]).forEach(n=>{if((n.nested||0)===0)return;let a=n.name||n.type||"unknown";t[a]||(t[a]={name:a,type:n.type,count:0,total:0,max:0,errors:0});let i=t[a];i.count+=1,i.total+=n.duration||0,i.max=Math.max(i.max,n.duration||0),n.error&&(i.errors+=1)})}),Object.values(t).map(r=>({...r,avg:r.total/r.count})).sort((r,n)=>n.total-r.total)},PZ=(e,t,r,n=60)=>{let a=Date.now(),i=[],o=n*1e3,l=Math.max(1,Math.round(r*60/n));for(let s=l-1;s>=0;s--){let u=a-(s+1)*o,c=a-s*o,d=e.filter(p=>p.timestamp_ms>=u&&p.timestamp_ms{f[p]=d.filter(v=>v.agent===p).length}),i.push({time:r>1440?new Date(c).toLocaleString([],{month:"short",day:"numeric",hour:"2-digit"}):new Date(c).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),timestamp:c,requests:d.length,errors:d.filter(p=>p.status==="ERROR").length,avgLatency:d.length>0?Math.round(d.reduce((p,v)=>p+(v.duration_ms||0),0)/d.length):0,traces:d,...f})}return i};function Wl({agentClass:e=null,embedded:t=!1}){let{darkMode:r}=pt(),[n,a]=(0,w.useState)([]),[i,o]=(0,w.useState)([]),[l,s]=(0,w.useState)({}),[u,c]=(0,w.useState)(!0),[d,f]=(0,w.useState)(null),[p,v]=(0,w.useState)(null),h=(0,w.useRef)(new URLSearchParams(window.location.search).get("trace")),[b,m]=(0,w.useState)(null),[g,x]=(0,w.useState)(null),[A,k]=(0,w.useState)({}),[_,M]=(0,w.useState)({status:"all",agent:"all",action:"all"}),[E,L]=(0,w.useState)("time"),[I,j]=(0,w.useState)(null),[z,X]=(0,w.useState)("timeline"),[R,Q]=(0,w.useState)("popular"),{timeWindow:y}=La(),C=(0,w.useCallback)(async()=>{try{let S=e?`&agent=${encodeURIComponent(e)}`:"",P=await fetch(`/api/traces?minutes=${y.minutes}${S}`);if(!P.ok)throw new Error(`Request failed (${P.status})`);let W=await P.json();a(W.traces||[]),o(e?[e]:W.agents||[]),s(W.agent_ids||{}),f(null)}catch(S){f(S.message)}finally{c(!1)}},[y.minutes,e]);(0,w.useEffect)(()=>{C();let S=setInterval(C,OZ);return()=>clearInterval(S)},[C]);let O=(0,w.useMemo)(()=>MZ(i),[i]);(0,w.useEffect)(()=>{j(null)},[y.id]);let D=(0,w.useMemo)(()=>PZ(n,i,y.minutes,y.bucketSeconds),[n,i,y]),N=Math.max(0,Math.ceil(D.length/6)-1),[T,$]=(0,w.useState)("time"),[Y,J]=(0,w.useState)({}),[fe,pe]=(0,w.useState)({}),ge=S=>{J(P=>({...P,[S]:"conversation"})),!fe[S]&&(pe(P=>({...P,[S]:"loading"})),fetch(`/api/interactions/trace-${S}`).then(P=>P.ok?P.json():Promise.reject(new Error("request failed"))).then(P=>pe(W=>({...W,[S]:P.interaction}))).catch(()=>pe(P=>({...P,[S]:"error"}))))},ze=(S,P)=>{let W=fe[S.id];if(!W||W==="loading")return w.default.createElement("div",{className:"text-sm text-gray-400 py-4"},"Loading conversation\u2026");if(W==="error")return w.default.createElement("div",{className:"text-sm text-red-500 py-4"},"Couldn't load the conversation for this trace.");let Z=W.instructions?[{id:`trace-${S.id}-system`,role:"system",content:W.instructions,created_at:W.created_at},...W.messages||[]]:W.messages||[];return Z.length===0?w.default.createElement("div",{className:"text-sm text-gray-400 py-4"},"This trace predates content capture \u2014 no conversation to show."):w.default.createElement(ki,{messages:Z,darkMode:P,tools:Tg(S)})},se=(0,w.useCallback)(S=>{let P=[...S||[]];switch(T){case"duration":return P.sort((W,Z)=>(Z.duration||0)-(W.duration||0));case"name":return P.sort((W,Z)=>(W.name||"").localeCompare(Z.name||""));default:return P.sort((W,Z)=>(W.start||0)-(Z.start||0))}},[T]),Ne=(0,w.useMemo)(()=>{let S=n;if(I!==null){let P=D[I];P&&(S=P.traces||[])}if(_.status!=="all"&&(S=S.filter(P=>_.status==="success"?P.status!=="ERROR":P.status==="ERROR")),_.agent!=="all"&&(S=S.filter(P=>P.agent===_.agent)),_.action!=="all")if(_.action.includes("#")){let[P,W]=_.action.split("#");S=S.filter(Z=>Z.agent===P&&Z.action===W)}else S=S.filter(P=>P.action===_.action);return E==="latency"&&(S=[...S].sort((P,W)=>(W.duration_ms||0)-(P.duration_ms||0))),b&&!S.some(P=>P.id===b.id)&&(S=[b,...S]),S},[n,I,D,_,E,b]);(0,w.useEffect)(()=>{let S=h.current;if(!S)return;let P=Z=>{h.current=null,v(Z.id),setTimeout(()=>{document.getElementById(`trace-row-${Z.id}`)?.scrollIntoView({behavior:"smooth",block:"start"})},150)},W=n.find(Z=>Z.id===S||Z.trace_id===S||Z.short_id&&String(S).startsWith(Z.short_id));if(W){P(W);return}n.length!==0&&fetch(`/api/traces/${encodeURIComponent(S)}`).then(Z=>Z.ok?Z.json():Promise.reject(new Error("not found"))).then(({trace:Z})=>{m(Z),P(Z)}).catch(()=>{h.current=null})},[n]);let F=(0,w.useMemo)(()=>{let S=I!==null?D[I]?.traces||[]:n,P={};S.forEach(Z=>{Z.agent&&(P[Z.agent]||(P[Z.agent]={agent:Z.agent,count:0,totalDuration:0,cost:0,errors:0,actions:{},tokens:{thinking:0,input:0,output:0}}),P[Z.agent].count++,P[Z.agent].totalDuration+=Z.duration_ms||0,P[Z.agent].cost+=Z.estimated_cost||0,Z.status==="ERROR"&&P[Z.agent].errors++,Z.tokens&&(P[Z.agent].tokens.thinking+=Z.tokens.thinking||0,P[Z.agent].tokens.input+=Z.tokens.input||0,P[Z.agent].tokens.output+=Z.tokens.output||0),Z.action&&(P[Z.agent].actions[Z.action]=(P[Z.agent].actions[Z.action]||0)+1))});let W={popular:Z=>Z.count,longest:Z=>Z.totalDuration/(Z.count||1),cost:Z=>Z.cost,tokens:Z=>(Z.tokens.input||0)+(Z.tokens.output||0)+(Z.tokens.thinking||0),errors:Z=>Z.errors}[R]||(Z=>Z.count);return Object.values(P).sort((Z,je)=>W(je)-W(Z)||je.count-Z.count)},[n,D,I,R]),H=(0,w.useMemo)(()=>{let S=I!==null?D[I]?.traces||[]:n,P={};return S.forEach(W=>{if(!W.agent)return;let Z=`${W.agent}#${W.action||"unknown"}`;P[Z]||(P[Z]={key:Z,agent:W.agent,action:W.action||"unknown",count:0,totalDuration:0,errors:0,minDuration:1/0,maxDuration:0,tokens:{thinking:0,input:0,output:0}}),P[Z].count++,P[Z].totalDuration+=W.duration_ms||0,P[Z].minDuration=Math.min(P[Z].minDuration,W.duration_ms||0),P[Z].maxDuration=Math.max(P[Z].maxDuration,W.duration_ms||0),W.status==="ERROR"&&P[Z].errors++,W.tokens&&(P[Z].tokens.thinking+=W.tokens.thinking||0,P[Z].tokens.input+=W.tokens.input||0,P[Z].tokens.output+=W.tokens.output||0)}),Object.values(P).forEach(W=>{W.minDuration===1/0&&(W.minDuration=0)}),Object.values(P).sort((W,Z)=>Z.count-W.count)},[n,D,I]),q=(0,w.useMemo)(()=>{let S=I!==null?D[I]?.traces||[]:n,P={};return S.forEach(W=>{(W.spans||[]).forEach(Z=>{if(Z.type!=="tool")return;let je=Z.attributes?.["tool.name"]||Z.name?.replace(/^tool\./,"")||"unknown";P[je]||(P[je]={name:je,count:0,totalDuration:0,maxDuration:0,errors:0,agents:new Set}),P[je].count++,P[je].totalDuration+=Z.duration||0,P[je].maxDuration=Math.max(P[je].maxDuration,Z.duration||0),Z.error&&P[je].errors++,W.agent&&P[je].agents.add(`${W.agent}#${W.action||"unknown"}`)})}),Object.values(P).map(W=>({...W,agents:[...W.agents],avgDuration:W.count?W.totalDuration/W.count:0})).sort((W,Z)=>Z.totalDuration-W.totalDuration)},[n,D,I]),ee=(0,w.useMemo)(()=>_.agent==="all"?H.map(S=>S.key):H.filter(S=>S.agent===_.agent).map(S=>S.key),[_.agent,H]),ie=(0,w.useMemo)(()=>DZ(Ne),[Ne]),K=S=>{if(S&&S.activeTooltipIndex!==void 0){let P=S.activeTooltipIndex;j(I===P?null:P),v(null)}},xe=S=>S==null?"\u2014":S<1e3?`${Math.round(S)}ms`:`${(S/1e3).toFixed(2)}s`,Ae=S=>{if(!S)return"0";let P=(S.input||0)+(S.output||0)+(S.thinking||0);return P>=1e3?`${(P/1e3).toFixed(1)}K`:`${P}`},we=S=>(S?.input||0)+(S?.output||0)+(S?.thinking||0),Ke=S=>{let P=S.count?S.errors/S.count:0;return[{label:`Calls ${y.label||""}`.trim(),value:S.count.toLocaleString()},{label:"Errors",value:`${(P*100).toFixed(1)}%`,tone:S.errors>0?"bad":"good"},{label:"Avg time",value:`${Math.round(S.totalDuration/(S.count||1))}ms`},{label:"Tokens",value:we(S.tokens).toLocaleString()},{label:"Cost",value:xr(S.cost),title:"Estimated from token counts at this model's published rates",tone:S.cost?void 0:"muted"},{label:"Actions",value:(S.agents?.length||0).toLocaleString()}]},xr=S=>S?S<.01?"<$0.01":`$${S.toFixed(2)}`:"\u2014",To=w.default.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"flex-end",gap:"8px",marginBottom:"12px"}},w.default.createElement("span",{style:{fontSize:"12px",color:r?"rgba(255,255,255,0.5)":"#6b7280"}},"Rank by"),w.default.createElement("select",{value:R,onChange:S=>Q(S.target.value),style:{fontSize:"12px",padding:"4px 8px",borderRadius:"6px",border:`1px solid ${r?"rgba(255,255,255,0.15)":"#e5e7eb"}`,background:r?"rgba(255,255,255,0.05)":"#ffffff",color:r?"#f9fafb":"#111827"}},w.default.createElement("option",{value:"popular"},"Most calls"),w.default.createElement("option",{value:"longest"},"Longest average"),w.default.createElement("option",{value:"cost"},"Highest cost"),w.default.createElement("option",{value:"tokens"},"Most tokens"),w.default.createElement("option",{value:"errors"},"Most errors"))),Kr=S=>M(P=>({...P,agent:P.agent===S?"all":S})),It=S=>{let P=l[S];if(!P)return Kr(S);let W=`/dashboard/agents/${P}`;window.history.pushState(window.history.state,"",W),window.dispatchEvent(new CustomEvent("dashboard:navigate",{detail:{path:W}}))},cQ=S=>{if(S==null)return"\u2014";let P=typeof S=="string"?S:JSON.stringify(S,null,2);if(typeof S=="string"&&/^[\[{]/.test(S.trim()))try{return JSON.stringify(JSON.parse(S),null,2)}catch{return P}return P},aA=S=>S==null?null:`$${S.toFixed(4)}`,kg=S=>{switch(S){case"root":return or.spans.root;case"prompt":return or.spans.prompt;case"generate":return or.spans.generate;case"llm":return or.spans.llm;case"thinking":return or.spans.thinking;case"tool":return or.spans.tool;case"response":return or.spans.response;default:return"-"}},Gl=S=>S.status!=="ERROR",iA=(S,P)=>{if(!P.duration_ms)return xe(S.duration);let W=(S.duration||0)/P.duration_ms*100;return`${xe(S.duration)} \xB7 ${W<1?W.toFixed(2):W.toFixed(1)}%`},hj=S=>{let P=S.tokens||{};return(P.input||0)+(P.output||0)+(P.thinking||0)},gj=S=>{if(typeof S!="string")return null;try{return JSON.stringify(JSON.parse(S),null,2)}catch{return null}},Og=S=>{let P=S.spans||[],W=Te=>{for(let Je of P){let Me=(Je.attributes||{})[Te];if(Me)return Me}return null},Z=W("llm.prompt");if(!Z){let Te=W("prompt.input.messages");if(Te)try{Z=[...JSON.parse(Te)].reverse().find(zt=>zt&&zt.role==="user"&&zt.content)?.content}catch{}}let je=W("llm.completion")||W("llm.output.message");return{input:Z,output:je}},$a=(S,P=200)=>{let W=String(S).replace(/\s+/g," ").trim();return W.length>P?`${W.slice(0,P)}\u2026`:W},yj=S=>{let P=S.attributes||{},W=null,Z="input:",je="user";if(P["prompt.input.messages"])try{let Ge=[...JSON.parse(P["prompt.input.messages"])].reverse().find(mt=>mt&&mt.role==="user"&&mt.content);typeof Ge?.content=="string"&&(W=Ge.content)}catch{}if(!W&&P["llm.prompt"]&&(W=String(P["llm.prompt"])),!W&&(P["tool.input.args"]||P["tool.arguments"])){let zt=P["tool.input.args"]||P["tool.arguments"];W=typeof zt=="string"?zt:JSON.stringify(zt),Z="in:",je="assistant"}let Te=null,Je="output:",Me="assistant";return P["llm.output.message"]||P["llm.completion"]?Te=P["llm.output.message"]||P["llm.completion"]:(P["tool.output.result"]||P["tool.result"])&&(Te=P["tool.output.result"]||P["tool.result"],Je="out:",Me="tool"),Te!=null&&typeof Te!="string"&&(Te=JSON.stringify(Te)),{input:W,output:Te,inputLabel:Z,inputTone:je,outputLabel:Je,outputTone:Me}},oA=(S,P)=>{let W=yj(S),Z=Og(P);return W.input&&W.input===Z.input&&(W.input=null),W.output&&W.output===Z.output&&(W.output=null),W},Xf=S=>{let P=S.spans||[],W=null;for(let Ge of P){let mt=Ge.tokens||{},Pe=(mt.input||0)+(mt.output||0);Pe>0&&(!W||Pe>W.total)&&(W={input:mt.input||0,output:mt.output||0,thinking:mt.thinking||0,cached:mt.cached||0,total:Pe})}if(!W)return null;let Z=Ge=>{for(let mt of P){let Pe=(mt.attributes||{})[Ge];if(Pe)return Pe}return null},je=za(Z("prompt.input.instructions")||Z("llm.instructions")),Te=za(Z("prompt.input.tools")||Z("llm.tools")),Je=za(Z("prompt.input.mcp_tools")),Me=0;for(let Ge of P){let mt=Ge.attributes||{},Pe=mt["tool.output.result"]||mt["tool.result"],et=mt["tool.input.args"]||mt["tool.arguments"];Pe&&(Me+=za(Pe)),et&&(Me+=za(et))}Me=Math.min(Me,W.input);let zt=Math.max(W.input-je-Te-Je-Me,0);return{used:W.total,limit:Vf(S.model),cached:W.cached,thinking:W.thinking,segments:[{key:"messages",label:"Messages",tokens:zt},{key:"tool_results",label:"Tool results",tokens:Me},{key:"instructions",label:"Instructions",tokens:je},{key:"tool_schemas",label:"Tool schemas",tokens:Te},{key:"mcp_schemas",label:"MCP tool schemas",tokens:Je},{key:"output",label:"Generated output",tokens:W.output}]}},lA=S=>S.endsWith(".instructions")?0:S.endsWith(".tools")?1:S.endsWith(".messages")?3:2,xj=S=>{let P={...S.attributes||{}},W=et=>{let Mo=P[et];return delete P[et],Mo},Z=[],je=et=>Z.push({id:`${S.span_id}-m${Z.length}`,...et}),Te=W("prompt.input.instructions")||W("llm.instructions");Te&&je({role:"system",content:String(Te)});let Je=W("prompt.input.messages");if(Je)try{JSON.parse(Je).forEach(et=>{et&&je({role:et.role||"user",content:typeof et.content=="string"?et.content:JSON.stringify(et.content),tool_call_id:et.tool_call_id,tool_calls:et.tool_calls})})}catch{P["prompt.input.messages"]=Je}let Me=W("llm.prompt");Me&&je({role:"user",content:String(Me)});let zt=W("tool.input.args")||W("tool.arguments"),Ge=W("tool.output.result")||W("tool.result");if(zt||Ge){let et=W("tool.name");je({role:"tool",tool_name:et||(S.name||"").replace(/^tool\./,""),tool_arguments:zt,content:Ge?String(Ge):null})}let mt=W("llm.completion"),Pe=W("llm.output.message");return(mt||Pe)&&je({role:"assistant",content:String(mt||Pe)}),{messages:Z,rest:P}},Tg=S=>{for(let P of S.spans||[]){let W=(P.attributes||{})["prompt.input.tools"];if(W)try{let Z=typeof W=="string"?JSON.parse(W):W;if(Array.isArray(Z)&&Z.length>0)return Z}catch{}}return[]},sA=(S,P,W=[])=>{let{messages:Z,rest:je}=xj(S),Te=null,Je=je["prompt.input.tools"];if(Je)try{let Pe=typeof Je=="string"?JSON.parse(Je):Je;Array.isArray(Pe)&&Pe.length>0&&(Te=Pe,delete je["prompt.input.tools"])}catch{}let Me=Object.entries(je).sort((Pe,et)=>lA(Pe[0])-lA(et[0])),zt=P?"rgba(255,255,255,0.75)":"#4b5563",Ge=P?"rgba(255,255,255,0.45)":"#9ca3af",mt={background:P?"rgba(255,255,255,0.06)":"#eef0f3",borderRadius:"6px",padding:"6px 8px",margin:"2px 0 4px 0",overflowX:"auto",whiteSpace:"pre"};return w.default.createElement("div",{style:{margin:"4px 0 8px 0",padding:"10px 12px",borderRadius:"8px",background:P?"rgba(0,0,0,0.35)":"#f9fafb",border:`1px solid ${P?"rgba(255,255,255,0.1)":"#e5e7eb"}`,fontFamily:Oe.mono,fontSize:"12px",color:zt}},w.default.createElement("div",{style:{display:"flex",flexWrap:"wrap",gap:"4px 20px"}},w.default.createElement("span",null,"duration: ",xe(S.duration)),w.default.createElement("span",null,"status: ",S.status||"OK"),hj(S)>0&&w.default.createElement("span",null,"tokens in:",S.tokens.input||0," out:",S.tokens.output||0,S.tokens.thinking>0?` thinking:${S.tokens.thinking}`:""),w.default.createElement("span",{style:{color:Ge}},"span: ",S.span_id)),(()=>{let Pe=Z.length===1&&Z[0].role==="tool"?Z[0]:null;if(!Pe)return null;let et=ta("tool",P).color,Mo=Pe.tool_arguments==null?null:typeof Pe.tool_arguments=="string"?Pe.tool_arguments:JSON.stringify(Pe.tool_arguments);return w.default.createElement("div",{onClick:Qf=>Qf.stopPropagation(),style:{marginTop:"8px",display:"grid",gap:"4px",cursor:"default"}},w.default.createElement("div",{style:{fontSize:"12px",wordBreak:"break-all"}},w.default.createElement("span",{style:{color:et}},"\u2699 ",Pe.tool_name),Mo&&w.default.createElement(w.default.Fragment,null," ",w.default.createElement("span",{style:{color:ta("assistant",P).color}},"in:")," ",w.default.createElement("span",{style:{color:zt}},Mo))),Pe.content&&w.default.createElement("div",{style:{fontSize:"12px"}},w.default.createElement("span",{style:{color:et}},"out:")," ",w.default.createElement("span",{style:{color:zt,whiteSpace:"pre-wrap",display:"inline-block",maxHeight:"240px",overflowY:"auto",verticalAlign:"top",maxWidth:"100%"}},Pe.content)))})(),Z.length>0&&!(Z.length===1&&Z[0].role==="tool")&&w.default.createElement("div",{onClick:Pe=>Pe.stopPropagation(),style:{marginTop:"10px",fontFamily:"ui-sans-serif, system-ui, sans-serif",cursor:"default"}},w.default.createElement(ki,{messages:Z,darkMode:P,tools:Te||W})),Te&&w.default.createElement(OS,{tools:Te,darkMode:P}),Me.length>0&&w.default.createElement("div",{style:{marginTop:"6px",display:"grid",gap:"2px"}},Me.map(([Pe,et])=>{let Mo=Pe.match(/args|result|input|output/)?gj(et):null,Qf=!Mo&&(Pe.endsWith(".instructions")||Pe.match(/input|output|result/))&&typeof et=="string"?et:null,Mg=Mo||Qf;if(!Mg)return w.default.createElement("div",{key:Pe,style:{wordBreak:"break-all"}},w.default.createElement("span",{style:{color:Ge}},Pe,":")," ",typeof et=="string"?et:JSON.stringify(et));let Dg=`${S.span_id}:${Pe}`,Jf=!!A[Dg];return w.default.createElement("div",{key:Pe},w.default.createElement("div",{onClick:Pg=>{Pg.stopPropagation(),k(dA=>({...dA,[Dg]:!dA[Dg]}))},title:Jf?"Collapse":"Expand",style:{cursor:"pointer",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},w.default.createElement("span",{style:{color:Ge}},Jf?"\u25BE":"\u25B8"," ",Pe,":")," ",!Jf&&w.default.createElement("span",{style:{color:Ge}},$a(Mg,140))),Jf&&w.default.createElement("pre",{onClick:Pg=>Pg.stopPropagation(),style:Qf?{...mt,whiteSpace:"pre-wrap"}:mt},Mg))})))},bj=S=>{let P=0,W=[];(S.spans||[]).forEach(Te=>{(Te.nested||0)!==0&&(Te.type==="tool"?W.push(Te):(Te.type==="llm"||Te.type==="generate")&&(P=Math.max(P,Te.duration||0)))});let Z=W.reduce((Te,Je)=>Te+(Je.duration||0),0);P=Math.max(P-Z,0);let je=S.duration_ms||P+Z||1;return{generation:P,tools:W,toolTotal:Z,total:je,overhead:Math.max(je-P-Z,0)}},uA=(S,P)=>{let{generation:W,tools:Z,toolTotal:je,total:Te,overhead:Je}=bj(S);if(W===0&&je===0||W/Te>=.99&&je/Te<.01)return null;let Me=P?"rgba(255,255,255,0.5)":"#6b7280",zt=P?"rgba(255,255,255,0.85)":"#374151",Ge=Pe=>`${(Pe/Te*100).toFixed(Pe/Te<.01?2:1)}%`,mt=(Pe,et)=>w.default.createElement("div",{style:{width:`${Math.max(Pe/Te*100,.4)}%`,background:et,height:"100%"}});return w.default.createElement("div",{style:{marginTop:"14px",paddingTop:"12px",borderTop:`1px solid ${P?"rgba(255,255,255,0.1)":"#e5e7eb"}`}},w.default.createElement("div",{style:{fontSize:"11px",textTransform:"uppercase",letterSpacing:"0.05em",color:Me,marginBottom:"6px"}},"Time breakdown \u2014 generation vs tools"),w.default.createElement("div",{style:{display:"flex",height:"10px",borderRadius:"5px",overflow:"hidden",background:P?"rgba(255,255,255,0.08)":"#f3f4f6"}},mt(W,"#ef4444"),mt(je,"#10b981"),Je>0&&mt(Je,P?"rgba(255,255,255,0.2)":"#d1d5db")),w.default.createElement("div",{style:{display:"flex",flexWrap:"wrap",gap:"4px 18px",marginTop:"8px",fontSize:"12px",fontFamily:Oe.mono,color:zt}},w.default.createElement("span",null,w.default.createElement("span",{style:{color:"#ef4444"}},"\u25A0")," generation ",xe(W)," (",Ge(W),")"),w.default.createElement("span",null,w.default.createElement("span",{style:{color:"#10b981"}},"\u25A0")," tools ",xe(je)," (",Ge(je),")"),Je>0&&w.default.createElement("span",null,w.default.createElement("span",{style:{color:Me}},"\u25A0")," overhead ",xe(Je)," (",Ge(Je),")")),Z.length>0&&w.default.createElement("div",{style:{display:"flex",flexWrap:"wrap",gap:"4px 18px",marginTop:"4px",fontSize:"12px",fontFamily:Oe.mono,color:Me}},Z.map((Pe,et)=>w.default.createElement("span",{key:et},Pe.name," ",xe(Pe.duration)," (",Ge(Pe.duration||0),")"))))},cA=S=>{let P=ie[0]?.total||1,W=S?"rgba(0,0,0,0.3)":"white",Z=S?"1px solid rgba(255,255,255,0.1)":"1px solid #e5e7eb",je=S?"white":"#111827",Te=S?"rgba(255,255,255,0.5)":"#6b7280",Je={llm:"#ef4444",tool:"#10b981",generate:"#8b5cf6",prompt:"#3b82f6"};return w.default.createElement("div",{style:{padding:S?"0 24px 24px 24px":0}},w.default.createElement("div",{style:{background:W,border:Z,borderRadius:"12px",padding:"16px"}},w.default.createElement("div",{style:{fontSize:"14px",fontWeight:600,color:je,marginBottom:"4px"}},"Slowest operations"),w.default.createElement("div",{style:{fontSize:"12px",color:Te,marginBottom:"14px"}},"Span durations aggregated across ",Ne.length," trace",Ne.length===1?"":"s"," \u2014 ranked by total time"),ie.length===0?w.default.createElement("div",{style:{fontSize:"13px",color:Te,padding:"12px 0"}},"No spans in the current selection"):w.default.createElement("div",{style:{display:"grid",gap:"10px"}},ie.map(Me=>w.default.createElement("div",{key:Me.name,style:{display:"flex",alignItems:"center",gap:"12px"}},w.default.createElement("div",{style:{width:"220px",flexShrink:0,display:"flex",alignItems:"center",gap:"8px"}},w.default.createElement("span",{style:{color:Te}},kg(Me.type)),w.default.createElement("span",{style:{fontSize:"13px",color:Me.errors>0?"#ef4444":je,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}},Me.name)),w.default.createElement("div",{style:{flex:1,height:"18px",background:S?"rgba(255,255,255,0.06)":"#f3f4f6",borderRadius:"4px",position:"relative"}},w.default.createElement("div",{style:{position:"absolute",top:"3px",left:0,height:"12px",borderRadius:"3px",width:`${Math.max(Me.total/P*100,.5)}%`,background:Je[Me.type]||"#6366f1"}})),w.default.createElement("div",{style:{width:"260px",flexShrink:0,fontSize:"12px",color:Te,fontFamily:Oe.mono,textAlign:"right"}},Me.count,"\xD7 \xB7 avg ",xe(Me.avg)," \xB7 max ",xe(Me.max)," \xB7 \u03A3 ",xe(Me.total)))))))};if(u)return w.default.createElement("div",{className:"flex items-center justify-center h-64"},w.default.createElement("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-red-500"}));let Pi={requests:D.reduce((S,P)=>S+P.requests,0),errors:D.reduce((S,P)=>S+P.errors,0),avgLatency:(()=>{let S=D.filter(P=>P.requests>0);return S.length===0?0:Math.round(S.reduce((P,W)=>P+W.avgLatency,0)/S.length)})(),throughput:(D.reduce((S,P)=>S+P.requests,0)/y.minutes).toFixed(1)},Zf=n.length===0;return r?w.default.createElement("div",{className:"preview-content",style:{borderRadius:"12px",overflow:"hidden",minHeight:"calc(100vh - 200px)"}},w.default.createElement("div",{style:{padding:"24px 24px 0 24px",borderBottom:"1px solid rgba(255,255,255,0.1)",marginBottom:"16px",paddingBottom:"16px"}},w.default.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between"}},w.default.createElement("div",null,!t&&w.default.createElement("h1",{style:{fontSize:"24px",fontWeight:"bold",color:"white",margin:0}},"Traces"),w.default.createElement("p",{style:{fontSize:"14px",color:"rgba(255,255,255,0.6)",marginTop:"4px"}},I!==null?`Viewing ${D[I]?.time} \u2022 ${Ne.length} requests`:`Last ${y.label} \u2022 Click timeline to drill down`)),w.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"12px"}},w.default.createElement("div",{style:{display:"flex",background:"rgba(255,255,255,0.1)",borderRadius:"8px",padding:"2px"}},["timeline","agents","actions","tools","spans"].map(S=>w.default.createElement("button",{key:S,onClick:()=>X(S),style:{padding:"6px 12px",background:z===S?"#ef4444":"transparent",color:"white",borderRadius:"6px",border:"none",fontSize:"13px",cursor:"pointer",textTransform:"capitalize"}},S))),!e&&w.default.createElement("select",{value:_.agent,onChange:S=>M({..._,agent:S.target.value,action:"all"}),style:{padding:"8px 12px",background:"rgba(255,255,255,0.1)",border:"1px solid rgba(255,255,255,0.2)",borderRadius:"8px",color:"white",fontSize:"14px",maxWidth:"180px",textOverflow:"ellipsis"}},w.default.createElement("option",{value:"all"},"All Agents"),i.map(S=>w.default.createElement("option",{key:S,value:S},S))),w.default.createElement("select",{value:_.action,onChange:S=>M({..._,action:S.target.value}),style:{padding:"8px 12px",background:_.action!=="all"?"rgba(239, 68, 68, 0.3)":"rgba(255,255,255,0.1)",border:_.action!=="all"?"1px solid #ef4444":"1px solid rgba(255,255,255,0.2)",borderRadius:"8px",color:"white",fontSize:"14px",maxWidth:"220px",textOverflow:"ellipsis"}},w.default.createElement("option",{value:"all"},"All Actions"),ee.map(S=>w.default.createElement("option",{key:S,value:S},S))),w.default.createElement("select",{value:_.status,onChange:S=>M({..._,status:S.target.value}),style:{padding:"8px 12px",background:"rgba(255,255,255,0.1)",border:"1px solid rgba(255,255,255,0.2)",borderRadius:"8px",color:"white",fontSize:"14px"}},w.default.createElement("option",{value:"all"},"All Status"),w.default.createElement("option",{value:"success"},"Success"),w.default.createElement("option",{value:"error"},"Error")),w.default.createElement("select",{value:E,onChange:S=>L(S.target.value),style:{padding:"8px 12px",background:"rgba(255,255,255,0.1)",border:"1px solid rgba(255,255,255,0.2)",borderRadius:"8px",color:"white",fontSize:"14px"}},w.default.createElement("option",{value:"time"},"Newest first"),w.default.createElement("option",{value:"latency"},"Slowest first")),I!==null&&w.default.createElement("button",{onClick:()=>j(null),style:{padding:"8px 16px",background:"rgba(255,255,255,0.2)",color:"white",borderRadius:"8px",border:"none",fontSize:"14px",fontWeight:"500",cursor:"pointer"}},"Clear Selection")))),d&&w.default.createElement("div",{style:{margin:"0 24px 16px 24px",padding:"12px 16px",background:"rgba(239, 68, 68, 0.1)",borderRadius:"8px",color:"#ef4444",fontSize:"13px"}},"Failed to load traces: ",d),w.default.createElement("div",{style:{padding:"0 24px",marginBottom:"24px"}},w.default.createElement("div",{style:{background:"rgba(0,0,0,0.3)",borderRadius:"12px",padding:"16px",border:"1px solid rgba(255,255,255,0.1)"}},w.default.createElement("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"12px"}},w.default.createElement("div",{style:{fontSize:"14px",fontWeight:"600",color:"white"}},"Agent Requests / Minute"),w.default.createElement("div",{style:{display:"flex",gap:"16px",flexWrap:"wrap"}},Object.entries(O).map(([S,P])=>w.default.createElement("div",{key:S,style:{display:"flex",alignItems:"center",gap:"6px"}},w.default.createElement("div",{style:{width:"10px",height:"10px",borderRadius:"2px",background:P}}),w.default.createElement("span",{style:{fontSize:"11px",color:"rgba(255,255,255,0.7)"}},S.replace("Agent","")))))),w.default.createElement("div",{style:{height:"150px"}},w.default.createElement(pu,{width:"100%",height:"100%"},w.default.createElement(oc,{data:D,onClick:K,style:{cursor:"pointer"}},w.default.createElement("defs",null,Object.entries(O).map(([S,P])=>w.default.createElement("linearGradient",{key:S,id:`gradient-${S}`,x1:"0",y1:"0",x2:"0",y2:"1"},w.default.createElement("stop",{offset:"5%",stopColor:P,stopOpacity:.8}),w.default.createElement("stop",{offset:"95%",stopColor:P,stopOpacity:.1})))),w.default.createElement(Il,{strokeDasharray:"3 3",stroke:"rgba(255,255,255,0.1)"}),w.default.createElement(Bl,{dataKey:"time",stroke:"rgba(255,255,255,0.5)",tick:{fontSize:10,fill:"rgba(255,255,255,0.5)"},tickLine:!1,interval:N}),w.default.createElement(Ul,{stroke:"rgba(255,255,255,0.5)",tick:{fontSize:10,fill:"rgba(255,255,255,0.5)"},tickLine:!1,axisLine:!1,allowDecimals:!1}),w.default.createElement(Gu,{contentStyle:{background:"rgba(0,0,0,0.9)",border:"1px solid rgba(255,255,255,0.2)",borderRadius:"8px",padding:"12px"},labelStyle:{color:"white",fontWeight:"600",marginBottom:"8px"},itemStyle:{color:"rgba(255,255,255,0.8)",fontSize:"12px"},formatter:(S,P)=>[S,P.replace("Agent","")]}),Object.keys(O).map(S=>w.default.createElement(Ll,{key:S,type:"monotone",dataKey:S,stackId:"1",stroke:O[S],fill:`url(#gradient-${S})`}))))),w.default.createElement("div",{style:{display:"flex",gap:"24px",marginTop:"12px",paddingTop:"12px",borderTop:"1px solid rgba(255,255,255,0.1)"}},w.default.createElement("div",null,w.default.createElement("div",{style:{fontSize:"20px",fontWeight:"700",color:"white"}},Pi.requests),w.default.createElement("div",{style:{fontSize:"11px",color:"rgba(255,255,255,0.5)"}},"Total Requests")),w.default.createElement("div",null,w.default.createElement("div",{style:{fontSize:"20px",fontWeight:"700",color:"#ef4444"}},Pi.errors),w.default.createElement("div",{style:{fontSize:"11px",color:"rgba(255,255,255,0.5)"}},"Errors")),w.default.createElement("div",null,w.default.createElement("div",{style:{fontSize:"20px",fontWeight:"700",color:"#10b981"}},Pi.avgLatency,"ms"),w.default.createElement("div",{style:{fontSize:"11px",color:"rgba(255,255,255,0.5)"}},"Avg Latency")),w.default.createElement("div",null,w.default.createElement("div",{style:{fontSize:"20px",fontWeight:"700",color:"#f59e0b"}},Pi.throughput,"/min"),w.default.createElement("div",{style:{fontSize:"11px",color:"rgba(255,255,255,0.5)"}},"Throughput"))))),z==="agents"&&w.default.createElement("div",{style:{padding:"0 24px",marginBottom:"24px"}},To,w.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit, minmax(280px, 1fr))",gap:"16px"}},F.map(S=>w.default.createElement(Vs,{key:S.agent,name:S.agent,accentColor:O[S.agent],subtitle:S.agents?.length?S.agents.join(", "):void 0,badge:w.default.createElement("span",{style:{fontSize:"12px",padding:"2px 8px",background:O[S.agent]+"30",color:O[S.agent],borderRadius:"4px",whiteSpace:"nowrap"}},S.count," calls"),onClick:()=>It(S.agent),stats:Ke(S),footer:w.default.createElement(w.default.Fragment,null,w.default.createElement("span",null,l[S.agent]?"Open agent \u2192":"Not linked to an agent record"),w.default.createElement("button",{onClick:P=>{P.stopPropagation(),Kr(S.agent)},style:{background:"none",border:"none",padding:0,cursor:"pointer",color:_.agent===S.agent?"#ef4444":"inherit",font:"inherit"}},_.agent===S.agent?"Clear filter":"Filter traces"))})))),z==="actions"&&w.default.createElement("div",{style:{padding:"0 24px",marginBottom:"24px"}},w.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit, minmax(320px, 1fr))",gap:"12px"}},H.map(S=>{let P=_.action===S.key;return w.default.createElement("div",{key:S.key,onClick:()=>{M({..._,action:P?"all":S.key}),X("timeline")},style:{background:P?"rgba(239, 68, 68, 0.15)":"rgba(0,0,0,0.3)",borderRadius:"10px",padding:"14px",border:P?"1px solid #ef4444":`1px solid ${O[S.agent]}30`,cursor:"pointer",transition:"all 0.15s"}},w.default.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"10px"}},w.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"8px"}},w.default.createElement("div",{style:{width:"6px",height:"6px",borderRadius:"50%",background:O[S.agent]}}),w.default.createElement("span",{style:{fontSize:"14px",fontWeight:"600",color:"white"}},S.agent),w.default.createElement("span",{style:{fontSize:"13px",color:"rgba(255,255,255,0.7)"}},"#",S.action)),w.default.createElement("span",{style:{fontSize:"12px",padding:"2px 8px",background:O[S.agent]+"30",color:O[S.agent],borderRadius:"4px"}},S.count," calls")),w.default.createElement("div",{style:{display:"flex",gap:"16px",flexWrap:"wrap"}},w.default.createElement("div",null,w.default.createElement("div",{style:{fontSize:"10px",color:"rgba(255,255,255,0.5)"}},"Avg"),w.default.createElement("div",{style:{fontSize:"13px",color:"white",fontWeight:"500"}},Math.round(S.totalDuration/S.count),"ms")),w.default.createElement("div",null,w.default.createElement("div",{style:{fontSize:"10px",color:"rgba(255,255,255,0.5)"}},"Min/Max"),w.default.createElement("div",{style:{fontSize:"13px",color:"rgba(255,255,255,0.8)"}},Math.round(S.minDuration),"ms / ",Math.round(S.maxDuration),"ms")),w.default.createElement("div",null,w.default.createElement("div",{style:{fontSize:"10px",color:"rgba(255,255,255,0.5)"}},"Errors"),w.default.createElement("div",{style:{fontSize:"13px",color:S.errors>0?"#ef4444":"#10b981"}},S.errors," (",(S.errors/S.count*100).toFixed(0),"%)")),w.default.createElement("div",null,w.default.createElement("div",{style:{fontSize:"10px",color:"rgba(255,255,255,0.5)"}},"Tokens"),w.default.createElement("div",{style:{fontSize:"13px",color:"rgba(255,255,255,0.8)"}},we(S.tokens).toLocaleString()))))}))),z==="spans"&&cA(!0),z==="timeline"&&!Zf&&w.default.createElement("div",{className:"preview-traces"},Ne.length===0?w.default.createElement("div",{style:{textAlign:"center",padding:"32px"}},w.default.createElement("div",{style:{color:"rgba(255,255,255,0.5)",fontSize:"14px"}},"No traces match filters")):Ne.map(S=>w.default.createElement(w.default.Fragment,{key:S.id},w.default.createElement("div",{className:"trace-header-row",id:`trace-row-${S.id}`,onClick:()=>v(p===S.id?null:S.id),style:{cursor:"pointer"}},w.default.createElement("div",{className:"trace-id"},w.default.createElement("span",{className:"trace-badge"},"TRACE"),w.default.createElement("span",{className:"trace-hash"},S.short_id),w.default.createElement("span",{style:{marginLeft:"12px",color:"rgba(255,255,255,0.9)",fontWeight:"500"}},S.display_name)),w.default.createElement("div",{className:"trace-meta"},S.model&&w.default.createElement("span",{className:"meta-item",title:"Model that generated this trace",style:{fontFamily:Oe.mono,background:"rgba(99,102,241,0.2)",color:"#a5b4fc",padding:"2px 8px",borderRadius:"4px"}},S.model),(()=>{let P=Xf(S);return P?w.default.createElement(Oi,{compact:!0,...P,label:"Context",darkMode:!0}):null})(),w.default.createElement("span",{className:"meta-item"},w.default.createElement("i",{className:"fa-solid fa-clock"})," ",xe(S.duration_ms)),w.default.createElement("span",{className:"meta-item"},Ae(S.tokens)," tokens"),S.estimated_cost!=null&&w.default.createElement("span",{className:"meta-item"},w.default.createElement("i",{className:"fa-solid fa-coins"})," ",aA(S.estimated_cost)),w.default.createElement("span",{className:`meta-item ${Gl(S)?"success":"error"}`},w.default.createElement("i",{className:`fa-solid ${Gl(S)?"fa-check":"fa-xmark"}`})," ",Gl(S)?"OK":"ERROR"))),(()=>{let P=Og(S);return!P.input&&!P.output?null:w.default.createElement("div",{onClick:()=>v(p===S.id?null:S.id),style:{cursor:"pointer",padding:"0 16px 10px",fontSize:"12px",fontFamily:Oe.mono,color:"rgba(255,255,255,0.6)",display:"grid",gap:"2px"}},P.input&&w.default.createElement("div",{style:{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},w.default.createElement("span",{style:{color:"rgba(255,255,255,0.35)"}},"input:")," ",$a(P.input)),P.output&&w.default.createElement("div",{style:{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},w.default.createElement("span",{style:{color:"rgba(255,255,255,0.35)"}},"output:")," ",$a(P.output)))})(),p===S.id&&w.default.createElement("div",{className:"trace-timeline"},w.default.createElement("div",{className:"timeline-scale"},w.default.createElement("span",null,"0ms"),w.default.createElement("span",null,Math.round((S.duration_ms||0)*.33),"ms"),w.default.createElement("span",null,Math.round((S.duration_ms||0)*.66),"ms"),w.default.createElement("span",null,xe(S.duration_ms))),(S.spans||[]).map((P,W)=>{let Z=`${S.id}:${W}`,je=P.attributes||{},Te=Object.keys(je).length>0,Je=g===Z;return w.default.createElement(w.default.Fragment,{key:W},w.default.createElement("div",{className:`span-row ${P.nested?`nested-${Math.min(P.nested,3)}`:""}`,onClick:Te?()=>x(Je?null:Z):void 0,style:Te?{cursor:"pointer"}:void 0,title:Te?"Click for span details":void 0},w.default.createElement("div",{className:"span-label"},w.default.createElement("span",{className:`span-icon ${P.type}`},kg(P.type)),w.default.createElement("span",{className:`span-name ${P.error?"error":""}`},P.name)),w.default.createElement("div",{className:"span-bar-container"},w.default.createElement("div",{className:`span-bar ${P.type} ${P.error?"error":""}`,style:{left:`${S.duration_ms?P.start/S.duration_ms*100:0}%`,width:`${S.duration_ms?Math.max(P.duration/S.duration_ms*100,2):2}%`}})),w.default.createElement("span",{style:{flexShrink:0,width:"130px",textAlign:"right",fontSize:"11px",fontFamily:Oe.mono,color:"rgba(255,255,255,0.55)",paddingLeft:"8px"}},iA(P,S))),(()=>{if(Je)return null;let Me=oA(P,S);return!Me.input&&!Me.output?null:w.default.createElement("div",{onClick:Te?()=>x(Je?null:Z):void 0,style:{padding:"0 0 4px 24px",fontFamily:Oe.mono,fontSize:"11px",color:"rgba(255,255,255,0.5)",display:"grid",gap:"1px",cursor:Te?"pointer":"default"}},Me.input&&w.default.createElement("div",{style:{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},w.default.createElement("span",{style:{color:ta(Me.inputTone,!0).color}},Me.inputLabel)," ",$a(Me.input,140)),Me.output&&w.default.createElement("div",{style:{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},w.default.createElement("span",{style:{color:ta(Me.outputTone,!0).color}},Me.outputLabel)," ",$a(Me.output,140)))})(),Je&&sA(P,!0,Tg(S)))}),(()=>{let P=Xf(S);return P?w.default.createElement("div",{style:{margin:"10px 0"},onClick:W=>W.stopPropagation()},w.default.createElement(Oi,{...P,label:"Context pressure",estimated:!0,darkMode:!0})):null})(),uA(S,!0),w.default.createElement("div",{className:"span-row nested-3"},w.default.createElement("div",{className:"span-label"},w.default.createElement("span",{className:"span-icon",style:{fontFamily:Oe.mono}},"+--"),w.default.createElement("span",{className:"span-name tokens",style:{fontFamily:Oe.mono}},we(S.tokens).toLocaleString()," tokens")),w.default.createElement("div",{className:"span-bar-container"},w.default.createElement("div",{className:"token-breakdown",style:{fontFamily:Oe.mono}},S.tokens.thinking>0&&w.default.createElement("span",{className:"token-thinking"},"T:",S.tokens.thinking.toLocaleString()),w.default.createElement("span",{className:"token-in"},"in:",S.tokens.input.toLocaleString()),w.default.createElement("span",{className:"token-out"},"out:",S.tokens.output.toLocaleString())))),S.error&&w.default.createElement("div",{style:{padding:"12px 16px",background:"rgba(239, 68, 68, 0.1)",borderRadius:"6px",marginTop:"12px"}},w.default.createElement("span",{style:{color:"#ef4444",fontSize:"13px"}},w.default.createElement("i",{className:"fa-solid fa-exclamation-triangle",style:{marginRight:"8px"}}),S.error)))))),Zf&&z==="timeline"&&w.default.createElement("div",{style:{textAlign:"center",padding:"48px 0"}},w.default.createElement("div",{style:{color:"rgba(255,255,255,0.5)",fontSize:"18px"}},"No traces yet"),w.default.createElement("p",{style:{color:"rgba(255,255,255,0.4)",fontSize:"14px",marginTop:"8px"}},"Run an agent, or point your app's ActiveAgent telemetry at this workspace to see traces appear here"))):w.default.createElement("div",{className:"space-y-6"},w.default.createElement("div",{className:"flex items-center justify-between flex-wrap gap-y-2"},w.default.createElement("div",null,w.default.createElement("h1",{className:"text-2xl font-bold text-gray-900"},"Traces"),w.default.createElement("p",{className:"text-sm text-gray-500"},I!==null?`Viewing ${D[I]?.time} \u2022 ${Ne.length} requests`:`Last ${y.label} \u2022 Click timeline to drill down`)),w.default.createElement("div",{className:"flex items-center space-x-3 flex-wrap gap-y-2 min-w-0"},w.default.createElement(Ba,null),w.default.createElement("div",{className:"flex bg-gray-100 rounded-lg p-1"},["timeline","agents","actions","tools","spans"].map(S=>w.default.createElement("button",{key:S,onClick:()=>X(S),className:`px-3 py-1 text-sm rounded-md transition-colors capitalize ${z===S?"bg-white shadow text-gray-900":"text-gray-600"}`},S))),w.default.createElement("select",{value:_.agent,onChange:S=>M({..._,agent:S.target.value,action:"all"}),className:"px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-red-500",style:{maxWidth:"180px",textOverflow:"ellipsis"}},w.default.createElement("option",{value:"all"},"All Agents"),i.map(S=>w.default.createElement("option",{key:S,value:S},S))),w.default.createElement("select",{value:_.action,onChange:S=>M({..._,action:S.target.value}),className:`px-3 py-2 border rounded-lg text-sm focus:ring-2 focus:ring-red-500 ${_.action!=="all"?"border-red-500 bg-red-50":"border-gray-300"}`,style:{maxWidth:"220px",textOverflow:"ellipsis"}},w.default.createElement("option",{value:"all"},"All Actions"),ee.map(S=>w.default.createElement("option",{key:S,value:S},S))),w.default.createElement("select",{value:_.status,onChange:S=>M({..._,status:S.target.value}),className:"px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-red-500"},w.default.createElement("option",{value:"all"},"All Status"),w.default.createElement("option",{value:"success"},"Success"),w.default.createElement("option",{value:"error"},"Error")),w.default.createElement("select",{value:E,onChange:S=>L(S.target.value),className:"px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-red-500"},w.default.createElement("option",{value:"time"},"Newest first"),w.default.createElement("option",{value:"latency"},"Slowest first")),I!==null&&w.default.createElement("button",{onClick:()=>j(null),className:"px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors text-sm font-medium"},"Clear Selection"))),d&&w.default.createElement("div",{className:"p-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700"},"Failed to load traces: ",d),w.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},w.default.createElement("div",{className:"flex justify-between items-center mb-3"},w.default.createElement("h3",{className:"text-sm font-semibold text-gray-700"},"Agent Requests / Minute"),w.default.createElement("div",{className:"flex gap-4 flex-wrap"},Object.entries(O).map(([S,P])=>w.default.createElement("div",{key:S,className:"flex items-center gap-1.5"},w.default.createElement("div",{className:"w-2.5 h-2.5 rounded-sm",style:{background:P}}),w.default.createElement("span",{className:"text-xs text-gray-500"},S.replace("Agent","")))))),w.default.createElement("div",{className:"h-36"},w.default.createElement(pu,{width:"100%",height:"100%"},w.default.createElement(oc,{data:D,onClick:K,style:{cursor:"pointer"}},w.default.createElement("defs",null,Object.entries(O).map(([S,P])=>w.default.createElement("linearGradient",{key:S,id:`gradient-light-${S}`,x1:"0",y1:"0",x2:"0",y2:"1"},w.default.createElement("stop",{offset:"5%",stopColor:P,stopOpacity:.6}),w.default.createElement("stop",{offset:"95%",stopColor:P,stopOpacity:.05})))),w.default.createElement(Il,{strokeDasharray:"3 3",stroke:"#e5e7eb"}),w.default.createElement(Bl,{dataKey:"time",stroke:"#9ca3af",tick:{fontSize:10,fill:"#6b7280"},tickLine:!1,interval:N}),w.default.createElement(Ul,{stroke:"#9ca3af",tick:{fontSize:10,fill:"#6b7280"},tickLine:!1,axisLine:!1,allowDecimals:!1}),w.default.createElement(Gu,{contentStyle:{background:"white",border:"1px solid #e5e7eb",borderRadius:"8px",boxShadow:"0 4px 6px -1px rgba(0,0,0,0.1)"},labelStyle:{color:"#111827",fontWeight:"600",marginBottom:"8px"},formatter:(S,P)=>[S,P.replace("Agent","")]}),Object.keys(O).map(S=>w.default.createElement(Ll,{key:S,type:"monotone",dataKey:S,stackId:"1",stroke:O[S],fill:`url(#gradient-light-${S})`}))))),w.default.createElement("div",{className:"flex gap-6 mt-3 pt-3 border-t border-gray-100"},w.default.createElement("div",null,w.default.createElement("div",{className:"text-lg font-bold text-gray-900"},Pi.requests),w.default.createElement("div",{className:"text-xs text-gray-500"},"Total Requests")),w.default.createElement("div",null,w.default.createElement("div",{className:"text-lg font-bold text-red-500"},Pi.errors),w.default.createElement("div",{className:"text-xs text-gray-500"},"Errors")),w.default.createElement("div",null,w.default.createElement("div",{className:"text-lg font-bold text-green-600"},Pi.avgLatency,"ms"),w.default.createElement("div",{className:"text-xs text-gray-500"},"Avg Latency")),w.default.createElement("div",null,w.default.createElement("div",{className:"text-lg font-bold text-amber-500"},Pi.throughput,"/min"),w.default.createElement("div",{className:"text-xs text-gray-500"},"Throughput")))),z==="agents"&&w.default.createElement("div",null,To,w.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"},F.map(S=>w.default.createElement(Vs,{key:S.agent,name:S.agent,accentColor:O[S.agent],subtitle:S.agents?.length?S.agents.join(", "):void 0,badge:w.default.createElement("span",{className:"text-sm px-2 py-0.5 rounded",style:{background:O[S.agent]+"20",color:O[S.agent]}},S.count," calls"),onClick:()=>It(S.agent),stats:Ke(S),footer:w.default.createElement(w.default.Fragment,null,w.default.createElement("span",null,l[S.agent]?"Open agent \u2192":"Not linked to an agent record"),w.default.createElement("button",{onClick:P=>{P.stopPropagation(),Kr(S.agent)},className:_.agent===S.agent?"text-red-600":"hover:text-gray-700"},_.agent===S.agent?"Clear filter":"Filter traces"))})))),z==="actions"&&w.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3"},H.map(S=>{let P=_.action===S.key;return w.default.createElement("div",{key:S.key,onClick:()=>{M({..._,action:P?"all":S.key}),X("timeline")},className:`rounded-xl p-4 cursor-pointer transition-all ${P?"bg-red-50 border-2 border-red-500":"bg-white border border-gray-200 hover:border-gray-300"}`,style:{borderLeftColor:O[S.agent],borderLeftWidth:P?"2px":"4px"}},w.default.createElement("div",{className:"flex items-center justify-between mb-3"},w.default.createElement("div",{className:"flex items-center gap-2"},w.default.createElement("div",{className:"w-2 h-2 rounded-full",style:{background:O[S.agent]}}),w.default.createElement("span",{className:"font-semibold text-gray-900"},S.agent),w.default.createElement("span",{className:"text-gray-500"},"#",S.action)),w.default.createElement("span",{className:"text-xs px-2 py-0.5 rounded",style:{background:O[S.agent]+"20",color:O[S.agent]}},S.count," calls")),w.default.createElement("div",{className:"flex flex-wrap gap-4 text-sm"},w.default.createElement("div",null,w.default.createElement("div",{className:"text-xs text-gray-500"},"Avg"),w.default.createElement("div",{className:"font-medium"},Math.round(S.totalDuration/S.count),"ms")),w.default.createElement("div",null,w.default.createElement("div",{className:"text-xs text-gray-500"},"Min/Max"),w.default.createElement("div",{className:"text-gray-600"},Math.round(S.minDuration),"ms / ",Math.round(S.maxDuration),"ms")),w.default.createElement("div",null,w.default.createElement("div",{className:"text-xs text-gray-500"},"Errors"),w.default.createElement("div",{className:S.errors>0?"text-red-600":"text-green-600"},S.errors," (",(S.errors/S.count*100).toFixed(0),"%)")),w.default.createElement("div",null,w.default.createElement("div",{className:"text-xs text-gray-500"},"Tokens"),w.default.createElement("div",{className:"text-gray-600"},we(S.tokens).toLocaleString()))))})),z==="tools"&&(q.length===0?w.default.createElement("div",{className:"text-center py-8 text-gray-500 text-sm"},"No tool calls in this window"):w.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 overflow-hidden"},w.default.createElement("div",{className:"flex items-center gap-4 px-4 py-2 text-xs font-medium text-gray-500 border-b border-gray-100"},w.default.createElement("div",{className:"flex-1"},"Tool"),w.default.createElement("div",{className:"w-20 text-right"},"Calls"),w.default.createElement("div",{className:"w-24 text-right"},"Total time"),w.default.createElement("div",{className:"w-20 text-right"},"Avg"),w.default.createElement("div",{className:"w-20 text-right"},"Slowest")),q.map(S=>{let P=q[0].totalDuration?S.totalDuration/q[0].totalDuration*100:0;return w.default.createElement("div",{key:S.name,className:"px-4 py-2 border-b border-gray-50 last:border-0"},w.default.createElement("div",{className:"flex items-center gap-4 text-sm"},w.default.createElement("div",{className:"flex-1 min-w-0"},w.default.createElement("div",{className:"font-medium text-gray-900 truncate",title:S.name},S.name),S.agents.length>0&&w.default.createElement("div",{className:"text-xs text-gray-400 truncate",title:S.agents.join(", ")},S.agents.join(", "))),w.default.createElement("div",{className:"w-20 text-right text-gray-700",style:{fontFamily:Oe.mono}},S.count.toLocaleString(),S.errors>0&&w.default.createElement("span",{className:"text-red-600 ml-1",title:`${S.errors} failed`},"(",S.errors,")")),w.default.createElement("div",{className:"w-24 text-right text-gray-900 font-medium",style:{fontFamily:Oe.mono}},xe(S.totalDuration)),w.default.createElement("div",{className:"w-20 text-right text-gray-500",style:{fontFamily:Oe.mono}},xe(S.avgDuration)),w.default.createElement("div",{className:"w-20 text-right text-gray-500",style:{fontFamily:Oe.mono}},xe(S.maxDuration))),w.default.createElement("div",{className:"h-1 bg-gray-100 rounded mt-1.5"},w.default.createElement("div",{className:"h-1 bg-green-500 rounded",style:{width:`${Math.max(P,1)}%`}})))}))),z==="spans"&&cA(!1),z==="timeline"&&!Zf&&w.default.createElement("div",{className:"space-y-4"},Ne.length===0?w.default.createElement("div",{className:"text-center py-8 text-gray-500"},"No traces match filters"):Ne.map(S=>w.default.createElement("div",{key:S.id,id:`trace-row-${S.id}`,className:"bg-white rounded-xl border border-gray-200 overflow-hidden hover:border-gray-300 transition-colors"},w.default.createElement("div",{className:"p-4 flex items-center justify-between cursor-pointer hover:bg-gray-50",onClick:()=>v(p===S.id?null:S.id)},w.default.createElement("div",{className:"flex items-center space-x-4"},w.default.createElement("div",{className:"flex items-center space-x-2"},w.default.createElement("span",{className:"px-2 py-1 bg-gray-100 text-gray-600 text-xs font-mono rounded"},"TRACE"),w.default.createElement("span",{className:"text-sm font-mono text-gray-500"},S.short_id)),w.default.createElement("span",{className:"text-sm font-medium text-gray-900"},S.display_name)),w.default.createElement("div",{className:"flex items-center space-x-4"},S.model&&w.default.createElement("span",{className:"text-xs px-2 py-0.5 rounded bg-indigo-50 text-indigo-700",style:{fontFamily:Oe.mono},title:"Model that generated this trace"},S.model),(()=>{let P=Xf(S);return P?w.default.createElement(Oi,{compact:!0,...P,label:"Context"}):null})(),w.default.createElement("span",{className:"text-sm text-gray-500"},w.default.createElement("i",{className:"fa-solid fa-clock mr-1"}),xe(S.duration_ms)),w.default.createElement("span",{className:"text-sm text-gray-500"},Ae(S.tokens)," tokens"),S.estimated_cost!=null&&w.default.createElement("span",{className:"text-sm text-gray-500"},w.default.createElement("i",{className:"fa-solid fa-coins mr-1"}),aA(S.estimated_cost)),w.default.createElement("span",{className:`text-sm ${Gl(S)?"text-green-600":"text-red-600"}`},w.default.createElement("i",{className:`fa-solid ${Gl(S)?"fa-check":"fa-xmark"} mr-1`}),Gl(S)?"OK":"ERROR"))),(()=>{let P=Og(S);return!P.input&&!P.output?null:w.default.createElement("div",{className:"px-4 pb-3 cursor-pointer",onClick:()=>v(p===S.id?null:S.id),style:{fontFamily:Oe.mono,fontSize:"12px",display:"grid",gap:"2px"}},P.input&&w.default.createElement("div",{className:"text-gray-500",style:{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},w.default.createElement("span",{className:"text-gray-400"},"input:")," ",$a(P.input)),P.output&&w.default.createElement("div",{className:"text-gray-500",style:{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},w.default.createElement("span",{className:"text-gray-400"},"output:")," ",$a(P.output)))})(),p===S.id&&w.default.createElement("div",{className:"border-t border-gray-100 p-4 bg-gray-50"},w.default.createElement("div",{className:"flex items-center justify-between mb-2"},w.default.createElement("div",{className:"flex items-center gap-1"},[{id:"spans",label:"Spans",hint:"Timing waterfall with span details"},{id:"conversation",label:"Conversation",hint:"The run as a message stream \u2014 prompt, tool calls, response"}].map(P=>w.default.createElement("button",{key:P.id,onClick:W=>{W.stopPropagation(),P.id==="conversation"?ge(S.id):J(Z=>({...Z,[S.id]:"spans"}))},title:P.hint,className:`px-2 py-0.5 text-xs rounded transition-colors ${(Y[S.id]||"spans")===P.id?"bg-white shadow text-gray-900":"text-gray-500 hover:bg-white"}`},P.label)),w.default.createElement("span",{className:"text-xs text-gray-500 ml-2"},(S.spans||[]).length," spans")),(Y[S.id]||"spans")==="spans"&&w.default.createElement("div",{className:"flex items-center gap-1"},w.default.createElement("span",{className:"text-xs text-gray-400 mr-1"},"Sort"),[{id:"time",label:"Time",hint:"Chronological \u2014 reads as a waterfall"},{id:"duration",label:"Slowest",hint:"Longest-running first"},{id:"name",label:"Name",hint:"Group repeated tool calls together"}].map(P=>w.default.createElement("button",{key:P.id,onClick:W=>{W.stopPropagation(),$(P.id)},title:P.hint,className:`px-2 py-0.5 text-xs rounded transition-colors ${T===P.id?"bg-white shadow text-gray-900":"text-gray-500 hover:bg-white"}`},P.label)))),(Y[S.id]||"spans")==="conversation"&&w.default.createElement("div",{className:"bg-white rounded-lg border border-gray-200 p-4"},ze(S,!1)),(Y[S.id]||"spans")==="spans"&&w.default.createElement(w.default.Fragment,null,T==="time"?w.default.createElement("div",{className:"flex justify-between text-xs text-gray-400 mb-2"},w.default.createElement("span",null,"0ms"),w.default.createElement("span",null,Math.round((S.duration_ms||0)*.33),"ms"),w.default.createElement("span",null,Math.round((S.duration_ms||0)*.66),"ms"),w.default.createElement("span",null,xe(S.duration_ms))):w.default.createElement("div",{className:"text-xs text-gray-400 mb-2"},"Bar length compares duration on a log scale"),w.default.createElement("div",{className:"space-y-2"},se(S.spans).map((P,W,Z)=>{let je=Math.max(...Z.map(Ge=>Ge.duration||0),1),Te=`${S.id}:${P.span_id||W}`,Je=P.attributes||{},Me=Object.keys(Je).length>0,zt=g===Te;return w.default.createElement(w.default.Fragment,{key:Te},w.default.createElement("div",{className:`group py-0.5 ${Me?"cursor-pointer hover:bg-gray-100 rounded":""}`,style:{paddingLeft:T==="time"?`${(P.nested||0)*16}px`:0},onClick:Me?()=>x(zt?null:Te):void 0,title:Me?`${P.name} \u2014 click for span details`:P.name},w.default.createElement("div",{className:"flex items-baseline gap-2 min-w-0"},w.default.createElement("span",{className:`flex-shrink-0 ${P.type==="thinking"?"":"text-gray-400"}`},kg(P.type)),w.default.createElement("span",{className:`text-sm font-medium ${P.error?"text-red-600":"text-gray-800"}`},P.name),w.default.createElement("span",{className:"text-xs text-gray-400 ml-auto flex-shrink-0",style:{fontFamily:Oe.mono}},xe(P.duration))),w.default.createElement("div",{className:"h-3 relative bg-gray-100 rounded mt-0.5"},w.default.createElement("div",{className:`absolute h-3 rounded transition-opacity ${P.type==="root"?"bg-gray-400":P.type==="prompt"?"bg-blue-400":P.type==="generate"?"bg-purple-500":P.type==="llm"?"bg-red-500":P.type==="thinking"?"bg-amber-400":P.type==="tool"?"bg-green-500":P.type==="response"?"bg-teal-400":"bg-gray-300"} ${P.error?"bg-red-400":""}`,style:{left:T==="time"?`${S.duration_ms?P.start/S.duration_ms*100:0}%`:0,width:T==="time"?`${S.duration_ms?Math.max(P.duration/S.duration_ms*100,1):1}%`:`${TZ(P.duration,je)}%`}})),w.default.createElement("span",{className:"flex-shrink-0 text-right text-xs text-gray-400 pl-2",style:{width:"130px",fontFamily:Oe.mono}},iA(P,S)),(()=>{if(zt)return null;let Ge=oA(P,S);return!Ge.input&&!Ge.output?null:w.default.createElement("div",{className:"min-w-0",style:{fontFamily:Oe.mono,fontSize:"11px",display:"grid",gap:"1px",marginTop:"2px"}},Ge.input&&w.default.createElement("div",{className:"text-gray-500",style:{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},w.default.createElement("span",{style:{color:ta(Ge.inputTone,!1).color}},Ge.inputLabel)," ",$a(Ge.input,160)),Ge.output&&w.default.createElement("div",{className:"text-gray-500",style:{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},w.default.createElement("span",{style:{color:ta(Ge.outputTone,!1).color}},Ge.outputLabel)," ",$a(Ge.output,160)))})()),zt&&sA(P,!1,Tg(S)))}))),(()=>{let P=Xf(S);return P?w.default.createElement("div",{className:"mt-3",onClick:W=>W.stopPropagation()},w.default.createElement(Oi,{...P,label:"Context pressure",estimated:!0})):null})(),uA(S,!1),w.default.createElement("div",{className:"mt-4 pt-4 border-t border-gray-200 flex items-center space-x-6"},w.default.createElement("span",{className:"text-sm text-gray-500"},"Tokens:"),S.tokens.thinking>0&&w.default.createElement("span",{className:"text-sm text-amber-600",style:{fontFamily:Oe.mono}},"T:",S.tokens.thinking.toLocaleString()),w.default.createElement("span",{className:"text-sm text-blue-600",style:{fontFamily:Oe.mono}},"in:",S.tokens.input.toLocaleString()),w.default.createElement("span",{className:"text-sm text-green-600",style:{fontFamily:Oe.mono}},"out:",S.tokens.output.toLocaleString()),S.provider&&w.default.createElement("span",{className:"text-sm text-gray-500",style:{fontFamily:Oe.mono}},S.provider,S.model?` \xB7 ${S.model}`:"")),S.error&&w.default.createElement("div",{className:"mt-4 p-3 bg-red-50 border border-red-200 rounded-lg"},w.default.createElement("span",{className:"text-sm text-red-700"},S.error)))))),Zf&&z==="timeline"&&w.default.createElement("div",{className:"text-center py-12"},w.default.createElement("div",{className:"text-gray-400 text-lg"},"No traces yet"),w.default.createElement("p",{className:"text-gray-500 text-sm mt-2"},"Run an agent, or point your app's ActiveAgent telemetry at this workspace to see traces appear here")))}var ce=te(ae());var nr=te(ae()),ZP={root:"#9ca3af",prompt:"#60a5fa",generate:"#a855f7",llm:"#ef4444",thinking:"#fbbf24",tool:"#22c55e",response:"#2dd4bf",embedding:"#818cf8"},PS=e=>e==null?"":e<1e3?`${Math.round(e)}ms`:`${(e/1e3).toFixed(2)}s`;function jS({traceId:e,darkMode:t}){let[r,n]=(0,nr.useState)(null),[a,i]=(0,nr.useState)(!1),[o,l]=(0,nr.useState)(!1);if((0,nr.useEffect)(()=>{let p=!0;return fetch(`/api/traces/${encodeURIComponent(e)}`).then(v=>v.ok?v.json():Promise.reject(new Error("not found"))).then(({trace:v})=>p&&n(v)).catch(()=>p&&i(!0)),()=>{p=!1}},[e]),a)return null;let s=t?"rgba(255,255,255,0.08)":"#f3f4f6",u=t?"rgba(255,255,255,0.4)":"#9ca3af";if(!r)return nr.default.createElement("div",{className:"rounded mt-1",style:{height:"8px",background:s}});let c=r.spans||[],d=r.duration_ms||1,f=[...c].sort((p,v)=>(p.nested||0)-(v.nested||0));return nr.default.createElement("div",{className:"mt-1",onClick:p=>p.stopPropagation()},nr.default.createElement("div",{onClick:()=>l(!o),title:o?"Collapse spans":`${c.length} spans \xB7 ${PS(r.duration_ms)} \u2014 click to expand`,style:{display:"flex",alignItems:"center",gap:"8px",cursor:"pointer"}},nr.default.createElement("div",{style:{position:"relative",height:"8px",borderRadius:"999px",background:s,overflow:"hidden",flex:1}},f.map((p,v)=>nr.default.createElement("div",{key:p.span_id||v,title:`${p.name} \xB7 ${PS(p.duration)}`,style:{position:"absolute",top:0,bottom:0,left:`${Math.min(p.start/d*100,99)}%`,width:`${Math.max(p.duration/d*100,.8)}%`,background:p.error?"#ef4444":ZP[p.type]||"#9ca3af"}}))),nr.default.createElement("span",{className:"font-mono",style:{fontSize:"10px",color:u,whiteSpace:"nowrap"}},c.length," spans ",o?"\u25BE":"\u25B8")),o&&nr.default.createElement("div",{style:{display:"grid",gap:"3px",marginTop:"6px"}},c.map((p,v)=>nr.default.createElement("div",{key:p.span_id||v,style:{display:"flex",alignItems:"center",gap:"8px",paddingLeft:`${(p.nested||0)*10}px`}},nr.default.createElement("span",{className:"font-mono",style:{fontSize:"10px",color:p.error?"#ef4444":u,width:"160px",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap",flexShrink:0},title:p.name},p.name),nr.default.createElement("div",{style:{position:"relative",height:"4px",borderRadius:"2px",background:s,flex:1}},nr.default.createElement("div",{style:{position:"absolute",top:0,bottom:0,left:`${Math.min(p.start/d*100,99)}%`,width:`${Math.max(p.duration/d*100,.8)}%`,borderRadius:"2px",background:p.error?"#ef4444":ZP[p.type]||"#9ca3af"}})),nr.default.createElement("span",{className:"font-mono",style:{fontSize:"10px",color:u,width:"52px",textAlign:"right",flexShrink:0}},PS(p.duration)))),nr.default.createElement("a",{href:`/dashboard/traces?trace=${r.trace_id||e}`,className:"font-mono hover:underline",style:{fontSize:"10px",color:"#3b82f6",justifySelf:"start"}},"open in Traces \u2192")))}var jZ=e=>{let t=null;if((e?.generations||[]).forEach(i=>{let o=i.tokens||{},l=(o.input||0)+(o.output||0);l>0&&(!t||l>t.total)&&(t={input:o.input||0,output:o.output||0,cached:o.cached||0,thinking:o.thinking||0,model:i.model,total:l})}),!t)return null;let r=za(e?.instructions),n=0;(e?.messages||[]).forEach(i=>{i.role==="tool"&&(n+=za(i.content)+za(i.tool_arguments))}),n=Math.min(n,t.input);let a=Math.max(t.input-r-n,0);return{used:t.total,limit:Vf(t.model),cached:t.cached,thinking:t.thinking,segments:[{key:"messages",label:"Messages",tokens:a},{key:"tool_results",label:"Tool results",tokens:n},{key:"instructions",label:"Instructions",tokens:r},{key:"output",label:"Generated output",tokens:t.output}]}},QP=["#6366f1","#10b981","#f59e0b","#ec4899","#3b82f6","#8b5cf6","#14b8a6","#f97316"],IZ=[{id:"recent",label:"Recent"},{id:"tokens",label:"Tokens"},{id:"messages",label:"Messages"}],zZ=3e4,Ti=e=>e==null?"0":e>=1e6?(e/1e6).toFixed(1)+"M":e>=1e3?(e/1e3).toFixed(1)+"K":e.toString(),LZ=e=>{if(!e)return"";let t=Math.floor((Date.now()-new Date(e).getTime())/1e3);return t<60?"just now":t<3600?`${Math.floor(t/60)}m ago`:t<86400?`${Math.floor(t/3600)}h ago`:`${Math.floor(t/86400)}d ago`};function Mi({agentId:e=null,embedded:t=!1}){let{darkMode:r}=pt(),[n,a]=(0,ce.useState)([]),[i,o]=(0,ce.useState)(!0),[l,s]=(0,ce.useState)(null),[u,c]=(0,ce.useState)(null),[d,f]=(0,ce.useState)({}),{timeWindow:p}=La(),v=(0,ce.useCallback)(async()=>{try{let j=await fetch(`/api/interactions?minutes=${p.minutes}${e?`&agent_id=${e}`:""}`);if(!j.ok)throw new Error(`Request failed (${j.status})`);let z=await j.json();a(z.interactions||[]),s(null)}catch(j){s(j.message)}finally{o(!1)}},[p.minutes,e]);(0,ce.useEffect)(()=>{v();let j=setInterval(v,zZ);return()=>clearInterval(j)},[v]);let[h,b]=(0,ce.useState)("recent"),m=(0,ce.useMemo)(()=>{let j=[...new Set(n.map(z=>z.display_name).filter(Boolean))].sort();return Object.fromEntries(j.map((z,X)=>[z,QP[X%QP.length]]))},[n]),g=(0,ce.useMemo)(()=>{let j=n.reduce((X,R)=>X+(R.tokens?.total||0),0),z=new Set(n.map(X=>X.display_name).filter(Boolean));return{interactions:n.length,tokens:j,agents:z.size,avgTokens:n.length?Math.round(j/n.length):0}},[n]),x=(0,ce.useMemo)(()=>{if(n.length===0)return[];let j=Date.now(),z=p.bucketSeconds*1e3,X=Math.max(1,Math.round(p.minutes*60/p.bucketSeconds)),R=Object.keys(m);return Array.from({length:X},(Q,y)=>{let C=j-(X-1-y)*z,O=C-z,D=n.filter(N=>{let T=new Date(N.last_activity_at).getTime();return T>=O&&T1440?new Date(C).toLocaleString([],{month:"short",day:"numeric",hour:"2-digit"}):new Date(C).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),...Object.fromEntries(R.map(N=>[N,D.filter(T=>T.display_name===N).length]))}})},[n,m,p]),A=Math.max(0,Math.ceil(x.length/6)-1),k=(0,ce.useCallback)(j=>{let z=[...j];switch(h){case"tokens":return z.sort((X,R)=>(R.tokens?.total||0)-(X.tokens?.total||0));case"messages":return z.sort((X,R)=>(R.message_count||0)-(X.message_count||0));default:return z.sort((X,R)=>String(R.last_activity_at).localeCompare(String(X.last_activity_at)))}},[h]),_=(0,ce.useMemo)(()=>{let j=new Map;n.forEach(X=>{let R=X.agent_name||X.agent?.name||"Unattributed",Q=X.action_name,y=`${R}#${Q||""}`;j.has(y)||j.set(y,{key:y,title:Q?`${R} ${Q.charAt(0).toUpperCase()}${Q.slice(1)} Agent Interactions`:`${R} Interactions`,sessions:[],tokens:0,lastActivity:X.last_activity_at});let C=j.get(y);C.sessions.push(X),C.tokens+=X.tokens?.total||0,X.last_activity_at>C.lastActivity&&(C.lastActivity=X.last_activity_at)});let z=[...j.values()].map(X=>({...X,sessions:k(X.sessions)}));return h==="tokens"?z.sort((X,R)=>R.tokens-X.tokens):h==="messages"?z.sort((X,R)=>R.sessions.length-X.sessions.length):z.sort((X,R)=>String(R.lastActivity).localeCompare(String(X.lastActivity)))},[n,h,k]),M=async j=>{if(u===j){c(null);return}if(c(j),!d[j])try{let z=await fetch(`/api/interactions/${j}`);if(z.ok){let X=await z.json();f(R=>({...R,[j]:X.interaction}))}}catch{}},E={cardBg:r?"#1f1f1f":"#ffffff",cardBorder:r?"#2a2a2a":"#e5e7eb",innerBg:r?"rgba(255,255,255,0.04)":"#f9fafb",textPrimary:r?"#ffffff":"#111827",textSecondary:r?"rgba(255,255,255,0.6)":"#6b7280",textMuted:r?"rgba(255,255,255,0.4)":"#9ca3af"},L=j=>{if(j==null)return"\u2014";if(typeof j!="string")return JSON.stringify(j,null,2);try{return JSON.stringify(JSON.parse(j),null,2)}catch{return j}},I=j=>{switch(j){case"user":return r?{background:"rgba(59,130,246,0.15)",color:"#93c5fd",label:"User"}:{background:"#eff6ff",color:"#1d4ed8",label:"User"};case"assistant":return r?{background:"rgba(239,68,68,0.12)",color:"#fca5a5",label:"Assistant"}:{background:"#fef2f2",color:"#b91c1c",label:"Assistant"};case"tool":return r?{background:"rgba(245,158,11,0.12)",color:"#fcd34d",label:"Tool"}:{background:"#fffbeb",color:"#b45309",label:"Tool"};default:return r?{background:"rgba(255,255,255,0.08)",color:"rgba(255,255,255,0.7)",label:j}:{background:"#f3f4f6",color:"#374151",label:j}}};return i?ce.default.createElement("div",{className:"flex items-center justify-center h-64"},ce.default.createElement("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-red-500"})):ce.default.createElement("div",{className:"space-y-6"},ce.default.createElement("div",{className:"flex items-start justify-between"},!t&&ce.default.createElement("div",null,ce.default.createElement("h1",{className:"text-2xl font-bold",style:{color:E.textPrimary}},"Interactions"),ce.default.createElement("p",{className:"text-sm mt-1",style:{color:E.textSecondary}},"Persisted conversation streams per agent \u2014 messages, generations and provenance")),ce.default.createElement("div",{className:"flex items-center gap-3"},ce.default.createElement("div",{className:"flex items-center rounded-lg p-1",style:{background:r?"rgba(255,255,255,0.06)":"#f3f4f6"}},ce.default.createElement("span",{className:"text-xs px-1",style:{color:E.textMuted}},"Sort"),IZ.map(j=>ce.default.createElement("button",{key:j.id,onClick:()=>b(j.id),className:"px-2 py-1 text-xs rounded transition-colors",style:h===j.id?{background:r?"#2a2a2a":"#ffffff",color:E.textPrimary,boxShadow:r?"none":"0 1px 2px rgba(0,0,0,0.08)"}:{color:E.textSecondary}},j.label))),ce.default.createElement(Ba,null))),l&&ce.default.createElement("div",{className:"p-3 rounded-lg text-sm",style:{background:r?"rgba(239,68,68,0.1)":"#fef2f2",color:"#ef4444"}},"Failed to load interactions: ",l),n.length>0&&ce.default.createElement("div",{className:"rounded-xl border p-4",style:{backgroundColor:E.cardBg,borderColor:E.cardBorder}},ce.default.createElement("div",{className:"flex items-center justify-between mb-3"},ce.default.createElement("h3",{className:"text-sm font-semibold",style:{color:E.textPrimary}},"Interactions over time"),ce.default.createElement("div",{className:"flex items-center gap-3 flex-wrap justify-end"},Object.entries(m).map(([j,z])=>ce.default.createElement("span",{key:j,className:"flex items-center gap-1.5 text-xs",style:{color:E.textSecondary}},ce.default.createElement("span",{className:"inline-block w-2 h-2 rounded-full",style:{background:z}}),j)))),ce.default.createElement("div",{className:"h-32"},ce.default.createElement(pu,{width:"100%",height:"100%"},ce.default.createElement(oc,{data:x},ce.default.createElement("defs",null,Object.entries(m).map(([j,z])=>ce.default.createElement("linearGradient",{key:j,id:`ix-gradient-${j.replace(/\W/g,"-")}`,x1:"0",y1:"0",x2:"0",y2:"1"},ce.default.createElement("stop",{offset:"5%",stopColor:z,stopOpacity:.6}),ce.default.createElement("stop",{offset:"95%",stopColor:z,stopOpacity:.05})))),ce.default.createElement(Il,{strokeDasharray:"3 3",stroke:r?"#2a2a2a":"#e5e7eb"}),ce.default.createElement(Bl,{dataKey:"time",stroke:r?"rgba(255,255,255,0.4)":"#9ca3af",tick:{fontSize:10,fill:r?"rgba(255,255,255,0.5)":"#6b7280"},tickLine:!1,interval:A}),ce.default.createElement(Ul,{stroke:r?"rgba(255,255,255,0.4)":"#9ca3af",tick:{fontSize:10,fill:r?"rgba(255,255,255,0.5)":"#6b7280"},tickLine:!1,axisLine:!1,allowDecimals:!1}),ce.default.createElement(Gu,{contentStyle:{background:r?"#1f1f1f":"white",border:`1px solid ${E.cardBorder}`,borderRadius:"8px"},labelStyle:{color:E.textPrimary,fontWeight:"600",marginBottom:"4px"}}),Object.entries(m).map(([j,z])=>ce.default.createElement(Ll,{key:j,type:"monotone",dataKey:j,stackId:"1",stroke:z,strokeWidth:2,fill:`url(#ix-gradient-${j.replace(/\W/g,"-")})`}))))),ce.default.createElement("div",{className:"flex items-center gap-8 pt-3 mt-3 border-t",style:{borderColor:E.cardBorder}},[{label:"Interactions",value:g.interactions.toLocaleString()},{label:"Agents",value:g.agents.toLocaleString()},{label:"Total tokens",value:Ti(g.tokens)},{label:"Avg / interaction",value:Ti(g.avgTokens)}].map(j=>ce.default.createElement("div",{key:j.label},ce.default.createElement("div",{className:"text-lg font-bold",style:{color:E.textPrimary}},j.value),ce.default.createElement("div",{className:"text-xs",style:{color:E.textSecondary}},j.label))))),n.length===0?ce.default.createElement("div",{className:"text-center py-12"},ce.default.createElement("div",{className:"text-lg",style:{color:E.textMuted}},"No interactions in the last ",p.label),ce.default.createElement("p",{className:"text-sm mt-2",style:{color:E.textSecondary}},"Widen the time window, or run an agent to start recording its conversation stream")):ce.default.createElement("div",{className:"space-y-8"},_.map(j=>ce.default.createElement("div",{key:j.key,className:"space-y-4"},ce.default.createElement("div",{className:"flex items-baseline justify-between"},ce.default.createElement("h2",{className:"text-sm font-semibold",style:{color:E.textPrimary}},j.title),ce.default.createElement("span",{className:"text-xs",style:{color:E.textMuted}},j.sessions.length," ",j.sessions.length===1?"interaction":"interactions",j.tokens>0&&` \xB7 ${Ti(j.tokens)} tokens`)),j.sessions.map(z=>{let X=d[z.id],R=u===z.id;return ce.default.createElement("div",{key:z.id,className:"rounded-xl border shadow-sm overflow-hidden",style:{backgroundColor:E.cardBg,borderColor:E.cardBorder}},ce.default.createElement("div",{className:"flex items-center justify-between p-4 cursor-pointer",onClick:()=>M(z.id)},ce.default.createElement("div",{className:"flex items-center gap-3 min-w-0"},z.source==="telemetry"?ce.default.createElement("span",{className:"px-2 py-1 text-xs font-medium rounded flex-shrink-0",style:{background:r?"rgba(168,85,247,0.15)":"#faf5ff",color:r?"#d8b4fe":"#7e22ce"},title:"Reported by an app running this agent outside the platform"},"REPORTED"):ce.default.createElement("span",{className:"px-2 py-1 text-xs font-medium bg-blue-100 text-blue-700 rounded flex-shrink-0"},"SESSION"),ce.default.createElement("span",{className:"font-mono text-sm truncate",style:{color:E.textPrimary}},z.display_name),z.agent&&ce.default.createElement("span",{className:"text-sm truncate",style:{color:E.textSecondary}},z.agent.name),z.service_name&&ce.default.createElement("span",{className:"text-sm truncate font-mono",style:{color:E.textMuted}},z.service_name)),ce.default.createElement("div",{className:"flex items-center gap-4 flex-shrink-0 text-sm",style:{color:E.textSecondary}},ce.default.createElement("span",null,z.message_count>0?`${z.message_count} messages`:`${z.tool_count||0} tool ${z.tool_count===1?"call":"calls"}`),ce.default.createElement("span",null,Ti(z.tokens?.total)," tokens"),(z.tokens?.total||0)>0&&ce.default.createElement(Oi,{compact:!0,darkMode:r,label:"Context",used:(z.tokens?.input||0)+(z.tokens?.output||0)||z.tokens?.total,limit:Vf(z.model),segments:[{key:"messages",label:"Context",tokens:(z.tokens?.input||0)+(z.tokens?.output||0)||z.tokens?.total}]}),ce.default.createElement("span",{style:{color:E.textMuted}},LZ(z.last_activity_at)),ce.default.createElement("svg",{className:`w-4 h-4 transition-transform ${R?"rotate-180":""}`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},ce.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})))),R&&ce.default.createElement("div",{className:"border-t p-4 space-y-3",style:{borderColor:E.cardBorder,background:E.innerBg}},X?ce.default.createElement(ce.default.Fragment,null,X.messages.length===0&&ce.default.createElement("div",{className:"text-sm",style:{color:E.textMuted}},"No conversation content captured for this run. Enable content capture in the reporting app to record prompts, tool arguments, and responses."),ce.default.createElement(ki,{darkMode:r,messages:X.instructions?[{id:`ctx-${z.id}-system`,role:"system",content:X.instructions,created_at:z.created_at},...X.messages]:X.messages}),X.generations.length>0&&ce.default.createElement("div",{className:"pt-3 mt-2 border-t",style:{borderColor:E.cardBorder}},ce.default.createElement("div",{className:"text-xs uppercase tracking-wide mb-2",style:{color:E.textMuted}},"Generations"),ce.default.createElement("div",{className:"space-y-2"},X.generations.map(Q=>{let y=Math.max(...X.generations.map(O=>(O.duration_seconds||0)*1e3),1),C=(Q.duration_seconds||0)*1e3;return ce.default.createElement("div",{key:Q.id},ce.default.createElement("div",{className:"flex flex-wrap items-center gap-3 text-xs font-mono",style:{color:E.textSecondary}},ce.default.createElement("span",{title:Q.cache_hit?`${Ti(Q.tokens.cached)} cached prompt tokens`:"No prompt cache hit",className:Q.cache_hit?"text-green-600":"",style:Q.cache_hit?{}:{color:E.textMuted}},Q.cache_hit?"\u26A1 cache hit":"\u25CF generated"),Q.thinking&&ce.default.createElement("span",{className:"text-amber-600",title:`${Ti(Q.tokens.thinking)} thinking tokens`},"\u{1F9E0} ",Ti(Q.tokens.thinking)),ce.default.createElement("span",null,Q.model||"unknown-model"),Q.provider&&ce.default.createElement("span",null,Q.provider),ce.default.createElement("span",{className:"text-blue-500"},"in:",Ti(Q.tokens.input)),ce.default.createElement("span",{className:"text-green-600"},"out:",Ti(Q.tokens.output)),Q.finish_reason&&ce.default.createElement("span",null,Q.finish_reason),Q.duration_seconds!=null&&ce.default.createElement("span",null,(Q.duration_seconds*1e3).toFixed(0),"ms"),Q.trace_id&&ce.default.createElement("a",{href:`/dashboard/traces?trace=${Q.trace_id}`,title:`${Q.trace_id} \u2014 open in Traces`,style:{color:"#3b82f6"},className:"hover:underline",onClick:O=>O.stopPropagation()},"trace:",Q.trace_id.slice(0,8)," \u2192")),Q.trace_id?ce.default.createElement(jS,{traceId:Q.trace_id,darkMode:r}):C>0&&ce.default.createElement("div",{className:"rounded mt-1",title:`${C.toFixed(0)}ms`,style:{height:"6px",background:r?"rgba(255,255,255,0.08)":"#f3f4f6"}},ce.default.createElement("div",{className:"rounded",style:{height:"6px",width:`${Math.max(C/y*100,2)}%`,background:"#ef4444"}})))}))),(()=>{let Q=jZ(X);return Q?ce.default.createElement("div",{className:"pt-3 mt-2 border-t",style:{borderColor:E.cardBorder}},ce.default.createElement(Oi,{...Q,label:"Context pressure",estimated:!0,darkMode:r})):null})()):ce.default.createElement("div",{className:"flex justify-center py-6"},ce.default.createElement("div",{className:"animate-spin rounded-full h-6 w-6 border-b-2 border-red-500"}))))})))))}var ne=te(ae());var IS=[{type:"response_present",key:"response_present",label:"Response present",config:{}},{type:"min_length",key:"response_length",label:"Response length \u2265 40 chars",config:{chars:40}},{type:"max_latency_ms",key:"latency",label:"Latency \u2264 5s",config:{ms:5e3}},{type:"token_budget",key:"token_budget",label:"Output \u2264 1000 tokens",config:{output_tokens:1e3}}],zS=[{type:"trace_error_rate",key:"trace_error_rate",label:"Trace error rate \u2264 5% (telemetry, 7d)",config:{max_error_rate:5,window_hours:168}},{type:"trace_latency",key:"trace_latency",label:"Avg trace latency \u2264 5s (telemetry, 7d)",config:{max_avg_ms:5e3,window_hours:168}}],JP=()=>document.querySelector('meta[name="csrf-token"]')?.content,BZ=e=>{if(!e)return"";let t=Math.floor((Date.now()-new Date(e).getTime())/6e4);return t<1?"just now":t<60?`${t} min ago`:t<1440?`${Math.floor(t/60)} hours ago`:`${Math.floor(t/1440)} days ago`},xg=e=>e>=.85?"high":e>=.7?"medium":"low";function Ff({embedded:e=!1,agentId:t=null}){let{darkMode:r}=pt(),[n,a]=(0,ne.useState)([]),[i,o]=(0,ne.useState)([]),[l,s]=(0,ne.useState)(!0),[u,c]=(0,ne.useState)(null),[d,f]=(0,ne.useState)(null),[p,v]=(0,ne.useState)(!1),[h,b]=(0,ne.useState)(null),[m,g]=(0,ne.useState)({agent_id:t?String(t):"",name:"",sample_size:20,criteria:IS.map(N=>N.key),containsPattern:"",llmJudgePrompt:"",judgeKind:"manual",judgeModel:"",compareModels:""}),[x,A]=(0,ne.useState)(null),[k,_]=(0,ne.useState)(!1),M=(0,ne.useCallback)(async()=>{try{let N=await fetch(`/api/evaluations${t?`?agent_id=${encodeURIComponent(t)}`:""}`);if(!N.ok)throw new Error(`Request failed (${N.status})`);let T=await N.json();a(T.evaluations||[]),c(null)}catch(N){c(N.message)}finally{s(!1)}},[t]);(0,ne.useEffect)(()=>{M(),fetch("/api/agents").then(N=>N.ok?N.json():{agents:[]}).then(N=>o(N.agents||[])).catch(()=>o([]))},[M]);let E=()=>{let N=[...IS,...zS].filter(T=>m.criteria.includes(T.key)).map(({key:T,type:$,config:Y})=>({key:T,type:$,config:Y}));return m.containsPattern.trim()&&N.push({key:"contains",type:"contains",config:{pattern:m.containsPattern.trim()}}),m.llmJudgePrompt.trim()&&N.push({key:"quality",type:"llm_judge",config:{prompt:m.llmJudgePrompt.trim()}}),N},L=async N=>{N.preventDefault(),_(!0),A(null);try{let T=await fetch("/api/evaluations",{method:"POST",headers:{"Content-Type":"application/json","X-CSRF-Token":JP()},body:JSON.stringify({evaluation:{agent_id:m.agent_id,name:m.name,sample_size:m.sample_size,judge_kind:m.judgeKind==="judge_defined"?"judge_defined":m.llmJudgePrompt.trim()?"llm":"rules",judge_model:m.judgeModel.trim()||void 0,compare_models:m.compareModels.split(",").map(Y=>Y.trim()).filter(Boolean),criteria:m.judgeKind==="judge_defined"?[]:E()}})}),$=await T.json();if(!T.ok)throw new Error(($.errors||[$.error]).filter(Boolean).join(", ")||"Failed to create evaluation");v(!1),g({...m,name:""}),await M(),f($.evaluation?.id??null)}catch(T){A(T.message)}finally{_(!1)}},I=async N=>{b(N);try{await fetch(`/api/evaluations/${N}/run`,{method:"POST",headers:{"X-CSRF-Token":JP()}}),await M()}finally{b(null)}},j={cardBg:r?"#1f1f1f":"#ffffff",cardBorder:r?"#2a2a2a":"#e5e7eb",innerBg:r?"rgba(255,255,255,0.04)":"#f9fafb",trackBg:r?"rgba(255,255,255,0.1)":"#f3f4f6",textPrimary:r?"#ffffff":"#111827",textSecondary:r?"rgba(255,255,255,0.6)":"#6b7280",textMuted:r?"rgba(255,255,255,0.4)":"#9ca3af",inputBg:r?"rgba(255,255,255,0.06)":"#ffffff",inputBorder:r?"rgba(255,255,255,0.2)":"#d1d5db"},z={high:"#22c55e",medium:"#eab308",low:"#ef4444"};if(l)return ne.default.createElement("div",{className:"flex items-center justify-center h-64"},ne.default.createElement("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-red-500"}));let X=t?n.filter(N=>String(N.agent?.id)===String(t)):n,R=X.map(N=>N.latest_run).filter(N=>N&&N.status==="complete"),Q=R.length?R.reduce((N,T)=>N+(T.average_score||0),0)/R.length:null,y=R.reduce((N,T)=>N+(T.samples_evaluated||0),0),C={padding:"8px 12px",borderRadius:"8px",fontSize:"14px",background:j.inputBg,border:`1px solid ${j.inputBorder}`,color:j.textPrimary},O=N=>N&&typeof N=="object"&&!("score"in N)&&!("skipped"in N),D=(N,T,{indent:$=!1,mono:Y=!1}={})=>ne.default.createElement("div",{key:N,className:"flex items-center gap-4",style:$?{paddingLeft:"16px"}:void 0},ne.default.createElement("div",{className:`w-32 text-sm truncate ${Y?"font-mono text-xs":""}`,style:{color:j.textSecondary},title:N},Y?N:N.replace(/_/g," "),T.source==="telemetry"&&ne.default.createElement("span",{"data-testid":"score-source-telemetry",className:"ml-1 text-[10px] uppercase tracking-wide",style:{color:j.textMuted},title:`Aggregate over ${T.traces} traces in the last ${T.window_hours}h`},"telemetry")),T.skipped?ne.default.createElement("div",{className:"flex-1 text-xs italic",style:{color:j.textMuted},title:T.reason},"skipped \u2014 ",T.reason):ne.default.createElement(ne.default.Fragment,null,ne.default.createElement("div",{className:"flex-1 h-2 rounded-full overflow-hidden",style:{background:j.trackBg}},ne.default.createElement("div",{className:"h-full rounded-full transition-all",style:{width:`${T.score*100}%`,background:z[xg(T.score)]}})),ne.default.createElement("div",{className:"w-12 text-sm font-medium text-right",style:{color:z[xg(T.score)]},title:`min ${T.min} \xB7 max ${T.max} \xB7 ${T.passed}/${T.total} passed`},T.score.toFixed(2))));return ne.default.createElement("div",{className:"space-y-6"},ne.default.createElement("div",{className:`flex items-center ${e?"justify-end":"justify-between"}`},!e&&ne.default.createElement("div",null,ne.default.createElement("h1",{className:"text-2xl font-bold",style:{color:j.textPrimary}},"Evaluations"),ne.default.createElement("p",{className:"text-sm mt-1",style:{color:j.textSecondary}},"Score outputs with LLM-as-judge, rule-based checks, or custom criteria")),ne.default.createElement("button",{onClick:()=>v(!p),className:"px-4 py-2 bg-red-500 text-white rounded-lg text-sm font-medium hover:bg-red-600 transition-colors"},p?"Cancel":"New Evaluation")),u&&ne.default.createElement("div",{className:"p-3 rounded-lg text-sm",style:{background:r?"rgba(239,68,68,0.1)":"#fef2f2",color:"#ef4444"}},"Failed to load evaluations: ",u),p&&ne.default.createElement("form",{onSubmit:L,className:"rounded-xl border p-5 space-y-4",style:{backgroundColor:j.cardBg,borderColor:j.cardBorder}},ne.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4"},ne.default.createElement("div",null,ne.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Agent"),ne.default.createElement("select",{required:!0,disabled:!!t,value:m.agent_id,onChange:N=>g({...m,agent_id:N.target.value}),style:{...C,width:"100%",opacity:t?.7:1}},!t&&ne.default.createElement("option",{value:""},"Select agent\u2026"),i.map(N=>ne.default.createElement("option",{key:N.id,value:N.id},N.name)))),ne.default.createElement("div",null,ne.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Name"),ne.default.createElement("input",{required:!0,type:"text",value:m.name,onChange:N=>g({...m,name:N.target.value}),placeholder:"Response Quality",style:{...C,width:"100%"}})),ne.default.createElement("div",null,ne.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Sample size"),ne.default.createElement("input",{type:"number",min:"1",max:"100",value:m.sample_size,onChange:N=>g({...m,sample_size:N.target.value}),style:{...C,width:"100%"}}))),ne.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4"},ne.default.createElement("div",null,ne.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"KPI definition"),ne.default.createElement("select",{value:m.judgeKind,onChange:N=>g({...m,judgeKind:N.target.value}),style:{...C,width:"100%"}},ne.default.createElement("option",{value:"manual"},"Manual criteria"),ne.default.createElement("option",{value:"judge_defined"},"Judge defines KPIs from agent goals"))),ne.default.createElement("div",null,ne.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Judge model (optional)"),ne.default.createElement("input",{type:"text",value:m.judgeModel,onChange:N=>g({...m,judgeModel:N.target.value}),placeholder:"e.g. claude-opus-5",style:{...C,width:"100%"}})),ne.default.createElement("div",null,ne.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Compare models (optional, comma-separated)"),ne.default.createElement("input",{type:"text",value:m.compareModels,onChange:N=>g({...m,compareModels:N.target.value}),placeholder:"e.g. claude-haiku-4-5, qwen3:8b",style:{...C,width:"100%"}}))),m.judgeKind==="judge_defined"&&ne.default.createElement("p",{className:"text-xs",style:{color:j.textSecondary}},"On the first run the judge reads the agent's instructions and recent interactions, defines 3\u20136 KPIs, then scores samples against them. KPIs persist so later runs (and model cohorts) stay comparable."),m.judgeKind!=="judge_defined"&&ne.default.createElement(ne.default.Fragment,null,ne.default.createElement("div",null,ne.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-2",style:{color:j.textMuted}},"Rule-based criteria (sampled generations)"),ne.default.createElement("div",{className:"flex flex-wrap gap-3"},IS.map(N=>ne.default.createElement("label",{key:N.key,className:"flex items-center gap-2 text-sm",style:{color:j.textPrimary}},ne.default.createElement("input",{type:"checkbox",checked:m.criteria.includes(N.key),onChange:T=>g({...m,criteria:T.target.checked?[...m.criteria,N.key]:m.criteria.filter($=>$!==N.key)})}),N.label)))),ne.default.createElement("div",null,ne.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-2",style:{color:j.textMuted}},"Telemetry criteria (trace aggregates)"),ne.default.createElement("div",{className:"flex flex-wrap gap-3"},zS.map(N=>ne.default.createElement("label",{key:N.key,className:"flex items-center gap-2 text-sm",style:{color:j.textPrimary}},ne.default.createElement("input",{type:"checkbox",checked:m.criteria.includes(N.key),onChange:T=>g({...m,criteria:T.target.checked?[...m.criteria,N.key]:m.criteria.filter($=>$!==N.key)})}),N.label)))),ne.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4"},ne.default.createElement("div",null,ne.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Must contain (optional pattern)"),ne.default.createElement("input",{type:"text",value:m.containsPattern,onChange:N=>g({...m,containsPattern:N.target.value}),placeholder:"e.g. password reset",style:{...C,width:"100%"}})),ne.default.createElement("div",null,ne.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"LLM judge criterion (optional, needs provider credentials)"),ne.default.createElement("input",{type:"text",value:m.llmJudgePrompt,onChange:N=>g({...m,llmJudgePrompt:N.target.value}),placeholder:"e.g. Is the answer helpful and accurate?",style:{...C,width:"100%"}})))),x&&ne.default.createElement("div",{className:"text-sm text-red-500"},x),ne.default.createElement("button",{type:"submit",disabled:k,className:"px-4 py-2 bg-red-500 text-white rounded-lg text-sm font-medium hover:bg-red-600 transition-colors disabled:opacity-50"},k?"Creating & running\u2026":"Create & Run")),ne.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4"},ne.default.createElement("div",{className:"rounded-xl p-5 border shadow-sm",style:{backgroundColor:j.cardBg,borderColor:j.cardBorder}},ne.default.createElement("div",{className:"text-sm mb-1",style:{color:j.textSecondary}},"Total Evaluations"),ne.default.createElement("div",{className:"text-3xl font-bold",style:{color:j.textPrimary}},X.length)),ne.default.createElement("div",{className:"rounded-xl p-5 border shadow-sm",style:{backgroundColor:j.cardBg,borderColor:j.cardBorder}},ne.default.createElement("div",{className:"text-sm mb-1",style:{color:j.textSecondary}},"Average Score"),ne.default.createElement("div",{className:"text-3xl font-bold",style:{color:Q==null?j.textMuted:z[xg(Q)]}},Q==null?"\u2014":`${(Q*100).toFixed(0)}%`)),ne.default.createElement("div",{className:"rounded-xl p-5 border shadow-sm",style:{backgroundColor:j.cardBg,borderColor:j.cardBorder}},ne.default.createElement("div",{className:"text-sm mb-1",style:{color:j.textSecondary}},"Samples Evaluated"),ne.default.createElement("div",{className:"text-3xl font-bold",style:{color:j.textPrimary}},y))),ne.default.createElement("div",{className:"space-y-4"},X.map(N=>{let T=N.latest_run,$=d===N.id,Y=(N.criteria||[]).some(J=>zS.some(fe=>fe.key===J.key));return ne.default.createElement("div",{key:N.id,"data-testid":"evaluation-card","data-telemetry":Y?"true":"false",className:"rounded-xl border shadow-sm overflow-hidden",style:{backgroundColor:j.cardBg,borderColor:j.cardBorder}},ne.default.createElement("div",{className:"flex items-center justify-between p-4 cursor-pointer",onClick:()=>f($?null:N.id)},ne.default.createElement("div",{className:"flex items-center gap-3 min-w-0"},ne.default.createElement("span",{className:"px-2 py-1 text-xs font-medium bg-purple-100 text-purple-700 rounded flex-shrink-0"},"EVALUATION"),ne.default.createElement("span",{className:"font-medium truncate",style:{color:j.textPrimary}},N.name),ne.default.createElement("span",{className:"text-sm truncate",style:{color:j.textSecondary}},N.agent?.name)),ne.default.createElement("div",{className:"flex items-center gap-4 flex-shrink-0 text-sm",style:{color:j.textSecondary}},T?.status==="failed"&&ne.default.createElement("span",{className:"text-red-500"},"failed"),T?.status==="complete"&&T.average_score!=null&&ne.default.createElement("span",{style:{color:z[xg(T.average_score)],fontWeight:600}},(T.average_score*100).toFixed(0),"%"),ne.default.createElement("span",{style:{color:j.textMuted}},BZ(T?.completed_at||N.created_at)))),$&&ne.default.createElement("div",{className:"border-t",style:{borderColor:j.cardBorder}},T?.status==="failed"?ne.default.createElement("div",{className:"p-4 text-sm text-red-500"},T.error_message):T?.scores?ne.default.createElement("div",{className:"p-4 space-y-3"},T.scores._verdict&&ne.default.createElement("div",{className:"p-3 rounded-lg text-sm",style:{background:r?"rgba(34,197,94,0.1)":"#f0fdf4"}},ne.default.createElement("span",{className:"font-semibold",style:{color:"#16a34a"}},"Winner: ",T.scores._verdict.winner),ne.default.createElement("span",{className:"ml-2",style:{color:j.textSecondary}},T.scores._verdict.rationale),ne.default.createElement("span",{className:"ml-2 text-xs",style:{color:j.textMuted}},"judged by ",T.scores._verdict.judge)),T.scores._missing_models&&ne.default.createElement("div",{className:"text-xs italic",style:{color:j.textMuted}},"No recorded generations for: ",T.scores._missing_models.join(", ")," \u2014 run the agent under those models first"),Object.entries(T.scores).filter(([J])=>!J.startsWith("_")).map(([J,fe])=>O(fe)?ne.default.createElement("div",{key:J,className:"space-y-1"},ne.default.createElement("div",{className:"text-sm",style:{color:j.textSecondary}},J.replace(/_/g," ")),Object.entries(fe).map(([pe,ge])=>D(pe,ge,{indent:!0,mono:!0}))):D(J,fe))):ne.default.createElement("div",{className:"p-4 text-sm",style:{color:j.textMuted}},"No runs yet"),ne.default.createElement("div",{className:"p-4 grid grid-cols-2 md:grid-cols-5 gap-4 text-sm",style:{background:j.innerBg}},ne.default.createElement("div",null,ne.default.createElement("div",{style:{color:j.textMuted}},"Judge"),ne.default.createElement("div",{className:"font-medium",style:{color:j.textPrimary}},N.judge_kind==="judge_defined"?`Judge-defined KPIs${N.judge_model?` (${N.judge_model})`:""}`:N.judge_kind==="llm"?N.judge_model||"LLM judge":"Rule-based")),ne.default.createElement("div",{className:"col-span-2"},ne.default.createElement("div",{style:{color:j.textMuted}},"Criteria"),ne.default.createElement("div",{className:"font-medium truncate",style:{color:j.textPrimary}},(N.criteria||[]).map(J=>J.key).join(", "))),ne.default.createElement("div",null,ne.default.createElement("div",{style:{color:j.textMuted}},"Samples"),ne.default.createElement("div",{className:"font-medium",style:{color:j.textPrimary}},T?`${T.samples_passed} / ${T.samples_evaluated} passed`:"\u2014")),ne.default.createElement("div",{className:"flex items-end justify-end"},ne.default.createElement("button",{onClick:J=>{J.stopPropagation(),I(N.id)},disabled:h===N.id,className:"px-3 py-1.5 text-sm bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors disabled:opacity-50"},h===N.id?"Running\u2026":"Run again")))))})),X.length===0&&!p&&ne.default.createElement("div",{className:"text-center py-12 rounded-xl border",style:{backgroundColor:j.cardBg,borderColor:j.cardBorder}},ne.default.createElement("div",{className:"text-lg",style:{color:j.textMuted}},"No evaluations yet"),ne.default.createElement("p",{className:"text-sm mt-2",style:{color:j.textSecondary}},"Create an evaluation to start scoring agent outputs")))}var me=te(ae());var UZ=[{id:"overview",label:"Overview"},{id:"traces",label:"Traces"},{id:"interactions",label:"Interactions"}];function LS({agent:e,onBack:t,embedded:r=!1}){let{darkMode:n}=pt(),a=qs(n),[i,o]=(0,me.useState)(null),[l,s]=(0,me.useState)(!0),{timeWindow:u,days:c}=La(),[d,f]=(0,me.useState)("overview"),p=e.telemetry_agent_class||(()=>{let A=e.agent_class_name||`${(e.name||"").replace(/[^a-zA-Z0-9]+(.)/g,(k,_)=>_.toUpperCase()).replace(/^./,k=>k.toUpperCase()).replace(/[^a-zA-Z0-9]/g,"")}`;return A.endsWith("Agent")?A:`${A}Agent`})();(0,me.useEffect)(()=>{v()},[e?.id,c]);let v=async()=>{if(e?.id){s(!0);try{let k=await(await fetch(`/api/agents/${e.id}/analytics?days=${c}`)).json();o(k)}catch(A){console.error("Failed to load analytics:",A)}finally{s(!1)}}},h=A=>A?A<1e3?`${A}ms`:`${(A/1e3).toFixed(2)}s`:"-",b=A=>A?A>=1e6?`${(A/1e6).toFixed(1)}M`:A>=1e3?`${(A/1e3).toFixed(1)}K`:A.toString():"0",m=A=>{switch(A){case"complete":return"bg-green-500";case"failed":return"bg-red-500";case"running":return"bg-blue-500";case"pending":return"bg-yellow-500";case"cancelled":return"bg-gray-500";default:return"bg-gray-400"}};if(l&&!i)return me.default.createElement("div",{className:"flex items-center justify-center h-64"},me.default.createElement("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-red-500"}));let g=i?.runs_by_day?.length>0?Math.max(...i.runs_by_day.map(A=>A.count)):1,x=r?"overview":d;return me.default.createElement("div",{className:"space-y-6"},r?me.default.createElement("div",{className:"flex items-center justify-end gap-2"},u.minutes<1440&&me.default.createElement("span",{className:"text-xs",style:{color:a.textMuted},title:"This view aggregates by day"},"showing 1 day"),me.default.createElement(Ba,null)):me.default.createElement("div",{className:"flex items-center justify-between"},me.default.createElement("div",{className:"flex items-center space-x-4"},me.default.createElement("button",{onClick:t,className:"p-2 transition-colors",style:{color:a.textMuted}},me.default.createElement("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},me.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M15 19l-7-7 7-7"}))),me.default.createElement("div",null,me.default.createElement("h1",{className:"text-2xl font-bold",style:{color:a.textPrimary}},e.name," Analytics"),me.default.createElement("p",{className:"text-sm",style:{color:a.textSecondary}},"Performance metrics and usage data"))),me.default.createElement("div",{className:"flex items-center gap-2"},u.minutes<1440&&me.default.createElement("span",{className:"text-xs",style:{color:a.textMuted},title:"This view aggregates by day"},"showing 1 day"),me.default.createElement(Ba,null))),!r&&me.default.createElement("div",{className:"flex rounded-lg p-1 w-fit",style:{background:a.mutedBg}},UZ.map(A=>me.default.createElement("button",{key:A.id,onClick:()=>f(A.id),className:`px-4 py-1.5 text-sm rounded-md transition-colors ${d===A.id?"shadow":""}`,style:d===A.id?{background:a.cardBg,color:a.textPrimary}:{color:a.textSecondary}},A.label))),x==="traces"&&me.default.createElement(Wl,{agentClass:p,embedded:!0}),x==="interactions"&&me.default.createElement(Mi,{agentId:e.id,embedded:!0}),x==="overview"&&me.default.createElement(me.default.Fragment,null,me.default.createElement("div",{className:"grid grid-cols-2 lg:grid-cols-4 gap-4"},me.default.createElement(bg,{title:"Total Runs",value:i?.summary?.total_runs||0,icon:me.default.createElement("svg",{className:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},me.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M13 10V3L4 14h7v7l9-11h-7z"})),color:"rose",colors:a,darkMode:n}),me.default.createElement(bg,{title:"Success Rate",value:`${i?.summary?.success_rate||0}%`,icon:me.default.createElement("svg",{className:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},me.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"})),color:"green",colors:a,darkMode:n}),me.default.createElement(bg,{title:"Avg Duration",value:h(i?.summary?.avg_duration_ms),icon:me.default.createElement("svg",{className:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},me.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"})),color:"blue",colors:a,darkMode:n}),me.default.createElement(bg,{title:"Total Tokens",value:b(i?.summary?.total_tokens),icon:me.default.createElement("svg",{className:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},me.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"})),color:"purple",colors:a,darkMode:n})),me.default.createElement("div",{className:"grid grid-cols-1 lg:grid-cols-3 gap-6"},me.default.createElement("div",{className:"lg:col-span-2 rounded-xl border p-6",style:{background:a.cardBg,borderColor:a.cardBorder}},me.default.createElement("h3",{className:"font-semibold mb-4",style:{color:a.textPrimary}},"Runs Over Time"),i?.runs_by_day?.length>0?me.default.createElement("div",{className:"h-48"},me.default.createElement("div",{className:"flex items-end justify-between h-full space-x-1"},i.runs_by_day.map((A,k)=>me.default.createElement("div",{key:A.date,className:"flex-1 flex flex-col items-center group"},me.default.createElement("div",{className:"relative w-full"},me.default.createElement("div",{className:"w-full bg-red-500 rounded-t transition-all hover:bg-red-600",style:{height:`${A.count/g*150}px`,minHeight:A.count>0?"4px":"0"}}),me.default.createElement("div",{className:"absolute -top-8 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap"},A.count," runs")),(k===0||k===i.runs_by_day.length-1||i.runs_by_day.length<=7)&&me.default.createElement("span",{className:"text-xs mt-2 transform -rotate-45 origin-left",style:{color:a.textMuted}},new Date(A.date).toLocaleDateString("en-US",{month:"short",day:"numeric"})))))):me.default.createElement("div",{className:"h-48 flex items-center justify-center",style:{color:a.textMuted}},"No data for this period")),me.default.createElement("div",{className:"rounded-xl border p-6",style:{background:a.cardBg,borderColor:a.cardBorder}},me.default.createElement("h3",{className:"font-semibold mb-4",style:{color:a.textPrimary}},"Status Breakdown"),Object.keys(i?.status_breakdown||{}).length>0?me.default.createElement("div",{className:"space-y-3"},Object.entries(i.status_breakdown).map(([A,k])=>{let _=i?.summary?.total_runs||1,M=(k/_*100).toFixed(1);return me.default.createElement("div",{key:A},me.default.createElement("div",{className:"flex justify-between text-sm mb-1"},me.default.createElement("span",{className:"capitalize",style:{color:a.textSecondary}},A),me.default.createElement("span",{className:"font-medium",style:{color:a.textPrimary}},k," (",M,"%)")),me.default.createElement("div",{className:"h-2 rounded-full overflow-hidden",style:{background:a.trackBg}},me.default.createElement("div",{className:`h-full ${m(A)} rounded-full transition-all`,style:{width:`${M}%`}})))})):me.default.createElement("div",{className:"h-32 flex items-center justify-center",style:{color:a.textMuted}},"No runs yet"))),i?.recent_errors?.length>0&&me.default.createElement("div",{className:"rounded-xl border p-6",style:{background:a.cardBg,borderColor:a.cardBorder}},me.default.createElement("h3",{className:"font-semibold mb-4",style:{color:a.textPrimary}},"Recent Errors"),me.default.createElement("div",{className:"space-y-3"},i.recent_errors.map(A=>me.default.createElement("div",{key:A.id,className:"flex items-start space-x-3 p-3 rounded-lg",style:{background:n?"rgba(220,38,38,0.12)":"#fef2f2"}},me.default.createElement("svg",{className:"w-5 h-5 text-red-500 flex-shrink-0 mt-0.5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},me.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"})),me.default.createElement("div",{className:"flex-1 min-w-0"},me.default.createElement("p",{className:"text-sm",style:{color:n?"#fca5a5":"#991b1b"}},A.error||"Unknown error"),me.default.createElement("p",{className:"text-xs mt-1",style:{color:n?"#f87171":"#dc2626"}},new Date(A.created_at).toLocaleString())))))),me.default.createElement("div",{className:"grid grid-cols-2 lg:grid-cols-4 gap-4"},me.default.createElement("div",{className:"rounded-lg p-4",style:{background:a.innerBg}},me.default.createElement("p",{className:"text-sm",style:{color:a.textSecondary}},"Completed Runs"),me.default.createElement("p",{className:"text-2xl font-bold",style:{color:a.textPrimary}},i?.summary?.completed_runs||0)),me.default.createElement("div",{className:"rounded-lg p-4",style:{background:a.innerBg}},me.default.createElement("p",{className:"text-sm",style:{color:a.textSecondary}},"Failed Runs"),me.default.createElement("p",{className:"text-2xl font-bold",style:{color:a.textPrimary}},i?.summary?.failed_runs||0)),me.default.createElement("div",{className:"rounded-lg p-4",style:{background:a.innerBg}},me.default.createElement("p",{className:"text-sm",style:{color:a.textSecondary}},"Avg Tokens/Run"),me.default.createElement("p",{className:"text-2xl font-bold",style:{color:a.textPrimary}},b(i?.summary?.avg_tokens_per_run))),me.default.createElement("div",{className:"rounded-lg p-4",style:{background:a.innerBg}},me.default.createElement("p",{className:"text-sm",style:{color:a.textSecondary}},"Period"),me.default.createElement("p",{className:"text-2xl font-bold",style:{color:a.textPrimary}},i?.period_days||30," days")))))}var RP={rose:["#dc2626","rgba(220,38,38,0.18)","#fee2e2"],green:["#16a34a","rgba(22,163,74,0.18)","#dcfce7"],blue:["#2563eb","rgba(37,99,235,0.18)","#dbeafe"],purple:["#7c3aed","rgba(124,58,237,0.18)","#ede9fe"]};function bg({title:e,value:t,icon:r,color:n,colors:a,darkMode:i}){let[o,l,s]=RP[n]||RP.rose;return me.default.createElement("div",{className:"rounded-xl border p-4",style:{background:a.cardBg,borderColor:a.cardBorder}},me.default.createElement("div",{className:"flex items-center justify-between"},me.default.createElement("div",null,me.default.createElement("p",{className:"text-sm",style:{color:a.textSecondary}},e),me.default.createElement("p",{className:"text-2xl font-bold mt-1",style:{color:a.textPrimary}},t)),me.default.createElement("div",{className:"p-3 rounded-lg",style:{background:i?l:s,color:o}},r)))}var $Z=[{id:"config",label:"Configuration"},{id:"instructions",label:"Instructions"},{id:"tools",label:"Tools"},{id:"versions",label:"Versions"},{id:"code",label:"Code"}],rj=[{id:"traces",label:"Traces"},{id:"metrics",label:"Metrics"},{id:"interactions",label:"Interactions"},{id:"evals",label:"Evals"},{id:"feedback",label:"Feedback"}],HZ=rj.map(e=>e.id),ej={success:{light:["#dcfce7","#166534"],dark:["rgba(22,163,74,0.18)","#4ade80"]},warning:{light:["#fef9c3","#854d0e"],dark:["rgba(234,179,8,0.18)","#facc15"]},neutral:{light:["#f3f4f6","#4b5563"],dark:["rgba(255,255,255,0.1)","rgba(255,255,255,0.7)"]},error:{light:["#fee2e2","#991b1b"],dark:["rgba(220,38,38,0.18)","#f87171"]},info:{light:["#dbeafe","#1e40af"],dark:["rgba(59,130,246,0.18)","#60a5fa"]}},VZ={active:"success",draft:"warning",archived:"neutral",failed:"error",trial:"info"};function BS({tone:e="neutral",mono:t=!1,darkMode:r,children:n}){let[a,i]=(ej[e]||ej.neutral)[r?"dark":"light"];return G.default.createElement("span",{style:{background:a,color:i,borderRadius:"999px",padding:"2px 8px",fontSize:"11px",fontWeight:600,fontFamily:t?Oe.mono:void 0,whiteSpace:"nowrap"}},n)}function Kl({variant:e="secondary",size:t="md",onClick:r,disabled:n,colors:a,children:i}){let l={padding:t==="sm"?"5px 10px":"7px 14px",borderRadius:"8px",fontSize:"13px",fontWeight:500,cursor:n?"not-allowed":"pointer",transition:"background 0.15s ease, border-color 0.15s ease",whiteSpace:"nowrap"},s={primary:{background:n?a.mutedBg:la,color:n?a.textMuted:"#ffffff",border:"1px solid transparent"},secondary:{background:"transparent",color:a.textCell,border:`1px solid ${a.inputBorder}`},danger:{background:"transparent",color:"#dc2626",border:"1px solid rgba(220,38,38,0.4)"}};return G.default.createElement("button",{type:"button",onClick:r,disabled:n,style:{...l,...s[e]||s.secondary}},i)}function tj({tabs:e,active:t,onChange:r,colors:n}){return G.default.createElement("div",{style:{display:"flex",alignItems:"center"}},e.map(a=>{let i=t===a.id;return G.default.createElement("button",{key:a.id,type:"button",onClick:()=>r(a.id),style:{padding:"10px 12px",marginBottom:"-1px",background:"none",border:"none",borderBottom:`2px solid ${i?la:"transparent"}`,color:i?la:n.textSecondary,fontSize:"13px",fontWeight:i?600:500,cursor:"pointer",whiteSpace:"nowrap"}},a.label)}))}function wg({agent:e,meta:t,onSave:r,onDelete:n,onRun:a,onDuplicate:i,onRunReport:o,onBack:l,isLoading:s,initialTab:u}){let{darkMode:c}=pt(),d=qs(c),[f,p]=(0,G.useState)(u||"config"),[v,h]=(0,G.useState)({name:e.name||"",description:e.description||"",provider:e.provider||"openai",model:e.model||"gpt-4o-mini",instructions:e.instructions||"",action_prompts:e.actionPrompts||e.action_prompts||[],preset_type:e.presetType||e.preset_type||"terminal",appearance:e.appearance||{},instruction_sets:e.instructionSets||e.instruction_sets||[],tools:e.tools||[],mcp_servers:e.mcpServers||e.mcp_servers||[],model_config:e.modelConfig||e.model_config||{temperature:.7},status:e.status||"draft"}),[b,m]=(0,G.useState)([]),[g,x]=(0,G.useState)(!1),[A,k]=(0,G.useState)(""),[_,M]=(0,G.useState)(Fs),E=e.telemetry_agent_class||e.telemetryAgentClass||(()=>{let Q=e.agent_class_name||e.agentClassName||`${(e.name||"").replace(/[^a-zA-Z0-9]+(.)/g,(y,C)=>C.toUpperCase()).replace(/^./,y=>y.toUpperCase()).replace(/[^a-zA-Z0-9]/g,"")}`;return Q.endsWith("Agent")?Q:`${Q}Agent`})();(0,G.useEffect)(()=>{let Q=!1;return Tm(v.provider).then(y=>{Q||y.length===0||M(C=>({...C,[v.provider]:y}))}),()=>{Q=!0}},[v.provider]),(0,G.useEffect)(()=>{let Q=JSON.stringify(v)!==JSON.stringify({name:e.name||"",description:e.description||"",provider:e.provider||"openai",model:e.model||"gpt-4o-mini",instructions:e.instructions||"",action_prompts:e.actionPrompts||e.action_prompts||[],preset_type:e.presetType||e.preset_type||"terminal",appearance:e.appearance||{},instruction_sets:e.instructionSets||e.instruction_sets||[],tools:e.tools||[],mcp_servers:e.mcpServers||e.mcp_servers||[],model_config:e.modelConfig||e.model_config||{temperature:.7},status:e.status||"draft"});x(Q)},[v,e]),(0,G.useEffect)(()=>{f==="versions"?L():f==="code"&&I()},[f]);let L=async()=>{try{let y=await(await fetch(`/api/agents/${e.id}/versions`)).json();m(y.versions)}catch(Q){console.error("Failed to load versions:",Q)}},I=async()=>{try{let y=await(await fetch(`/api/agents/${e.id}/export`)).json();k(y.code)}catch(Q){console.error("Failed to load code preview:",Q)}},j=(Q,y)=>{h(C=>({...C,[Q]:y}))},z=(Q,y)=>{h(C=>{let O=C[Q]||[],D=O.includes(y)?O.filter(N=>N!==y):[...O,y];return{...C,[Q]:D}})},X=()=>{r(v)},R=HZ.includes(f);return G.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"20px"}},G.default.createElement("div",{style:{display:"flex",alignItems:"flex-start",gap:"12px"}},G.default.createElement("button",{type:"button",onClick:l,title:"Back to agents",style:{fontFamily:Oe.mono,fontSize:"13px",color:d.textSecondary,background:"none",border:"none",cursor:"pointer",padding:"4px 2px",marginTop:"10px"}},"<-"),G.default.createElement(Tt,{size:44}),G.default.createElement("div",{style:{flex:1,minWidth:0}},G.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"8px",flexWrap:"wrap"}},G.default.createElement("h1",{style:{fontSize:"20px",fontWeight:700,color:d.textPrimary,margin:0}},v.name||"Untitled agent"),G.default.createElement(BS,{tone:VZ[v.status]||"neutral",darkMode:c},v.status),g&&G.default.createElement(BS,{tone:"info",darkMode:c},"unsaved")),G.default.createElement("p",{style:{fontSize:"13px",color:d.textSecondary,margin:"4px 0 0"}},v.description||"No description")),G.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"8px",flexShrink:0}},i&&G.default.createElement(Kl,{variant:"secondary",colors:d,onClick:i},"Duplicate"),G.default.createElement(Kl,{variant:"primary",colors:d,onClick:a},"Run Agent"))),G.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"20px",borderBottom:`1px solid ${d.cardBorder}`,overflowX:"auto"}},G.default.createElement(tj,{tabs:$Z,active:f,onChange:p,colors:d}),G.default.createElement("div",{style:{width:"1px",height:"18px",background:d.borderStrong,flexShrink:0}}),G.default.createElement(tj,{tabs:rj,active:f,onChange:p,colors:d})),R?G.default.createElement("div",null,f==="traces"&&G.default.createElement(Wl,{agentClass:E,embedded:!0}),f==="metrics"&&G.default.createElement(LS,{agent:e,embedded:!0}),f==="interactions"&&G.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"12px"}},o&&G.default.createElement("div",{style:{display:"flex",justifyContent:"flex-end"}},G.default.createElement("button",{type:"button",onClick:o,style:{background:"none",border:"none",cursor:"pointer",fontFamily:Oe.mono,fontSize:"11px",color:"#3b82f6"}},"run report ->")),G.default.createElement(Mi,{agentId:e.id,embedded:!0})),f==="evals"&&G.default.createElement(Ff,{embedded:!0,agentId:e.id}),f==="feedback"&&G.default.createElement(GZ,{colors:d})):G.default.createElement("div",{style:{background:d.cardBg,border:`1px solid ${d.cardBorder}`,borderRadius:"12px",padding:"20px"}},f==="config"&&G.default.createElement(FZ,{formData:v,updateField:j,providerModels:_,colors:d,darkMode:c,onDelete:n}),f==="instructions"&&G.default.createElement(qZ,{formData:v,updateField:j,meta:t,toggleArrayItem:z,colors:d}),f==="tools"&&G.default.createElement(YZ,{formData:v,meta:t,toggleArrayItem:z,colors:d}),f==="versions"&&G.default.createElement(WZ,{versions:b,agentId:e.id,onRestore:L,colors:d,darkMode:c}),f==="code"&&G.default.createElement(KZ,{code:A,colors:d})),g&&G.default.createElement("div",{style:{position:"sticky",bottom:"0",display:"flex",alignItems:"center",justifyContent:"space-between",gap:"12px",background:d.cardBg,border:`1px solid ${d.borderStrong}`,borderRadius:"12px",padding:"10px 14px",boxShadow:"0 4px 16px rgba(0,0,0,0.12)"}},G.default.createElement("span",{style:{fontSize:"13px",color:d.textSecondary}},"Unsaved changes"),G.default.createElement("div",{style:{display:"flex",gap:"8px"}},G.default.createElement(Kl,{variant:"primary",size:"sm",colors:d,onClick:X,disabled:s},s?"Saving\u2026":"Save Changes"))))}var Ua=e=>({display:"block",fontSize:"13px",fontWeight:500,color:e.textCell,marginBottom:"6px"}),Di=e=>({width:"100%",padding:"8px 12px",borderRadius:"8px",border:`1px solid ${e.inputBorder}`,background:e.inputBg,color:e.textPrimary,fontFamily:"inherit",fontSize:"13px"}),nj=e=>({fontFamily:Oe.mono,fontSize:"11px",fontWeight:600,letterSpacing:"0.06em",textTransform:"uppercase",color:e.textSecondary});function FZ({formData:e,updateField:t,providerModels:r,colors:n,darkMode:a,onDelete:i}){return G.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"20px"}},G.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"16px"}},G.default.createElement("div",null,G.default.createElement("label",{style:Ua(n)},"Agent Name"),G.default.createElement("input",{type:"text",value:e.name,onChange:o=>t("name",o.target.value),className:"aa-field",style:Di(n)})),G.default.createElement("div",null,G.default.createElement("label",{style:Ua(n)},"Preset Type"),G.default.createElement("select",{value:e.preset_type,onChange:o=>t("preset_type",o.target.value),className:"aa-field",style:Di(n)},Object.keys(Hs).map(o=>G.default.createElement("option",{key:o,value:o},o))))),G.default.createElement("div",null,G.default.createElement("label",{style:Ua(n)},"Description"),G.default.createElement("textarea",{value:e.description,onChange:o=>t("description",o.target.value),rows:3,className:"aa-field",style:{...Di(n),resize:"vertical"}})),G.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"16px"}},G.default.createElement("div",null,G.default.createElement("label",{style:Ua(n)},"Provider"),G.default.createElement("select",{value:e.provider,onChange:o=>{t("provider",o.target.value),t("model",r[o.target.value][0])},className:"aa-field",style:Di(n)},Object.keys(r).map(o=>G.default.createElement("option",{key:o,value:o},o.charAt(0).toUpperCase()+o.slice(1))))),G.default.createElement("div",null,G.default.createElement("label",{style:Ua(n)},"Model"),G.default.createElement("select",{value:e.model,onChange:o=>t("model",o.target.value),className:"aa-field",style:{...Di(n),fontFamily:Oe.mono}},(r[e.provider]?.includes(e.model)?r[e.provider]:[e.model,...r[e.provider]||[]]).filter(Boolean).map(o=>G.default.createElement("option",{key:o,value:o},o))))),G.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"16px"}},G.default.createElement("div",null,G.default.createElement("label",{style:Ua(n)},"Temperature"," ",G.default.createElement("span",{style:{fontFamily:Oe.mono,color:n.textSecondary}},e.model_config.temperature)),G.default.createElement("input",{type:"range",min:"0",max:"1",step:"0.1",value:e.model_config.temperature,onChange:o=>t("model_config",{...e.model_config,temperature:parseFloat(o.target.value)}),style:{width:"100%",accentColor:la}})),G.default.createElement("div",null,G.default.createElement("label",{style:Ua(n)},"Status"),G.default.createElement("select",{value:e.status,onChange:o=>t("status",o.target.value),className:"aa-field",style:Di(n)},G.default.createElement("option",{value:"draft"},"Draft"),G.default.createElement("option",{value:"active"},"Active"),G.default.createElement("option",{value:"archived"},"Archived")))),i&&G.default.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",gap:"12px",borderTop:`1px solid ${n.cardBorder}`,paddingTop:"16px"}},G.default.createElement("div",null,G.default.createElement("div",{style:nj(n)},"Danger zone"),G.default.createElement("p",{style:{fontSize:"13px",color:n.textMuted,margin:"4px 0 0"}},"Deleting an agent removes its configuration and version history.")),G.default.createElement(Kl,{variant:"danger",size:"sm",colors:n,onClick:i},"Delete Agent")))}function qZ({formData:e,updateField:t,meta:r,toggleArrayItem:n,colors:a}){let i=e.action_prompts||[],o=(u,c)=>{t("action_prompts",i.map((d,f)=>f===u?{...d,...c}:d))},l=()=>{t("action_prompts",[...i,{name:"",prompt:"",expose_as_tool:!1}])},s=u=>{t("action_prompts",i.filter((c,d)=>d!==u))};return G.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"20px"}},G.default.createElement("div",null,G.default.createElement("label",{style:Ua(a)},"System Instructions"),G.default.createElement("textarea",{value:e.instructions,onChange:u=>t("instructions",u.target.value),placeholder:"You are a helpful AI assistant...",rows:12,className:"aa-field",style:{...Di(a),fontFamily:Oe.mono,resize:"vertical"}})),G.default.createElement("div",null,G.default.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"6px"}},G.default.createElement("label",{style:{...Ua(a),marginBottom:0}},"Action Prompts"),G.default.createElement(Kl,{variant:"secondary",size:"sm",colors:a,onClick:l},"+ Add action")),G.default.createElement("p",{style:{fontSize:"12px",color:a.textMuted,margin:"0 0 12px"}},"Named actions run with their prompt stacked below the system instructions. The default ",G.default.createElement("span",{style:{fontFamily:Oe.mono}},"#ask")," action uses the system instructions alone."),i.length>0&&G.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"12px"}},i.map((u,c)=>G.default.createElement("div",{key:c,style:{border:`1px solid ${a.cardBorder}`,borderRadius:"10px",padding:"12px",display:"flex",flexDirection:"column",gap:"8px"}},G.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"8px"}},G.default.createElement("span",{style:{fontFamily:Oe.mono,fontSize:"13px",color:a.textMuted}},"#"),G.default.createElement("input",{type:"text",value:u.name,onChange:d=>o(c,{name:d.target.value.toLowerCase().replace(/[^a-z0-9_]/g,"_")}),placeholder:"action_name",className:"aa-field",style:{...Di(a),flex:1,padding:"6px 10px",fontFamily:Oe.mono}}),G.default.createElement("label",{style:{display:"flex",alignItems:"center",gap:"6px",fontSize:"12px",color:a.textSecondary,whiteSpace:"nowrap"}},G.default.createElement("input",{type:"checkbox",checked:!!u.expose_as_tool,onChange:d=>o(c,{expose_as_tool:d.target.checked}),style:{accentColor:la}}),"Expose as tool"),G.default.createElement("button",{type:"button",onClick:()=>s(c),title:"Remove action",style:{fontFamily:Oe.mono,fontSize:"12px",color:a.textMuted,background:"none",border:"none",cursor:"pointer",padding:"4px"}},"[x]")),G.default.createElement("textarea",{value:u.prompt,onChange:d=>o(c,{prompt:d.target.value}),placeholder:"This action's system prompt, applied on top of the base instructions...",rows:4,className:"aa-field",style:{...Di(a),fontFamily:Oe.mono,resize:"vertical"}}))))),G.default.createElement("div",null,G.default.createElement("label",{style:Ua(a)},"Instruction Sets"),G.default.createElement("div",{style:{display:"flex",flexWrap:"wrap",gap:"8px"}},r.instructionSets?.map(u=>{let c=e.instruction_sets.includes(u);return G.default.createElement("button",{key:u,type:"button",onClick:()=>n("instruction_sets",u),style:{padding:"5px 12px",borderRadius:"999px",fontSize:"13px",cursor:"pointer",border:`1px solid ${c?la:a.inputBorder}`,background:c?la:"transparent",color:c?"#ffffff":a.textCell}},u)}))))}function YZ({formData:e,meta:t,toggleArrayItem:r,colors:n}){let a=i=>({terminal:"$",playwright:">",filesystem:"/",code:"<>",database:"#",slack:"@",fetch:"~",search:"?",edit:"*",translate:"[]",memory:"M"})[i]||"[]";return G.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"20px"}},G.default.createElement("p",{style:{fontSize:"13px",color:n.textSecondary,margin:0}},"Select the tools your agent can use."),G.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(140px, 1fr))",gap:"12px"}},t.availableTools?.map(i=>{let o=e.tools.includes(i);return G.default.createElement("button",{key:i,type:"button",onClick:()=>r("tools",i),style:{padding:"14px",borderRadius:"12px",textAlign:"center",cursor:"pointer",border:`1px solid ${o?la:n.cardBorder}`,background:o?"rgba(239,68,68,0.08)":"transparent",color:o?la:n.textCell}},G.default.createElement("span",{style:{display:"block",fontFamily:Oe.mono,fontSize:"20px",marginBottom:"6px"}},a(i)),G.default.createElement("span",{style:{fontSize:"13px",fontWeight:500,textTransform:"capitalize"}},i))})),e.tools.length>0&&G.default.createElement("div",{style:{background:n.innerBg,borderRadius:"10px",padding:"14px"}},G.default.createElement("div",{style:{...nj(n),marginBottom:"10px"}},"Selected tools \xB7 ",e.tools.length),G.default.createElement("div",{style:{display:"flex",flexWrap:"wrap",gap:"8px"}},e.tools.map(i=>G.default.createElement("span",{key:i,style:{fontFamily:Oe.mono,fontSize:"11px",padding:"3px 8px",borderRadius:"6px",border:`1px solid ${n.inputBorder}`,color:n.textCell}},a(i)," ",i)))))}function WZ({versions:e,agentId:t,onRestore:r,colors:n,darkMode:a}){let i=async o=>{if(confirm("Restore this version? This will create a new version with the restored configuration."))try{await fetch(`/api/agents/${t}/restore`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({version_id:o})}),r()}catch(l){console.error("Failed to restore version:",l)}};return G.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"16px"}},G.default.createElement("p",{style:{fontSize:"13px",color:n.textSecondary,margin:0}},"View and restore previous versions of this agent."),e.length>0?G.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"10px"}},e.map(o=>{let l=o.is_latest||o.isLatest;return G.default.createElement("div",{key:o.id,style:{display:"flex",alignItems:"flex-start",justifyContent:"space-between",gap:"12px",padding:"14px",borderRadius:"10px",border:`1px solid ${l?la:n.cardBorder}`,background:l?"rgba(239,68,68,0.06)":"transparent"}},G.default.createElement("div",{style:{minWidth:0}},G.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"8px"}},G.default.createElement("span",{style:{fontFamily:Oe.mono,fontSize:"13px",fontWeight:600,color:n.textPrimary}},"v",o.version_number||o.versionNumber),l&&G.default.createElement(BS,{tone:"error",darkMode:a},"current")),G.default.createElement("p",{style:{fontSize:"13px",color:n.textCell,margin:"4px 0 0"}},o.change_summary||o.changeSummary),G.default.createElement("p",{style:{fontFamily:Oe.mono,fontSize:"11px",color:n.textMuted,margin:"4px 0 0"}},new Date(o.created_at||o.createdAt).toLocaleString())),!l&&G.default.createElement(Kl,{variant:"secondary",size:"sm",colors:n,onClick:()=>i(o.id)},"Restore"))})):G.default.createElement(aj,{colors:n,glyph:"[#]",title:"No version history",hint:"Saving a change records a new version."}))}function KZ({code:e,colors:t}){let r=()=>{navigator.clipboard.writeText(e)};return G.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"12px"}},G.default.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between"}},G.default.createElement("p",{style:{fontSize:"13px",color:t.textSecondary,margin:0}},"Generated Ruby code for this agent."),G.default.createElement(Kl,{variant:"secondary",size:"sm",colors:t,onClick:r},"Copy")),G.default.createElement("pre",{style:{padding:"16px",background:"#111827",color:"#e5e7eb",borderRadius:"10px",overflowX:"auto",fontFamily:Oe.mono,fontSize:"12px",lineHeight:1.6,margin:0}},G.default.createElement("code",null,e||"Loading...")))}function GZ({colors:e}){return G.default.createElement("div",{style:{background:e.cardBg,border:`1px solid ${e.cardBorder}`,borderRadius:"12px",padding:"20px"}},G.default.createElement(aj,{colors:e,glyph:"[?]",title:"No feedback recorded",hint:"Thumbs and comments are not captured yet. Once an interaction feedback source is recorded, CSAT and per-trace comments appear here."}))}function aj({colors:e,glyph:t,title:r,hint:n}){return G.default.createElement("div",{style:{textAlign:"center",padding:"40px 20px"}},G.default.createElement("div",{style:{fontFamily:Oe.mono,fontSize:"20px",color:e.textMuted}},t),G.default.createElement("div",{style:{fontSize:"14px",fontWeight:600,color:e.textPrimary,marginTop:"10px"}},r),G.default.createElement("p",{style:{fontSize:"13px",color:e.textMuted,margin:"6px auto 0",maxWidth:"420px"}},n))}var re=te(ae());async function cc(){let e=window.ACTIVE_AGENT_DASHBOARD?.meta?.upgradeUrl;if(!e)throw new Error("This dashboard has no billing configured.");window.location.href=e}var XZ={llm:{role:"assistant",label:"LLM"},tool:{role:"tool",label:"Tool"},agent:{role:"developer",label:"Agent"},mcp:{role:"system",label:"MCP"}},ZZ=e=>{let t=XZ[e]||{role:e,label:e};return{...ta(t.role,!1),label:t.label}};function US({agent:e,onBack:t}){let[r,n]=(0,re.useState)(""),a=["ask",...(e.action_prompts||[]).map(y=>y.name).filter(Boolean)],[i,o]=(0,re.useState)("ask"),[l,s]=(0,re.useState)([]),[u,c]=(0,re.useState)(!1),[d,f]=(0,re.useState)(null),[p,v]=(0,re.useState)(null),[h,b]=(0,re.useState)({}),[m,g]=(0,re.useState)(!1),[x,A]=(0,re.useState)(null),k=(0,re.useRef)(null);(0,re.useEffect)(()=>{_()},[e.id]),(0,re.useEffect)(()=>{k.current&&(k.current.scrollTop=k.current.scrollHeight)},[d?.output]);let _=async()=>{try{let C=await(await fetch(`/api/agents/${e.id}/runs?per_page=10`)).json();s(C.runs)}catch(y){console.error("Failed to load runs:",y)}},M=1200,E=600*1e3,L=async()=>{if(!(!r.trim()||u)){c(!0),f({status:"pending",input_prompt:r,output:"",logs:[],started_at:new Date().toISOString()});try{let y=await fetch(`/api/agents/${e.id}/execute`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:r,action_name:i})}),C=await y.json();if(y.status===402&&C.upgrade_required){v(C.usage),f(T=>({...T,status:"failed",error_message:C.message||"Plan limit reached"}));return}if(!y.ok)throw new Error(C.error||"Run failed");let O=C.run.id,D=Date.now(),N=async()=>{try{let T=await fetch(`/api/runs/${O}`);if(T.ok){let $=await T.json();if(f($.run),!["pending","running"].includes($.run.status)){c(!1),_();return}}}catch{}Date.now()-D({...C,status:"failed",error_message:y.message})),c(!1)}}},I=y=>{let C=new Map;return(y?.logs||[]).filter(O=>O.eid).forEach(O=>{let D=C.get(O.eid)||{eid:O.eid};O.status==="started"?Object.assign(D,O,{input:O.detail,output:D.output}):Object.assign(D,O,{input:D.input,output:O.detail}),C.set(O.eid,D)}),[...C.values()]},j=y=>{if(!y)return null;try{return JSON.stringify(JSON.parse(y),null,2)}catch{return y}},z=y=>{y.key==="Enter"&&(y.metaKey||y.ctrlKey)&&L()},X=y=>{switch(y){case"complete":return"text-green-600 bg-green-100";case"failed":return"text-red-600 bg-red-100";case"running":return"text-blue-600 bg-blue-100";case"pending":return"text-yellow-600 bg-yellow-100";default:return"text-gray-600 bg-gray-100"}},R=y=>y?y<1e3?`${y}ms`:`${(y/1e3).toFixed(2)}s`:"-",Q=async()=>{g(!0),A(null);try{await cc({planSlug:"pro"})}catch(y){A(y.message),g(!1)}};return re.default.createElement("div",{className:"grid grid-cols-3 gap-6 h-full"},re.default.createElement("div",{className:"col-span-2 flex flex-col space-y-4"},p&&re.default.createElement("div",{className:"bg-amber-50 border border-amber-200 rounded-xl p-4 flex items-center justify-between gap-4"},re.default.createElement("div",null,re.default.createElement("p",{className:"font-medium text-amber-900"},"Monthly run limit reached"),re.default.createElement("p",{className:"text-sm text-amber-700 mt-0.5"},"You've used ",p.runs_used," of ",p.runs_limit," runs on the"," ",p.plan||"free"," plan. Upgrade to keep running agents."),x&&re.default.createElement("p",{className:"text-sm text-red-600 mt-1"},x)),re.default.createElement("div",{className:"flex items-center gap-2 flex-shrink-0"},re.default.createElement("a",{href:"/pricing",className:"px-4 py-2 text-sm text-amber-800 hover:text-amber-900"},"See plans"),re.default.createElement("button",{onClick:Q,disabled:m,className:"px-4 py-2 text-sm bg-amber-500 text-white rounded-lg hover:bg-amber-600 transition-colors disabled:opacity-50"},m?"Redirecting\u2026":"Upgrade to Pro"))),re.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},re.default.createElement("div",{className:"flex items-start space-x-4"},re.default.createElement("div",{className:"flex-shrink-0"},re.default.createElement(Tt,{size:50})),re.default.createElement("div",{className:"flex-1"},re.default.createElement("textarea",{value:r,onChange:y=>n(y.target.value),onKeyDown:z,placeholder:"Enter your prompt here... (Cmd+Enter to run)",rows:4,disabled:u,className:"w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent resize-none"}),re.default.createElement("div",{className:"flex items-center justify-between mt-3"},re.default.createElement("div",{className:"flex items-center gap-3"},a.length>1&&re.default.createElement("select",{value:i,onChange:y=>o(y.target.value),disabled:u,className:"px-2 py-1 border border-gray-300 rounded-lg text-sm font-mono text-gray-700 focus:ring-2 focus:ring-red-500",title:"Agent action to invoke"},a.map(y=>re.default.createElement("option",{key:y,value:y},"#",y))),re.default.createElement("span",{className:"text-xs text-gray-400"},"Press ",re.default.createElement("kbd",{className:"px-1.5 py-0.5 bg-gray-100 rounded text-gray-600"},"\u2318")," + ",re.default.createElement("kbd",{className:"px-1.5 py-0.5 bg-gray-100 rounded text-gray-600"},"Enter")," to run")),re.default.createElement("button",{onClick:L,disabled:!r.trim()||u,className:`flex items-center space-x-2 px-6 py-2 rounded-lg transition-colors ${r.trim()&&!u?"bg-emerald-500 text-white hover:bg-emerald-600":"bg-gray-200 text-gray-400 cursor-not-allowed"}`},u?re.default.createElement(re.default.Fragment,null,re.default.createElement("span",{className:"animate-spin",style:{fontFamily:Oe.mono}},"[~]"),re.default.createElement("span",null,"Running...")):re.default.createElement(re.default.Fragment,null,re.default.createElement("span",{style:{fontFamily:Oe.mono}},"[>]"),re.default.createElement("span",null,"Run"))))))),re.default.createElement("div",{className:"flex-1 bg-white rounded-xl border border-gray-200 flex flex-col overflow-hidden"},re.default.createElement("div",{className:"px-4 py-3 border-b border-gray-200 flex items-center justify-between"},re.default.createElement("h3",{className:"font-medium text-gray-900"},"Output"),d&&re.default.createElement("div",{className:"flex items-center space-x-3 text-sm"},re.default.createElement("span",{className:`px-2 py-0.5 rounded-full text-xs font-medium ${X(d.status)}`},d.status),d.output_metadata?.model&&re.default.createElement("span",{className:"px-2 py-0.5 rounded bg-indigo-50 text-indigo-700 text-xs font-mono",title:"Model that generated this run"},d.output_metadata.provider,"/",d.output_metadata.model),d.duration_ms&&re.default.createElement("span",{className:"text-gray-400"},R(d.duration_ms)),d.total_tokens&&re.default.createElement("span",{className:"text-gray-400"},d.total_tokens," tokens"),d.trace_id&&re.default.createElement("a",{href:`/dashboard/traces?trace=${d.trace_id}`,className:"text-gray-400 hover:text-red-500 font-mono text-xs",title:`${d.trace_id} \u2014 open in Traces`},"trace:",d.trace_id.slice(0,8)," \u2192"))),re.default.createElement("div",{ref:k,className:"flex-1 p-4 overflow-auto bg-gray-50 font-mono text-sm"},d?re.default.createElement(re.default.Fragment,null,I(d).length>0&&re.default.createElement("div",{className:"mb-4 space-y-2"},I(d).map(y=>{let C=!!h[y.eid],O=!!(y.input||y.output),D=ZZ(y.kind),N=MS(!1);return re.default.createElement("div",{key:y.eid},re.default.createElement("div",{className:`flex gap-3 items-start rounded-lg -mx-2 px-2 py-1 ${O?"cursor-pointer hover:bg-black/5":""}`,onClick:O?()=>b(T=>({...T,[y.eid]:!T[y.eid]})):void 0,title:O?"Click to inspect input/output":void 0,style:C?{background:"rgba(0,0,0,0.03)"}:{}},re.default.createElement("span",{className:"px-2 py-0.5 rounded text-xs font-medium flex-shrink-0 mt-0.5",style:{background:D.background,color:D.color,minWidth:"72px",textAlign:"center"}},D.label),re.default.createElement("div",{className:"min-w-0 flex-1"},re.default.createElement("div",{className:"text-sm break-words text-gray-800"},y.label,y.output&&!C&&re.default.createElement("span",{className:"text-gray-400"}," \u201C",y.output.slice(0,160),y.output.length>160?"\u2026":"","\u201D")),re.default.createElement("div",{className:"text-xs mt-0.5 font-mono flex items-center gap-2 flex-wrap text-gray-400"},y.status==="started"?re.default.createElement("span",{className:"text-blue-500 animate-pulse"},"running\u2026"):re.default.createElement("span",{className:y.status==="error"?"text-red-500":""},y.status==="error"?"failed":"\u2713",y.duration_ms!=null&&` ${R(y.duration_ms)}`),y.at&&re.default.createElement("span",null,new Date(y.at).toLocaleTimeString()))),O&&re.default.createElement("svg",{className:`w-3.5 h-3.5 mt-1 flex-shrink-0 transition-transform ${C?"rotate-180":""} text-gray-400`,fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},re.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"}))),C&&re.default.createElement("div",{className:"ml-3 mt-1 mb-2 pl-4 space-y-2 border-l-2",style:{borderColor:D.color+"55"}},y.input&&re.default.createElement("div",null,re.default.createElement("div",{className:"text-xs uppercase tracking-wide mb-1 text-gray-400"},"Input"),re.default.createElement("pre",{style:{...N,whiteSpace:"pre-wrap",maxHeight:"160px",overflowY:"auto"}},j(y.input))),y.output&&re.default.createElement("div",null,re.default.createElement("div",{className:"text-xs uppercase tracking-wide mb-1 text-gray-400"},y.status==="error"?"Error":"Result"),re.default.createElement("pre",{style:{...N,whiteSpace:"pre-wrap",maxHeight:"224px",overflowY:"auto",...y.status==="error"?{background:"#fef2f2",color:"#b91c1c"}:{}}},j(y.output)))))})),["pending","running"].includes(d.status)?re.default.createElement("div",{className:"flex items-center space-x-2 text-gray-500"},re.default.createElement("span",{className:"animate-pulse"},"\u25CF"),re.default.createElement("span",null,I(d).length>0?"Working\u2026":"Starting run\u2026")):d.error_message?re.default.createElement("div",{className:"text-red-600"},re.default.createElement("div",{className:"font-semibold mb-2"},"Error:"),re.default.createElement("pre",{className:"whitespace-pre-wrap"},d.error_message)):re.default.createElement("pre",{className:"whitespace-pre-wrap text-gray-800"},d.output||"No output")):re.default.createElement("div",{className:"text-gray-400 text-center py-12"},"Enter a prompt and click Run to test your agent")))),re.default.createElement("div",{className:"space-y-4"},re.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},re.default.createElement("div",{className:"flex items-center space-x-3 mb-4"},re.default.createElement(Tt,{size:60}),re.default.createElement("div",null,re.default.createElement("h3",{className:"font-semibold text-gray-900"},e.name),re.default.createElement("p",{className:"text-sm text-gray-500"},e.provider," / ",e.model))),re.default.createElement("button",{onClick:t,className:"w-full px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors"},"\u2190 Back to Editor")),re.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},re.default.createElement("h4",{className:"font-medium text-gray-900 mb-3"},"Configuration"),re.default.createElement("dl",{className:"space-y-2 text-sm"},re.default.createElement("div",{className:"flex justify-between"},re.default.createElement("dt",{className:"text-gray-500"},"Temperature"),re.default.createElement("dd",{className:"text-gray-900"},e.modelConfig?.temperature||e.model_config?.temperature||.7)),re.default.createElement("div",{className:"flex justify-between"},re.default.createElement("dt",{className:"text-gray-500"},"Tools"),re.default.createElement("dd",{className:"text-gray-900"},e.tools?.length||0)),re.default.createElement("div",{className:"flex justify-between"},re.default.createElement("dt",{className:"text-gray-500"},"Status"),re.default.createElement("dd",{className:`px-2 py-0.5 rounded text-xs ${e.status==="active"?"bg-green-100 text-green-700":"bg-gray-100 text-gray-600"}`},e.status))),e.instructions&&re.default.createElement("div",{className:"mt-3 pt-3 border-t border-gray-100"},re.default.createElement("div",{className:"text-xs text-gray-500 mb-1.5"},"System Instructions"),re.default.createElement("p",{className:"text-xs text-gray-700 whitespace-pre-wrap max-h-44 overflow-y-auto font-mono leading-relaxed"},e.instructions))),re.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 overflow-hidden"},re.default.createElement("div",{className:"px-4 py-3 border-b border-gray-100"},re.default.createElement("h4",{className:"font-medium text-gray-900"},"Recent Runs")),re.default.createElement("div",{className:"max-h-64 overflow-auto"},l.length>0?re.default.createElement("div",{className:"divide-y divide-gray-100"},l.map(y=>re.default.createElement("div",{key:y.id,className:"px-4 py-3 hover:bg-gray-50 cursor-pointer",onClick:()=>f(y)},re.default.createElement("div",{className:"flex items-center justify-between mb-1"},re.default.createElement("span",{className:`px-2 py-0.5 rounded-full text-xs font-medium ${X(y.status)}`},y.status),re.default.createElement("span",{className:"text-xs text-gray-400"},R(y.duration_ms))),re.default.createElement("p",{className:"text-sm text-gray-600 truncate"},y.input_preview||y.input_prompt?.substring(0,50)),re.default.createElement("p",{className:"text-xs text-gray-400 mt-1 flex items-center gap-2 flex-wrap"},y.model&&re.default.createElement("span",{className:"px-1.5 py-0.5 rounded bg-indigo-50 text-indigo-700 font-mono"},y.model),re.default.createElement("span",null,new Date(y.created_at).toLocaleString()))))):re.default.createElement("div",{className:"px-4 py-8 text-center text-gray-400 text-sm"},"No runs yet"))),re.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},re.default.createElement("h4",{className:"font-medium text-gray-900 mb-3"},"Example Prompts"),re.default.createElement("div",{className:"space-y-2"},["Hello, what can you help me with?","List the files in the current directory","Explain how this code works"].map((y,C)=>re.default.createElement("button",{key:C,onClick:()=>n(y),className:"w-full text-left px-3 py-2 text-sm text-gray-600 hover:bg-gray-50 rounded-lg transition-colors border border-gray-200"},y))))))}var he=te(ae()),QZ=[{value:7,label:"7 days"},{value:14,label:"14 days"},{value:30,label:"30 days"},{value:90,label:"90 days"}];function $S({onSelectAgent:e}){let[t,r]=(0,he.useState)(null),[n,a]=(0,he.useState)(!0),[i,o]=(0,he.useState)(30);(0,he.useEffect)(()=>{l()},[i]);let l=async()=>{a(!0);try{let p=await(await fetch(`/api/analytics?days=${i}`)).json();r(p)}catch(f){console.error("Failed to load analytics:",f)}finally{a(!1)}},s=f=>f?f>=1e6?`${(f/1e6).toFixed(1)}M`:f>=1e3?`${(f/1e3).toFixed(1)}K`:f.toString():"0",u=f=>f?f<1e3?`${f}ms`:`${(f/1e3).toFixed(2)}s`:"-";if(n&&!t)return he.default.createElement("div",{className:"flex items-center justify-center h-64"},he.default.createElement("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-red-500"}));let c=t?.charts?.runs_by_day?.length>0?Math.max(...t.charts.runs_by_day.map(f=>f.count)):1,d=t?.charts?.tokens_by_day?.length>0?Math.max(...t.charts.tokens_by_day.map(f=>f.tokens)):1;return he.default.createElement("div",{className:"space-y-6"},he.default.createElement("div",{className:"flex items-center justify-between"},he.default.createElement("div",null,he.default.createElement("h1",{className:"text-2xl font-bold text-gray-900"},"Analytics Dashboard"),he.default.createElement("p",{className:"text-sm text-gray-500"},"Overview of all your agents' performance")),he.default.createElement("select",{value:i,onChange:f=>o(Number(f.target.value)),className:"px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500"},QZ.map(f=>he.default.createElement("option",{key:f.value,value:f.value},"Last ",f.label)))),he.default.createElement("div",{className:"grid grid-cols-2 lg:grid-cols-5 gap-4"},he.default.createElement(qf,{title:"Total Agents",value:t?.summary?.total_agents||0,subtitle:`${t?.summary?.active_agents||0} active`,color:"rose"}),he.default.createElement(qf,{title:"Total Runs",value:t?.summary?.total_runs||0,subtitle:`${t?.period_days} day period`,color:"blue"}),he.default.createElement(qf,{title:"Success Rate",value:`${t?.summary?.success_rate||0}%`,subtitle:`${t?.summary?.completed_runs||0} completed`,color:"green"}),he.default.createElement(qf,{title:"Avg Duration",value:u(t?.summary?.avg_duration_ms),subtitle:"per run",color:"purple"}),he.default.createElement(qf,{title:"Total Tokens",value:s(t?.summary?.total_tokens),subtitle:`${s(t?.summary?.avg_tokens_per_run)}/run avg`,color:"amber"})),he.default.createElement("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-6"},he.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-6"},he.default.createElement("h3",{className:"font-semibold text-gray-900 mb-4"},"Runs Over Time"),t?.charts?.runs_by_day?.length>0?he.default.createElement("div",{className:"h-48"},he.default.createElement("div",{className:"flex items-end justify-between h-full space-x-1"},t.charts.runs_by_day.map((f,p)=>he.default.createElement("div",{key:f.date,className:"flex-1 flex flex-col items-center group"},he.default.createElement("div",{className:"relative w-full"},he.default.createElement("div",{className:"w-full bg-red-500 rounded-t transition-all hover:bg-red-600",style:{height:`${f.count/c*150}px`,minHeight:f.count>0?"4px":"0"}}),he.default.createElement("div",{className:"absolute -top-8 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-10"},f.count," runs"))))),he.default.createElement("div",{className:"flex justify-between mt-2 text-xs text-gray-400"},he.default.createElement("span",null,t.charts.runs_by_day[0]?.date&&new Date(t.charts.runs_by_day[0].date).toLocaleDateString("en-US",{month:"short",day:"numeric"})),he.default.createElement("span",null,t.charts.runs_by_day[t.charts.runs_by_day.length-1]?.date&&new Date(t.charts.runs_by_day[t.charts.runs_by_day.length-1].date).toLocaleDateString("en-US",{month:"short",day:"numeric"})))):he.default.createElement("div",{className:"h-48 flex items-center justify-center text-gray-400"},"No runs in this period")),he.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-6"},he.default.createElement("h3",{className:"font-semibold text-gray-900 mb-4"},"Token Usage Over Time"),t?.charts?.tokens_by_day?.length>0?he.default.createElement("div",{className:"h-48"},he.default.createElement("div",{className:"flex items-end justify-between h-full space-x-1"},t.charts.tokens_by_day.map((f,p)=>he.default.createElement("div",{key:f.date,className:"flex-1 flex flex-col items-center group"},he.default.createElement("div",{className:"relative w-full"},he.default.createElement("div",{className:"w-full bg-purple-500 rounded-t transition-all hover:bg-purple-600",style:{height:`${f.tokens/d*150}px`,minHeight:f.tokens>0?"4px":"0"}}),he.default.createElement("div",{className:"absolute -top-8 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-10"},s(f.tokens)," tokens"))))),he.default.createElement("div",{className:"flex justify-between mt-2 text-xs text-gray-400"},he.default.createElement("span",null,t.charts.tokens_by_day[0]?.date&&new Date(t.charts.tokens_by_day[0].date).toLocaleDateString("en-US",{month:"short",day:"numeric"})),he.default.createElement("span",null,t.charts.tokens_by_day[t.charts.tokens_by_day.length-1]?.date&&new Date(t.charts.tokens_by_day[t.charts.tokens_by_day.length-1].date).toLocaleDateString("en-US",{month:"short",day:"numeric"})))):he.default.createElement("div",{className:"h-48 flex items-center justify-center text-gray-400"},"No token usage in this period"))),he.default.createElement("div",{className:"grid grid-cols-1 lg:grid-cols-3 gap-6"},he.default.createElement("div",{className:"lg:col-span-2 bg-white rounded-xl border border-gray-200 p-6"},he.default.createElement("h3",{className:"font-semibold text-gray-900 mb-4"},"Top Agents"),t?.top_agents?.length>0?he.default.createElement("div",{className:"space-y-3"},t.top_agents.map((f,p)=>he.default.createElement("div",{key:f.id,onClick:()=>e?.(f),className:"flex items-center space-x-4 p-3 rounded-lg hover:bg-gray-50 cursor-pointer transition-colors"},he.default.createElement("div",{className:"w-8 h-8 rounded-full bg-red-100 flex items-center justify-center text-red-600 font-semibold"},p+1),he.default.createElement("div",{className:"flex-1 min-w-0"},he.default.createElement("p",{className:"font-medium text-gray-900 truncate"},f.name),he.default.createElement("p",{className:"text-sm text-gray-500"},f.run_count," runs")),he.default.createElement("div",{className:"text-right"},he.default.createElement("p",{className:"font-medium text-gray-900"},s(f.total_tokens)),he.default.createElement("p",{className:"text-xs text-gray-500"},"tokens"))))):he.default.createElement("div",{className:"py-8 text-center text-gray-400"},"No agent activity yet")),he.default.createElement("div",{className:"space-y-6"},he.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-6"},he.default.createElement("h3",{className:"font-semibold text-gray-900 mb-4"},"By Provider"),Object.keys(t?.provider_breakdown||{}).length>0?he.default.createElement("div",{className:"space-y-3"},Object.entries(t.provider_breakdown).map(([f,p])=>{let v=t?.summary?.total_runs||1,h=(p/v*100).toFixed(1);return he.default.createElement("div",{key:f},he.default.createElement("div",{className:"flex justify-between text-sm mb-1"},he.default.createElement("span",{className:"capitalize text-gray-600"},f),he.default.createElement("span",{className:"text-gray-900 font-medium"},p)),he.default.createElement("div",{className:"h-2 bg-gray-100 rounded-full overflow-hidden"},he.default.createElement("div",{className:"h-full bg-blue-500 rounded-full",style:{width:`${h}%`}})))})):he.default.createElement("div",{className:"py-4 text-center text-gray-400 text-sm"},"No data")),he.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-6"},he.default.createElement("h3",{className:"font-semibold text-gray-900 mb-4"},"Token Usage"),he.default.createElement("div",{className:"space-y-4"},he.default.createElement("div",{className:"flex justify-between items-center"},he.default.createElement("span",{className:"text-sm text-gray-500"},"Input Tokens"),he.default.createElement("span",{className:"font-medium text-gray-900"},s(t?.summary?.total_input_tokens))),he.default.createElement("div",{className:"flex justify-between items-center"},he.default.createElement("span",{className:"text-sm text-gray-500"},"Output Tokens"),he.default.createElement("span",{className:"font-medium text-gray-900"},s(t?.summary?.total_output_tokens))),he.default.createElement("div",{className:"pt-2 border-t border-gray-100"},he.default.createElement("div",{className:"flex justify-between items-center"},he.default.createElement("span",{className:"text-sm font-medium text-gray-700"},"Total"),he.default.createElement("span",{className:"font-bold text-gray-900"},s(t?.summary?.total_tokens)))))))))}function qf({title:e,value:t,subtitle:r,color:n}){return he.default.createElement("div",{className:`bg-white rounded-xl border border-gray-200 border-l-4 ${{rose:"border-l-red-500",blue:"border-l-blue-500",green:"border-l-green-500",purple:"border-l-purple-500",amber:"border-l-amber-500"}[n]} p-4`},he.default.createElement("p",{className:"text-sm text-gray-500"},e),he.default.createElement("p",{className:"text-2xl font-bold text-gray-900 mt-1"},t),r&&he.default.createElement("p",{className:"text-xs text-gray-400 mt-1"},r))}var U=te(ae());function HS({agent:e,onBack:t}){let{timeWindow:r}=La(),[n,a]=(0,U.useState)([]),[i,o]=(0,U.useState)([]),[l,s]=(0,U.useState)(null),[u,c]=(0,U.useState)(null),[d,f]=(0,U.useState)([]),[p,v]=(0,U.useState)("run"),[h,b]=(0,U.useState)(!1),[m,g]=(0,U.useState)(!1),x=(0,U.useRef)(!1),A=(0,U.useRef)(!1),[k,_]=(0,U.useState)("recent"),[M,E]=(0,U.useState)({}),[L,I]=(0,U.useState)(!0),[j,z]=(0,U.useState)(1),[X,R]=(0,U.useState)(!0),[Q,y]=(0,U.useState)(""),[C,O]=(0,U.useState)(""),[D,N]=(0,U.useState)("recent"),T=(0,U.useRef)(null),$=`/dashboard/agents/${e.id}/interactions`;(0,U.useEffect)(()=>{Y()},[e.id,j,Q,C,D,r.minutes]),(0,U.useEffect)(()=>{z(1)},[r.minutes]),(0,U.useEffect)(()=>{fetch(`/api/interactions?agent_id=${e.id}`).then(K=>K.json()).then(K=>o(K.interactions||[])).catch(()=>{}).finally(()=>g(!0))},[e.id]),(0,U.useEffect)(()=>{let K=()=>{let xe=window.location.pathname,Ae=xe.match(/\/interactions\/runs\/(\d+)/),we=xe.match(/\/interactions\/sessions\/(\d+)/);if(Ae)v("run"),c(null),fe(parseInt(Ae[1],10),{updateUrl:!1});else if(we)v("all"),c({id:parseInt(we[1],10)});else if(xe.endsWith("/interactions/sessions")||xe.endsWith("/interactions/all"))xe.endsWith("/all")&&window.history.replaceState({},"",xe.replace(/\/all$/,"/sessions")),v("all"),c(null);else{x.current=!1,s(null),c(null),f([]);return}x.current=!0};return K(),window.addEventListener("popstate",K),()=>window.removeEventListener("popstate",K)},[e.id]),(0,U.useEffect)(()=>{x.current||A.current||!h||!m||v(n.length===0&&i.length>0?"all":"run")},[h,m,n.length,i.length]);let Y=async()=>{I(!0);try{let K=new URLSearchParams({page:j.toString(),per_page:"20",minutes:String(r.minutes)});Q&&K.append("status",Q),C&&K.append("source",C),D!=="recent"&&K.append("sort",D);let Ae=await(await fetch(`/api/agents/${e.id}/runs?${K}`)).json();a(j===1?Ae.runs:we=>[...we,...Ae.runs]),R(Ae.runs.length===20)}catch(K){console.error("Failed to load runs:",K)}finally{I(!1),b(!0)}},J=K=>{window.location.pathname!==K&&window.history.pushState({},"",K)},fe=async(K,{updateUrl:xe=!0}={})=>{try{let we=await(await fetch(`/api/runs/${K}`)).json();s(we.run),f(we.messages||[]),v("run"),c(null),xe&&J(`${$}/runs/${K}`)}catch(Ae){console.error("Failed to load run details:",Ae)}},pe=K=>{if(K.source==="reported"){let xe=K.trace_id||String(K.id).replace(/^trace-/,"");window.history.pushState(window.history.state,"",`/dashboard/traces/${xe}`),window.dispatchEvent(new CustomEvent("dashboard:navigate",{detail:{path:`/dashboard/traces/${xe}`}}));return}fe(K.record_id||K.id)},ge=()=>{s(null),c(null),f([]),v("run"),J($)},ze=K=>{v("all"),K?(c({id:K.id,name:K.display_name}),J(`${$}/sessions/${K.id}`)):(c(null),J(`${$}/sessions`))},se=K=>{A.current=!0,v(K),J(K==="all"?u?`${$}/sessions/${u.id}`:`${$}/sessions`:l?`${$}/runs/${l.id}`:$)},Ne=u?u.name||i.find(K=>K.id===u.id)?.display_name||`Session #${u.id}`:null,F=()=>{let K=new Map;n.forEach(we=>{let Ke=`${we.model||"unknown"}|${we.instructions_digest||"none"}`;K.has(Ke)||K.set(Ke,{key:Ke,model:we.model,instructionsDigest:we.instructions_digest,instructionsVersion:we.instructions_version,instructionsCodename:we.instructions_codename,instructionsPreview:we.instructions_preview,runs:[]}),K.get(Ke).runs.push(we)});let xe=[...K.values()].map(we=>({...we,count:we.runs.length,completed:we.runs.filter(Ke=>Ke.status==="complete").length,failed:we.runs.filter(Ke=>Ke.status==="failed").length,totalTokens:we.runs.reduce((Ke,xr)=>Ke+(xr.tokens||0),0),avgTokens:we.runs.reduce((Ke,xr)=>Ke+(xr.tokens||0),0)/we.runs.length,avgDuration:we.runs.reduce((Ke,xr)=>Ke+(xr.duration_ms||0),0)/we.runs.length,latest:we.runs[0],sortedRuns:[...we.runs].sort((Ke,xr)=>(xr.duration_ms||0)-(Ke.duration_ms||0))}));xe.sort((we,Ke)=>k==="longest"?Ke.avgDuration-we.avgDuration:new Date(Ke.latest.created_at)-new Date(we.latest.created_at));let Ae={runs:n.length,completed:n.filter(we=>we.status==="complete").length,tokens:n.reduce((we,Ke)=>we+(Ke.tokens||0),0),avgDuration:n.length?n.reduce((we,Ke)=>we+(Ke.duration_ms||0),0)/n.length:0,models:[...new Set(n.map(we=>we.model).filter(Boolean))]};return{cohorts:xe,totals:Ae}},H=K=>{switch(K){case"complete":return"bg-green-100 text-green-700";case"failed":return"bg-red-100 text-red-700";case"running":return"bg-blue-100 text-blue-700";case"pending":return"bg-yellow-100 text-yellow-700";case"cancelled":return"bg-gray-100 text-gray-500";default:return"bg-gray-100 text-gray-500"}},q=K=>K?K<1e3?`${K}ms`:K<6e4?`${(K/1e3).toFixed(1)}s`:`${Math.floor(K/6e4)}m ${Math.floor(K%6e4/1e3)}s`:"-",ee=K=>K==null?"\u2014":K===0?"$0":K<.01?`$${K.toFixed(4)}`:`$${K.toFixed(2)}`,ie=K=>{let xe=new Date(K),we=new Date-xe,Ke=Math.floor(we/6e4),xr=Math.floor(we/36e5),To=Math.floor(we/864e5);return Ke<1?"Just now":Ke<60?`${Ke}m ago`:xr<24?`${xr}h ago`:To<7?`${To}d ago`:xe.toLocaleDateString()};return U.default.createElement("div",{className:"flex h-full"},U.default.createElement("div",{className:"w-80 border-r border-gray-200 flex flex-col bg-white"},U.default.createElement("div",{className:"p-4 border-b border-gray-200"},U.default.createElement("div",{className:"flex items-center space-x-3 mb-4"},U.default.createElement("button",{onClick:t,className:"p-1 text-gray-400 hover:text-gray-600 transition-colors"},U.default.createElement("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},U.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M15 19l-7-7 7-7"}))),U.default.createElement("div",null,U.default.createElement("h2",{className:"font-semibold text-gray-900"},"Agent Interactions"),U.default.createElement("p",{className:"text-sm text-gray-500"},e.name))),U.default.createElement("div",{className:"mb-2"},U.default.createElement(Ba,{compact:!0})),U.default.createElement("div",{className:"flex bg-gray-100 rounded-lg p-1 mb-2 text-sm"},[{value:"",label:"All"},{value:"dashboard",label:"Dashboard"},{value:"reported",label:"Reported"}].map(K=>U.default.createElement("button",{key:K.value||"all",onClick:()=>{O(K.value),z(1)},className:`flex-1 px-2 py-1 rounded-md transition-colors ${C===K.value?"bg-white shadow text-gray-900":"text-gray-600"}`,title:K.value==="dashboard"?"Executions started from this dashboard":K.value==="reported"?"Executions reported by your app via telemetry":"Every execution, however it started"},K.label))),U.default.createElement("select",{value:Q,onChange:K=>{y(K.target.value),z(1)},className:"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-red-500"},U.default.createElement("option",{value:""},"All statuses"),U.default.createElement("option",{value:"complete"},"Completed"),U.default.createElement("option",{value:"failed"},"Failed"),U.default.createElement("option",{value:"running"},"Running"),U.default.createElement("option",{value:"cancelled"},"Cancelled")),U.default.createElement("select",{value:D,onChange:K=>{N(K.target.value),z(1)},className:"w-full mt-2 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-red-500",title:"Rank these executions"},U.default.createElement("option",{value:"recent"},"Most recent"),U.default.createElement("option",{value:"longest"},"Longest running"),U.default.createElement("option",{value:"cost"},"Highest cost"),U.default.createElement("option",{value:"tokens"},"Most tokens"))),U.default.createElement("div",{className:"flex-1 overflow-auto"},L&&n.length===0?U.default.createElement("div",{className:"flex items-center justify-center h-32"},U.default.createElement("div",{className:"animate-spin rounded-full h-6 w-6 border-b-2 border-red-500"})):n.length>0?U.default.createElement(U.default.Fragment,null,n.map(K=>{let xe=K.source==="reported";return U.default.createElement("div",{key:K.id,onClick:()=>pe(K),title:xe?"Reported by your app \u2014 opens its trace":"Started from this dashboard",className:`p-4 border-b border-gray-100 cursor-pointer transition-colors ${!xe&&l?.id===K.id?"bg-red-50 border-l-4 border-l-red-500":"hover:bg-gray-50"}`},U.default.createElement("div",{className:"flex items-center justify-between mb-2 gap-2"},U.default.createElement("span",{className:`px-2 py-0.5 rounded-full text-xs font-medium ${H(K.status)}`},K.status),U.default.createElement("div",{className:"flex items-center gap-2"},U.default.createElement("span",{className:`px-1.5 py-0.5 rounded text-[10px] uppercase tracking-wide ${xe?"bg-blue-100 text-blue-700":"bg-gray-100 text-gray-600"}`},xe?"reported":"dashboard"),U.default.createElement("span",{className:"text-xs text-gray-400"},ie(K.created_at||K.occurred_at)))),U.default.createElement("p",{className:"text-sm text-gray-700 truncate"},K.input_preview||K.input_prompt?.substring(0,60)||(xe?`${K.action_name||"execution"} \xB7 trace ${String(K.trace_id||"").slice(0,8)}`:"No input")),U.default.createElement("div",{className:"flex items-center space-x-3 mt-2 text-xs text-gray-400 flex-wrap gap-y-1"},K.model&&U.default.createElement("span",{className:"px-1.5 py-0.5 rounded bg-indigo-50 text-indigo-700 font-mono"},K.model),U.default.createElement("span",{className:D==="longest"?"text-gray-700 font-medium":void 0},q(K.duration_ms)),!!K.tokens&&U.default.createElement("span",{className:D==="tokens"?"text-gray-700 font-medium":void 0},K.tokens," tokens"),K.cost!=null&&U.default.createElement("span",{className:D==="cost"?"text-gray-700 font-medium":void 0,title:"Estimated from token counts at this model's published rates"},ee(K.cost))))}),X&&U.default.createElement("button",{onClick:()=>z(K=>K+1),disabled:L,className:"w-full py-3 text-sm text-red-600 hover:bg-gray-50 transition-colors"},L?"Loading...":"Load More")):U.default.createElement("div",{className:"p-8 text-center text-gray-400"},U.default.createElement("svg",{className:"w-12 h-12 mx-auto mb-3 text-gray-300",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},U.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:1.5,d:"M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"})),U.default.createElement("p",null,"No conversations yet")))),U.default.createElement("div",{className:"flex-1 flex flex-col bg-gray-50"},U.default.createElement("div",{className:"px-4 py-2 bg-white border-b border-gray-100 flex items-center gap-1.5 text-xs text-gray-500"},U.default.createElement("button",{onClick:t,className:"hover:text-gray-900 hover:underline transition-colors"},e.name),U.default.createElement("span",{className:"text-gray-300"},"/"),p==="all"||l?U.default.createElement(U.default.Fragment,null,U.default.createElement("button",{onClick:ge,className:"hover:text-gray-900 hover:underline transition-colors"},"Interactions"),U.default.createElement("span",{className:"text-gray-300"},"/"),p==="all"?u?U.default.createElement(U.default.Fragment,null,U.default.createElement("button",{onClick:()=>ze(null),className:"hover:text-gray-900 hover:underline transition-colors"},"Sessions"),U.default.createElement("span",{className:"text-gray-300"},"/"),U.default.createElement("span",{className:"text-gray-900 font-medium"},Ne)):U.default.createElement("span",{className:"text-gray-900 font-medium"},"Sessions"):U.default.createElement("span",{className:"text-gray-900 font-medium"},"Run #",l.id)):U.default.createElement("span",{className:"text-gray-900 font-medium"},"Interactions")),U.default.createElement("div",{className:"p-3 bg-white border-b border-gray-200 flex items-center justify-between"},U.default.createElement("div",{className:"flex bg-gray-100 rounded-lg p-1"},U.default.createElement("button",{onClick:()=>se("run"),className:`px-3 py-1 text-sm rounded-md transition-colors ${p==="run"?"bg-white shadow text-gray-900":"text-gray-600"}`},"Selected run"),U.default.createElement("button",{onClick:()=>se("all"),className:`px-3 py-1 text-sm rounded-md transition-colors ${p==="all"?"bg-white shadow text-gray-900":"text-gray-600"}`},"Sessions")),p==="run"&&l&&U.default.createElement("span",{className:`px-3 py-1 rounded-full text-sm font-medium ${H(l.status)}`},l.status)),p==="all"?U.default.createElement("div",{className:"flex-1 overflow-auto p-4 space-y-4"},(()=>{let{totals:K}=F();return U.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4 space-y-3"},U.default.createElement("div",{className:"flex items-center gap-3"},U.default.createElement(Tt,{size:40}),U.default.createElement("div",null,U.default.createElement("h3",{className:"font-semibold text-gray-900"},e.name),U.default.createElement("p",{className:"text-xs text-gray-500"},u?`${Ne} \u2014 one persisted interaction stream`:"Sessions group this agent\u2019s runs into persisted interaction streams"))),U.default.createElement("div",{className:"grid grid-cols-2 lg:grid-cols-4 gap-3"},U.default.createElement("div",{className:"bg-gray-50 rounded-lg p-3"},U.default.createElement("p",{className:"text-xs text-gray-500"},"Runs"),U.default.createElement("p",{className:"text-lg font-bold text-gray-900"},K.runs)),U.default.createElement("div",{className:"bg-gray-50 rounded-lg p-3"},U.default.createElement("p",{className:"text-xs text-gray-500"},"Success"),U.default.createElement("p",{className:"text-lg font-bold text-gray-900"},K.runs?Math.round(K.completed/K.runs*100):0,"%")),U.default.createElement("div",{className:"bg-gray-50 rounded-lg p-3"},U.default.createElement("p",{className:"text-xs text-gray-500"},"Avg Duration"),U.default.createElement("p",{className:"text-lg font-bold text-gray-900"},q(Math.round(K.avgDuration)))),U.default.createElement("div",{className:"bg-gray-50 rounded-lg p-3"},U.default.createElement("p",{className:"text-xs text-gray-500"},"Tokens"),U.default.createElement("p",{className:"text-lg font-bold text-gray-900"},K.tokens.toLocaleString()))))})(),U.default.createElement(Mi,{agentId:e.id,embedded:!0,selectedSessionId:u?.id??null,onSelectSession:ze})):l?U.default.createElement(U.default.Fragment,null,U.default.createElement("div",{className:"p-4 bg-white border-b border-gray-200"},U.default.createElement("div",{className:"flex items-center space-x-3"},U.default.createElement(Tt,{size:40}),U.default.createElement("div",null,U.default.createElement("h3",{className:"font-medium text-gray-900"},e.name," \u2014 Run #",l.id),U.default.createElement("p",{className:"text-xs text-gray-500"},new Date(l.created_at).toLocaleString())))),U.default.createElement("div",{ref:T,className:"flex-1 overflow-auto p-6"},U.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 shadow-sm p-4"},d.length>0?U.default.createElement(ki,{messages:d,darkMode:!1}):U.default.createElement(ki,{darkMode:!1,messages:[{id:`run-${l.id}-input`,role:"user",content:l.input_prompt||l.input_preview,created_at:l.created_at},{id:`run-${l.id}-output`,role:"assistant",content:l.error_message||l.output||l.output_preview||"No response",created_at:l.completed_at||l.created_at}]}))),U.default.createElement("div",{className:"p-4 bg-white border-t border-gray-200"},U.default.createElement("div",{className:"flex items-center justify-center space-x-8 text-sm text-gray-500"},U.default.createElement("span",null,"Duration: ",q(l.duration_ms)),l.total_tokens&&U.default.createElement("span",null,"Tokens: ",l.total_tokens),l.input_tokens&&l.output_tokens&&U.default.createElement("span",null,"Input: ",l.input_tokens," / Output: ",l.output_tokens),l.trace_id&&U.default.createElement("a",{href:`/dashboard/traces?trace=${l.trace_id}`,className:"font-mono text-xs text-blue-500 hover:underline",title:`${l.trace_id} \u2014 open in Traces`},"trace:",l.trace_id.slice(0,8)," \u2192")))):(()=>{let{cohorts:K,totals:xe}=F();return K.length===0?U.default.createElement("div",{className:"flex-1 flex items-center justify-center"},U.default.createElement("div",{className:"text-center text-gray-400"},U.default.createElement(Tt,{size:100}),U.default.createElement("p",{className:"mt-4"},"No runs yet \u2014 run the agent to build its report"))):U.default.createElement("div",{className:"flex-1 overflow-auto p-6 space-y-5"},U.default.createElement("div",{className:"flex items-center gap-3"},U.default.createElement(Tt,{size:40}),U.default.createElement("div",null,U.default.createElement("h3",{className:"font-semibold text-gray-900"},"Agent Report"),U.default.createElement("p",{className:"text-xs text-gray-500"},"Recent activity for ",e.name," \u2014 select a run for its full interaction"))),U.default.createElement("div",{className:"grid grid-cols-2 lg:grid-cols-4 gap-3"},U.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-3"},U.default.createElement("p",{className:"text-xs text-gray-500"},"Runs"),U.default.createElement("p",{className:"text-xl font-bold text-gray-900"},xe.runs)),U.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-3"},U.default.createElement("p",{className:"text-xs text-gray-500"},"Success"),U.default.createElement("p",{className:"text-xl font-bold text-gray-900"},xe.runs?Math.round(xe.completed/xe.runs*100):0,"%")),U.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-3"},U.default.createElement("p",{className:"text-xs text-gray-500"},"Avg Duration"),U.default.createElement("p",{className:"text-xl font-bold text-gray-900"},q(Math.round(xe.avgDuration)))),U.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-3"},U.default.createElement("p",{className:"text-xs text-gray-500"},"Tokens"),U.default.createElement("p",{className:"text-xl font-bold text-gray-900"},xe.tokens.toLocaleString()))),xe.models.length>0&&U.default.createElement("div",{className:"flex items-center gap-2 flex-wrap text-xs"},U.default.createElement("span",{className:"text-gray-500"},"Models used:"),xe.models.map(Ae=>U.default.createElement("span",{key:Ae,className:"px-1.5 py-0.5 rounded bg-indigo-50 text-indigo-700 font-mono"},Ae))),i.length>0&&U.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 overflow-hidden"},U.default.createElement("div",{className:"px-4 py-3 border-b border-gray-100"},U.default.createElement("span",{className:"text-xs uppercase tracking-wide text-gray-400"},"Sessions \u2014 grouped interaction streams")),U.default.createElement("div",{className:"divide-y divide-gray-100"},i.map(Ae=>U.default.createElement("div",{key:Ae.id,className:"px-4 py-3 hover:bg-gray-50 cursor-pointer flex items-center justify-between gap-3",onClick:()=>ze(Ae),title:"Open this session's interaction stream"},U.default.createElement("div",{className:"flex items-center gap-2 min-w-0",title:Ae.display_name},U.default.createElement("span",{className:"px-2 py-0.5 text-xs font-medium bg-blue-100 text-blue-700 rounded flex-shrink-0"},"SESSION"),U.default.createElement("span",{className:"text-sm text-gray-900 truncate"},Ae.agent?.name||Ae.agent_name),Ae.action_name&&U.default.createElement("span",{className:"px-1.5 py-0.5 rounded bg-purple-50 text-purple-700 font-mono text-xs flex-shrink-0"},"#",Ae.action_name)),U.default.createElement("div",{className:"flex items-center gap-3 text-xs text-gray-400 flex-shrink-0"},U.default.createElement("span",null,Ae.message_count," messages"),U.default.createElement("span",null,(Ae.tokens?.total||0).toLocaleString()," tokens"),U.default.createElement("span",null,ie(Ae.last_activity_at))))))),U.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 overflow-hidden"},U.default.createElement("div",{className:"px-4 py-3 border-b border-gray-100 flex items-center justify-between"},U.default.createElement("span",{className:"text-xs uppercase tracking-wide text-gray-400"},"Interactions by instructions \xD7 model"),U.default.createElement("select",{value:k,onChange:Ae=>_(Ae.target.value),className:"text-xs border border-gray-200 rounded px-2 py-1 text-gray-600"},U.default.createElement("option",{value:"recent"},"Most recent"),U.default.createElement("option",{value:"longest"},"Longest interactions"))),U.default.createElement("div",{className:"divide-y divide-gray-100"},K.map(Ae=>U.default.createElement("div",{key:Ae.key},U.default.createElement("div",{className:"px-4 py-3 hover:bg-gray-50 cursor-pointer",onClick:()=>E(we=>({...we,[Ae.key]:!we[Ae.key]})),title:"Show this configuration's runs"},U.default.createElement("div",{className:"flex items-center gap-2 flex-wrap"},U.default.createElement("span",{className:"px-1.5 py-0.5 rounded bg-indigo-50 text-indigo-700 font-mono text-xs"},Ae.model||"unknown model"),U.default.createElement("span",{className:"text-xs text-gray-400 font-mono",title:Ae.instructionsDigest?`sha:${Ae.instructionsDigest}`:void 0},"instructions"," ",Ae.instructionsDigest?[Ae.instructionsVersion,Ae.instructionsCodename||Ae.instructionsDigest].filter(Boolean).join(" \xB7 "):"n/a")),Ae.instructionsPreview&&U.default.createElement("p",{className:"text-xs text-gray-500 mt-1 truncate"},Ae.instructionsPreview),U.default.createElement("div",{className:"flex items-center gap-3 mt-1.5 text-xs text-gray-400 flex-wrap gap-y-1"},U.default.createElement("span",null,Ae.count," run",Ae.count>1?"s":""),U.default.createElement("span",{className:Ae.failed>0?"text-red-500":"text-green-600"},Ae.count?Math.round(Ae.completed/Ae.count*100):0,"% success"),U.default.createElement("span",null,"avg ",q(Math.round(Ae.avgDuration))),U.default.createElement("span",null,"avg ",Math.round(Ae.avgTokens).toLocaleString()," tokens"),U.default.createElement("span",null,"last ",ie(Ae.latest.created_at)))),M[Ae.key]&&U.default.createElement("div",{className:"bg-gray-50 divide-y divide-gray-100 border-t border-gray-100"},Ae.sortedRuns.map(we=>U.default.createElement("div",{key:we.id,className:"px-6 py-2 hover:bg-gray-100 cursor-pointer",onClick:()=>fe(we.id),title:"Open this run's interaction"},U.default.createElement("p",{className:"text-xs text-gray-700 truncate"},we.input_preview||"No input"),U.default.createElement("div",{className:"flex items-center gap-3 mt-0.5 text-[11px] text-gray-400"},U.default.createElement("span",{className:we.status==="failed"?"text-red-500":""},we.status),U.default.createElement("span",null,q(we.duration_ms)),we.tokens&&U.default.createElement("span",null,we.tokens.toLocaleString()," tokens"),U.default.createElement("span",null,ie(we.created_at)))))))))))})()))}var ke=te(ae());var JZ={productivity:"[+]",development:"<>",research:"[?]",creative:"[*]",data:"[#]",automation:"[>]"};function VS({onUseTemplate:e,onClose:t}){let[r,n]=(0,ke.useState)([]),[a,i]=(0,ke.useState)([]),[o,l]=(0,ke.useState)(""),[s,u]=(0,ke.useState)(null),[c,d]=(0,ke.useState)(!0),[f,p]=(0,ke.useState)(!1);(0,ke.useEffect)(()=>{v()},[o]);let v=async()=>{d(!0);try{let b=new URLSearchParams;o&&b.append("category",o);let g=await(await fetch(`/api/templates?${b}`)).json();n(g.templates),i(g.categories)}catch(b){console.error("Failed to load templates:",b)}finally{d(!1)}},h=async b=>{p(!0);try{let m=await fetch(`/api/templates/${b.id}/use`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:b.name})});if(m.ok){let g=await m.json();e(g.agent)}}catch(m){console.error("Failed to create agent from template:",m)}finally{p(!1)}};return ke.default.createElement("div",{className:"fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"},ke.default.createElement("div",{className:"bg-white rounded-2xl w-full max-w-5xl max-h-[90vh] flex flex-col overflow-hidden"},ke.default.createElement("div",{className:"px-6 py-4 border-b border-gray-200 flex items-center justify-between"},ke.default.createElement("div",null,ke.default.createElement("h2",{className:"text-xl font-semibold text-gray-900"},"Template Library"),ke.default.createElement("p",{className:"text-sm text-gray-500"},"Start with a pre-configured agent template")),ke.default.createElement("button",{onClick:t,className:"p-2 text-gray-400 hover:text-gray-600 transition-colors"},ke.default.createElement("svg",{className:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},ke.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})))),ke.default.createElement("div",{className:"flex-1 flex overflow-hidden"},ke.default.createElement("div",{className:"w-48 border-r border-gray-200 p-4 flex-shrink-0"},ke.default.createElement("h3",{className:"text-xs font-semibold text-gray-400 uppercase mb-3"},"Categories"),ke.default.createElement("div",{className:"space-y-1"},ke.default.createElement("button",{onClick:()=>l(""),className:`w-full text-left px-3 py-2 rounded-lg text-sm transition-colors ${o?"text-gray-600 hover:bg-gray-50":"bg-red-50 text-red-600"}`},"All Templates"),a.map(b=>ke.default.createElement("button",{key:b,onClick:()=>l(b),className:`w-full text-left px-3 py-2 rounded-lg text-sm flex items-center space-x-2 transition-colors ${o===b?"bg-red-50 text-red-600":"text-gray-600 hover:bg-gray-50"}`},ke.default.createElement("span",null,JZ[b]||"\u{1F4E6}"),ke.default.createElement("span",{className:"capitalize"},b))))),ke.default.createElement("div",{className:"flex-1 p-6 overflow-auto"},c?ke.default.createElement("div",{className:"flex items-center justify-center h-full"},ke.default.createElement("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-red-500"})):r.length>0?ke.default.createElement("div",{className:"grid grid-cols-2 lg:grid-cols-3 gap-4"},r.map(b=>ke.default.createElement("div",{key:b.id,onClick:()=>u(b),className:`bg-white rounded-xl border-2 p-4 cursor-pointer transition-all hover:shadow-lg ${s?.id===b.id?"border-red-500 ring-2 ring-red-100":"border-gray-200 hover:border-gray-300"}`},ke.default.createElement("div",{className:"flex items-start space-x-3"},ke.default.createElement("div",{className:"flex-shrink-0"},ke.default.createElement(Tt,{size:60})),ke.default.createElement("div",{className:"flex-1 min-w-0"},ke.default.createElement("div",{className:"flex items-center space-x-2"},ke.default.createElement("span",{className:"text-lg"},b.icon),ke.default.createElement("h3",{className:"font-medium text-gray-900 truncate"},b.name)),ke.default.createElement("p",{className:"text-xs text-gray-500 mt-1 line-clamp-2"},b.description),ke.default.createElement("div",{className:"flex items-center space-x-2 mt-2"},ke.default.createElement("span",{className:"text-xs px-2 py-0.5 bg-gray-100 rounded capitalize"},b.category),b.featured&&ke.default.createElement("span",{className:"text-xs px-2 py-0.5 bg-yellow-100 text-yellow-700 rounded"},"Featured"))))))):ke.default.createElement("div",{className:"text-center py-12 text-gray-500"},"No templates found in this category")),s&&ke.default.createElement("div",{className:"w-80 border-l border-gray-200 p-6 flex-shrink-0 bg-gray-50 overflow-auto"},ke.default.createElement("div",{className:"flex justify-center mb-4"},ke.default.createElement(Tt,{size:120})),ke.default.createElement("h3",{className:"text-lg font-semibold text-gray-900 text-center"},s.name),ke.default.createElement("p",{className:"text-sm text-gray-500 text-center mt-1"},s.description),ke.default.createElement("div",{className:"mt-6 space-y-4"},ke.default.createElement("div",null,ke.default.createElement("span",{className:"text-xs font-medium text-gray-500 uppercase"},"Provider / Model"),ke.default.createElement("p",{className:"text-sm text-gray-900"},s.provider," / ",s.model)),s.tools?.length>0&&ke.default.createElement("div",null,ke.default.createElement("span",{className:"text-xs font-medium text-gray-500 uppercase"},"Tools"),ke.default.createElement("div",{className:"flex flex-wrap gap-1 mt-1"},s.tools.map(b=>ke.default.createElement("span",{key:b,className:"text-xs px-2 py-0.5 bg-red-100 text-red-700 rounded"},b)))),ke.default.createElement("div",null,ke.default.createElement("span",{className:"text-xs font-medium text-gray-500 uppercase"},"Usage"),ke.default.createElement("p",{className:"text-sm text-gray-900"},s.usage_count," agents created"))),ke.default.createElement("button",{onClick:()=>h(s),disabled:f,className:"w-full mt-6 px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors disabled:opacity-50"},f?"Creating...":"Use This Template")))))}var Ce=te(ae());function FS({currentView:e,onNavigate:t,agentCount:r,account:n,user:a,gemVersion:i}){let{darkMode:o}=pt(),[l,s]=(0,Ce.useState)(!1),u=(0,Ce.useRef)(null);(0,Ce.useEffect)(()=>{function b(m){u.current&&!u.current.contains(m.target)&&s(!1)}return document.addEventListener("mousedown",b),()=>document.removeEventListener("mousedown",b)},[]);let c=n?.name||"My Workspace",d=a?.name||a?.email?.split("@")[0]||"User",f=[{id:"list",label:"Agents",icon:or.nav.agents,badge:r},{id:"builder",label:"New Agent",icon:or.nav.newAgent},{id:"sandbox",label:"Run Agents",icon:or.nav.demo,highlight:!0}],p=[{id:"traces",label:"Traces",icon:or.nav.traces},{id:"interactions",label:"Interactions",icon:or.nav.interactions},{id:"metrics",label:"Metrics",icon:or.nav.metrics},{id:"evaluations",label:"Evaluations",icon:or.nav.evaluations},{id:"replay",label:"Session Replay",icon:or.nav.replay}],v=[{id:"organization",label:"Organization",icon:"\u{1F3E2}"},{id:"settings",label:"Settings",icon:"\u2699\uFE0F"}],h=({item:b})=>Ce.default.createElement("button",{onClick:()=>t(b.id),className:`w-full flex items-center justify-between py-3 rounded-lg text-left transition-colors ${b.indent?"pl-10 pr-4":"px-4"}`,style:{backgroundColor:e===b.id?o?"rgba(239, 68, 68, 0.15)":"#fef2f2":"transparent",color:e===b.id?"#ef4444":o?"#d1d5db":"#374151"},onMouseEnter:m=>{e!==b.id&&(m.currentTarget.style.backgroundColor=o?"#252525":"#f3f4f6")},onMouseLeave:m=>{e!==b.id&&(m.currentTarget.style.backgroundColor="transparent")}},Ce.default.createElement("div",{className:"flex items-center space-x-3"},Ce.default.createElement("span",{style:{fontFamily:Oe.mono,fontSize:"12px",width:"20px",textAlign:"center"}},b.icon),Ce.default.createElement("span",{className:"font-medium"},b.label)),b.badge!==void 0&&Ce.default.createElement("span",{className:"px-2 py-0.5 text-xs rounded-full",style:{backgroundColor:e===b.id?"#fee2e2":o?"#2a2a2a":"#f3f4f6",color:e===b.id?"#ef4444":o?"#d1d5db":"#4b5563"}},b.badge));return Ce.default.createElement("aside",{className:"w-64 border-r flex flex-col",style:{backgroundColor:o?"#1a1a1a":"#ffffff",borderColor:o?"#2a2a2a":"#e5e7eb"}},Ce.default.createElement("div",{className:"h-16 flex items-center px-4 border-b relative",style:{borderColor:o?"#2a2a2a":"#e5e7eb"},ref:u},Ce.default.createElement("button",{onClick:()=>s(!l),className:"w-full flex items-center justify-between px-2 py-2 rounded-lg transition-colors",style:{backgroundColor:l?o?"#252525":"#f3f4f6":"transparent"}},Ce.default.createElement("div",{className:"flex items-center space-x-3 min-w-0"},Ce.default.createElement(Tt,{size:28}),Ce.default.createElement("span",{className:`font-semibold truncate ${o?"text-white":"text-gray-900"}`},c)),Ce.default.createElement("svg",{className:`w-4 h-4 flex-shrink-0 transition-transform ${l?"rotate-180":""}`,style:{color:o?"#9ca3af":"#6b7280"},fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},Ce.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"}))),l&&Ce.default.createElement("div",{className:"absolute left-4 right-4 top-14 rounded-lg shadow-lg border overflow-hidden z-50",style:{backgroundColor:o?"#1a1a1a":"#ffffff",borderColor:o?"#2a2a2a":"#e5e7eb"}},Ce.default.createElement("div",{className:"px-4 py-3 border-b",style:{borderColor:o?"#2a2a2a":"#e5e7eb"}},Ce.default.createElement("div",{className:"flex items-center space-x-3"},Ce.default.createElement(Tt,{size:32}),Ce.default.createElement("div",{className:"min-w-0"},Ce.default.createElement("div",{className:`font-medium truncate ${o?"text-white":"text-gray-900"}`},c)))),Ce.default.createElement("div",{className:"py-1"},Ce.default.createElement("button",{onClick:()=>{t("settings"),s(!1)},className:`w-full flex items-center space-x-3 px-4 py-2 text-left text-sm transition-colors ${o?"text-gray-300 hover:bg-gray-800":"text-gray-700 hover:bg-gray-100"}`},Ce.default.createElement("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},Ce.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"}),Ce.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M15 12a3 3 0 11-6 0 3 3 0 016 0z"})),Ce.default.createElement("span",null,"Settings")),Ce.default.createElement("button",{onClick:()=>{t("organization"),s(!1)},className:`w-full flex items-center space-x-3 px-4 py-2 text-left text-sm transition-colors ${o?"text-gray-300 hover:bg-gray-800":"text-gray-700 hover:bg-gray-100"}`},Ce.default.createElement("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},Ce.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"})),Ce.default.createElement("span",null,"Organization"))),Ce.default.createElement("div",{className:"border-t py-1",style:{borderColor:o?"#2a2a2a":"#e5e7eb"}},Ce.default.createElement("div",{className:`px-4 py-2 flex items-center space-x-3 ${o?"text-gray-300":"text-gray-700"}`},Ce.default.createElement("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},Ce.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"})),Ce.default.createElement("span",{className:"text-sm"},d))))),Ce.default.createElement("nav",{className:"flex-1 px-4 py-4 space-y-1 overflow-y-auto"},Ce.default.createElement("div",{className:"space-y-1"},f.map(b=>Ce.default.createElement(h,{key:b.id,item:b}))),Ce.default.createElement("div",{className:"pt-4"},Ce.default.createElement("div",{className:"px-4 pb-2"},Ce.default.createElement("span",{className:`text-xs font-semibold uppercase tracking-wide ${o?"text-gray-500":"text-gray-400"}`},"Observability")),Ce.default.createElement("div",{className:"space-y-1"},p.map(b=>Ce.default.createElement(h,{key:b.id,item:b})))),Ce.default.createElement("div",{className:"pt-4"},Ce.default.createElement("div",{className:"px-4 pb-2"},Ce.default.createElement("span",{className:`text-xs font-semibold uppercase tracking-wide ${o?"text-gray-500":"text-gray-400"}`},"Workspace")),Ce.default.createElement("div",{className:"space-y-1"},v.map(b=>Ce.default.createElement(h,{key:b.id,item:b}))))),Ce.default.createElement("div",{className:"px-4 py-4 border-t",style:{borderColor:o?"#2a2a2a":"#e5e7eb"}},Ce.default.createElement("div",{className:`text-xs font-semibold uppercase mb-3 ${o?"text-gray-500":"text-gray-400"}`},"Resources"),Ce.default.createElement("div",{className:"space-y-2"},Ce.default.createElement("a",{href:"https://docs.activeagents.ai",target:"_blank",rel:"noopener noreferrer",className:`flex items-center space-x-2 text-sm transition-colors ${o?"text-gray-400 hover:text-red-400":"text-gray-600 hover:text-red-600"}`},Ce.default.createElement("span",{style:{fontFamily:Oe.mono,fontSize:"12px",width:"20px",textAlign:"center"}},or.nav.docs),Ce.default.createElement("span",null,"Documentation")),Ce.default.createElement("a",{href:"https://github.com/activeagents/activeagent",target:"_blank",rel:"noopener noreferrer",className:`flex items-center space-x-2 text-sm transition-colors ${o?"text-gray-400 hover:text-red-400":"text-gray-600 hover:text-red-600"}`},Ce.default.createElement("span",{style:{fontFamily:Oe.mono,fontSize:"12px",width:"20px",textAlign:"center"}},or.nav.github),Ce.default.createElement("span",null,"GitHub")))),Ce.default.createElement("div",{className:"px-6 py-4 border-t",style:{borderColor:o?"#2a2a2a":"#e5e7eb"}},Ce.default.createElement("div",{className:`text-xs ${o?"text-gray-500":"text-gray-400"}`},"Active Agent ",i?`v${i}`:"")))}var Qe=te(ae());function qS({user:e,account:t}){let{darkMode:r,toggleDarkMode:n}=pt(),[a,i]=(0,Qe.useState)(!1),o=(0,Qe.useRef)(null);(0,Qe.useEffect)(()=>{function c(d){o.current&&!o.current.contains(d.target)&&i(!1)}return document.addEventListener("mousedown",c),()=>document.removeEventListener("mousedown",c)},[]);let l=()=>{let c=document.querySelector('meta[name="csrf-token"]')?.content,d=document.createElement("form");d.method="POST",d.action="/session";let f=document.createElement("input");f.type="hidden",f.name="_method",f.value="delete",d.appendChild(f);let p=document.createElement("input");p.type="hidden",p.name="authenticity_token",p.value=c,d.appendChild(p),document.body.appendChild(d),d.submit()},s=e?.name||e?.email?.split("@")[0]||"User",u=s.charAt(0).toUpperCase();return Qe.default.createElement("header",{className:"h-16 border-b flex items-center justify-between px-6",style:{backgroundColor:r?"#1a1a1a":"#ffffff",borderColor:r?"#2a2a2a":"#e5e7eb"}},Qe.default.createElement("div",{className:"flex items-center space-x-4"}),Qe.default.createElement("div",{className:"flex items-center space-x-4"},Qe.default.createElement("button",{onClick:n,className:"p-2 rounded-lg transition-colors",style:{backgroundColor:r?"#252525":"#f3f4f6",color:r?"#fbbf24":"#4b5563"},title:r?"Switch to light mode":"Switch to dark mode"},r?Qe.default.createElement("svg",{className:"w-5 h-5",fill:"currentColor",viewBox:"0 0 20 20"},Qe.default.createElement("path",{fillRule:"evenodd",d:"M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z",clipRule:"evenodd"})):Qe.default.createElement("svg",{className:"w-5 h-5",fill:"currentColor",viewBox:"0 0 20 20"},Qe.default.createElement("path",{d:"M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"}))),Qe.default.createElement("div",{className:`flex items-center space-x-2 text-sm ${r?"text-gray-400":"text-gray-500"}`},Qe.default.createElement("span",{className:"w-2 h-2 bg-green-400 rounded-full"}),Qe.default.createElement("span",null,"Connected")),Qe.default.createElement("div",{className:"relative",ref:o},Qe.default.createElement("button",{onClick:()=>i(!a),className:"flex items-center space-x-2 p-1 rounded-lg transition-colors",style:{backgroundColor:a?r?"#252525":"#f3f4f6":"transparent"}},Qe.default.createElement("div",{className:"w-8 h-8 bg-red-100 rounded-full flex items-center justify-center"},Qe.default.createElement("span",{className:"text-red-600 font-medium text-sm"},u))),a&&Qe.default.createElement("div",{className:"absolute right-0 mt-2 w-64 rounded-lg shadow-lg border overflow-hidden z-50",style:{backgroundColor:r?"#1a1a1a":"#ffffff",borderColor:r?"#2a2a2a":"#e5e7eb"}},Qe.default.createElement("div",{className:"px-4 py-3 border-b",style:{borderColor:r?"#2a2a2a":"#e5e7eb"}},Qe.default.createElement("div",{className:"flex items-center space-x-3"},Qe.default.createElement("div",{className:"w-10 h-10 bg-red-100 rounded-full flex items-center justify-center"},Qe.default.createElement("span",{className:"text-red-600 font-medium"},u)),Qe.default.createElement("div",null,Qe.default.createElement("div",{className:`font-medium ${r?"text-white":"text-gray-900"}`},s),Qe.default.createElement("div",{className:`text-sm ${r?"text-gray-400":"text-gray-500"}`},e?.email)))),Qe.default.createElement("div",{className:"py-1"},Qe.default.createElement("button",{onClick:l,className:`w-full flex items-center space-x-3 px-4 py-2 text-left text-sm transition-colors ${r?"text-gray-300 hover:bg-gray-800":"text-gray-700 hover:bg-gray-100"}`},Qe.default.createElement("svg",{className:"w-4 h-4",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},Qe.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"})),Qe.default.createElement("span",null,"Sign out")))))))}var be=te(ae());var RZ=6e4,ij=e=>{if(!e||e.length<2)return"";let t=Math.max(...e,1),r=100/(e.length-1);return e.map((n,a)=>`${(a*r).toFixed(1)},${(28-n/t*26+1).toFixed(1)}`).join(" ")},YS=[{value:"popular",label:"Requests",column:"requests"},{value:"tokens",label:"Tokens",column:"tokens"},{value:"cost",label:"Cost",column:"cost"},{value:"longest",label:"Avg Duration",column:"avg_duration_ms"},{value:"errors",label:"Errors",column:"errors"}];function WS(){let{darkMode:e}=pt(),[t,r]=(0,be.useState)(null),[n,a]=(0,be.useState)(!0),[i,o]=(0,be.useState)(null),[l,s]=(0,be.useState)("popular"),u=(0,be.useCallback)(async()=>{try{let E=await fetch(`/api/metrics?hours=24&sort=${l}`);if(!E.ok)throw new Error(`Request failed (${E.status})`);let L=await E.json();r(L),o(null)}catch(E){o(E.message)}finally{a(!1)}},[l]);if((0,be.useEffect)(()=>{u();let E=setInterval(u,RZ);return()=>clearInterval(E)},[u]),n)return be.default.createElement("div",{className:"flex items-center justify-center h-64"},be.default.createElement("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-red-500"}));let c=E=>E==null?"0":E>=1e6?(E/1e6).toFixed(1)+"M":E>=1e3?(E/1e3).toFixed(1)+"K":E.toString(),d=E=>E?E<.01?"<$0.01":`$${E.toFixed(2)}`:"$0.00",f={bg:e?"transparent":"#f9fafb",cardBg:e?"rgba(255,255,255,0.05)":"#ffffff",border:e?"rgba(255,255,255,0.1)":"#e5e7eb",borderLight:e?"rgba(255,255,255,0.05)":"#f3f4f6",textPrimary:e?"#ffffff":"#111827",textSecondary:e?"rgba(255,255,255,0.6)":"#6b7280",textMuted:e?"rgba(255,255,255,0.4)":"#9ca3af",textCell:e?"rgba(255,255,255,0.7)":"#4b5563",trendUp:"#16a34a",trendDown:"#dc2626",tokenIn:"#2563eb",tokenOut:"#7c3aed",tokenThinking:"#d97706",badgeBg:e?"rgba(239, 68, 68, 0.2)":"#fef2f2",badgeText:"#ef4444"};if(i||!t)return be.default.createElement("div",{style:{padding:"24px"}},be.default.createElement("h1",{style:{fontSize:"24px",fontWeight:"bold",color:f.textPrimary,margin:0}},"Metrics"),be.default.createElement("div",{style:{marginTop:"16px",padding:"12px 16px",background:f.badgeBg,borderRadius:"8px",color:f.badgeText,fontSize:"14px"}},"Failed to load metrics",i?`: ${i}`:""));let{summary:p,hourly_requests:v=[],by_agent:h=[]}=t,b=v.map(E=>E.count),m=v.map(E=>E.avg_latency_ms),g=Math.max(...b,1),x=p.total_requests===0,A=(E,L=!1)=>{if(E==null)return null;let I=E>=0;return be.default.createElement("div",{style:{fontSize:"13px",color:(L?!I:I)?f.trendUp:f.trendDown,marginTop:"8px"}},I?"\u2191":"\u2193"," ",Math.abs(E),"% vs previous 24h")},k=YS.find(E=>E.value===l)?.column,_=E=>({padding:"12px 0",textAlign:"right",color:f.textCell,fontWeight:E===k?"600":"400"}),M=({label:E,children:L,trend:I,sparkline:j,sparklineColor:z})=>be.default.createElement("div",{style:{background:f.cardBg,borderRadius:"12px",padding:"20px",border:`1px solid ${f.border}`}},be.default.createElement("div",{style:{fontSize:"11px",color:f.textSecondary,textTransform:"uppercase",letterSpacing:"0.05em",marginBottom:"8px"}},E),L,I,j&&be.default.createElement("div",{style:{marginTop:"16px",height:"40px"}},be.default.createElement("svg",{viewBox:"0 0 100 30",style:{width:"100%",height:"100%"},preserveAspectRatio:"none"},be.default.createElement("polyline",{points:j,fill:"none",stroke:z,strokeWidth:"2"}))));return be.default.createElement("div",{style:{borderRadius:"12px",overflow:"hidden",minHeight:"calc(100vh - 200px)",backgroundColor:f.bg}},be.default.createElement("div",{style:{padding:"24px 24px 0 24px",borderBottom:`1px solid ${f.border}`,marginBottom:"16px",paddingBottom:"16px"}},be.default.createElement("h1",{style:{fontSize:"24px",fontWeight:"bold",color:f.textPrimary,margin:0}},"Metrics"),be.default.createElement("p",{style:{fontSize:"14px",color:f.textSecondary,marginTop:"4px"}},"Requests, latency, errors and token usage \u2014 last 24 hours")),be.default.createElement("div",{style:{padding:"0 24px 24px 24px"}},be.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit, minmax(220px, 1fr))",gap:"16px",marginBottom:"16px"}},be.default.createElement(M,{label:"Total Requests",trend:A(p.requests_change),sparkline:ij(b),sparklineColor:"#ef4444"},be.default.createElement("div",{style:{fontSize:"32px",fontWeight:"bold",color:f.textPrimary,fontFamily:"monospace"}},c(p.total_requests))),be.default.createElement(M,{label:"Avg Latency",trend:A(p.latency_change,!0),sparkline:ij(m),sparklineColor:"#22c55e"},be.default.createElement("div",{style:{fontSize:"32px",fontWeight:"bold",color:f.textPrimary,fontFamily:"monospace"}},p.avg_latency_ms,be.default.createElement("span",{style:{fontSize:"14px",color:f.textMuted,marginLeft:"4px"}},"ms"))),be.default.createElement(M,{label:"Error Rate"},be.default.createElement("div",{style:{fontSize:"32px",fontWeight:"bold",color:p.error_rate>5?"#ef4444":f.textPrimary,fontFamily:"monospace"}},p.error_rate,be.default.createElement("span",{style:{fontSize:"14px",color:f.textMuted,marginLeft:"4px"}},"%")),be.default.createElement("div",{style:{fontSize:"13px",color:f.textSecondary,marginTop:"8px"}},p.errors," error",p.errors===1?"":"s"," in period")),be.default.createElement(M,{label:"Total Cost"},be.default.createElement("div",{style:{fontSize:"32px",fontWeight:"bold",color:f.textPrimary,fontFamily:"monospace"}},d(p.total_cost)),be.default.createElement("div",{style:{fontSize:"13px",color:f.textSecondary,marginTop:"8px"}},"estimated, this period")),be.default.createElement(M,{label:"Tokens Used"},be.default.createElement("div",{style:{fontSize:"32px",fontWeight:"bold",color:f.textPrimary,fontFamily:"monospace"}},c(p.tokens_used)),be.default.createElement("div",{style:{fontSize:"13px",marginTop:"8px",display:"flex",gap:"12px",flexWrap:"wrap"}},be.default.createElement("span",{style:{color:f.tokenIn}},"\u2193 ",c(p.tokens_input)),be.default.createElement("span",{style:{color:f.tokenOut}},"\u2191 ",c(p.tokens_output)),p.tokens_thinking>0&&be.default.createElement("span",{style:{color:f.tokenThinking}},"\u{1F9E0} ",c(p.tokens_thinking)))),be.default.createElement(M,{label:"Active Agents"},be.default.createElement("div",{style:{fontSize:"32px",fontWeight:"bold",color:f.textPrimary,fontFamily:"monospace"}},p.unique_agents),be.default.createElement("div",{style:{fontSize:"13px",color:f.textSecondary,marginTop:"8px"}},"with traffic in period"))),be.default.createElement("div",{style:{background:f.cardBg,borderRadius:"12px",padding:"20px",border:`1px solid ${f.border}`,marginBottom:"16px"}},be.default.createElement("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"16px"}},be.default.createElement("span",{style:{fontSize:"14px",fontWeight:"600",color:f.textPrimary}},"Requests / Hour"),be.default.createElement("span",{style:{fontSize:"13px",color:f.textSecondary}},"Last 24h")),be.default.createElement("div",{style:{display:"flex",alignItems:"flex-end",justifyContent:"space-between",height:"120px",gap:"4px"}},v.map((E,L)=>be.default.createElement("div",{key:L,style:{flex:1,height:`${Math.max(E.count/g*100,2)}%`,background:E.active?"linear-gradient(180deg, #ef4444 0%, #dc2626 100%)":"linear-gradient(180deg, #fca5a5 0%, #f87171 100%)",opacity:E.count===0&&!E.active?.25:1,borderRadius:"4px 4px 0 0",transition:"all 0.2s ease"},title:`${E.count} requests at ${E.hour}`}))),be.default.createElement("div",{style:{display:"flex",justifyContent:"space-between",marginTop:"8px"}},v.map((E,L)=>be.default.createElement("span",{key:L,style:{flex:1,textAlign:"center",fontSize:"10px",color:f.textMuted}},L%4===0||E.active?E.active?"Now":E.hour:"")))),be.default.createElement("div",{style:{background:f.cardBg,borderRadius:"12px",padding:"20px",border:`1px solid ${f.border}`}},be.default.createElement("div",{style:{display:"flex",alignItems:"baseline",justifyContent:"space-between",marginBottom:"16px",gap:"12px",flexWrap:"wrap"}},be.default.createElement("h3",{style:{fontSize:"16px",fontWeight:"600",color:f.textPrimary,margin:0}},"Agent Statistics"),be.default.createElement("span",{style:{fontSize:"12px",color:f.textMuted}},"Ranked by ",YS.find(E=>E.value===l)?.label.toLowerCase()," \u2014 click a column to change")),h.length===0?be.default.createElement("div",{style:{textAlign:"center",padding:"24px 0",color:f.textSecondary,fontSize:"14px"}},x?"No traffic yet \u2014 run an agent, or point your app's ActiveAgent telemetry at this workspace":"No per-agent data for this period"):be.default.createElement("table",{style:{width:"100%",borderCollapse:"collapse"}},be.default.createElement("thead",null,be.default.createElement("tr",{style:{textAlign:"left",fontSize:"13px",color:f.textSecondary,borderBottom:`1px solid ${f.border}`}},be.default.createElement("th",{style:{paddingBottom:"12px",fontWeight:"500"}},"Agent"),YS.map(E=>{let L=l===E.value;return be.default.createElement("th",{key:E.value,style:{paddingBottom:"12px",fontWeight:"500",textAlign:"right"}},be.default.createElement("button",{onClick:()=>s(E.value),title:`Rank agents by ${E.label.toLowerCase()}`,style:{background:"none",border:"none",padding:0,cursor:"pointer",font:"inherit",fontWeight:L?"600":"500",color:L?f.textPrimary:f.textSecondary}},E.label,L?" \u2193":""))}))),be.default.createElement("tbody",null,h.map((E,L)=>be.default.createElement("tr",{key:E.name,style:{borderBottom:L0?f.badgeText:f.textCell}},E.errors))))))))}var B=te(ae());var Oo=te(ae());var Ag={logger:typeof console<"u"?console:void 0,WebSocket:typeof WebSocket<"u"?WebSocket:void 0},Lt={log(...e){this.enabled&&(e.push(Date.now()),Ag.logger.log("[ActionCable]",...e))}},Wf=()=>new Date().getTime(),Sg=e=>(Wf()-e)/1e3,Kf=class{constructor(t){this.visibilityDidChange=this.visibilityDidChange.bind(this),this.connection=t,this.reconnectAttempts=0}start(){this.isRunning()||(this.startedAt=Wf(),delete this.stoppedAt,this.startPolling(),addEventListener("visibilitychange",this.visibilityDidChange),Lt.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`))}stop(){this.isRunning()&&(this.stoppedAt=Wf(),this.stopPolling(),removeEventListener("visibilitychange",this.visibilityDidChange),Lt.log("ConnectionMonitor stopped"))}isRunning(){return this.startedAt&&!this.stoppedAt}recordMessage(){this.pingedAt=Wf()}recordConnect(){this.reconnectAttempts=0,delete this.disconnectedAt,Lt.log("ConnectionMonitor recorded connect")}recordDisconnect(){this.disconnectedAt=Wf(),Lt.log("ConnectionMonitor recorded disconnect")}startPolling(){this.stopPolling(),this.poll()}stopPolling(){clearTimeout(this.pollTimeout)}poll(){this.pollTimeout=setTimeout((()=>{this.reconnectIfStale(),this.poll()}),this.getPollInterval())}getPollInterval(){let{staleThreshold:t,reconnectionBackoffRate:r}=this.constructor,n=Math.pow(1+r,Math.min(this.reconnectAttempts,10)),i=(this.reconnectAttempts===0?1:r)*Math.random();return t*1e3*n*(1+i)}reconnectIfStale(){this.connectionIsStale()&&(Lt.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${Sg(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`),this.reconnectAttempts++,this.disconnectedRecently()?Lt.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${Sg(this.disconnectedAt)} s`):(Lt.log("ConnectionMonitor reopening"),this.connection.reopen()))}get refreshedAt(){return this.pingedAt?this.pingedAt:this.startedAt}connectionIsStale(){return Sg(this.refreshedAt)>this.constructor.staleThreshold}disconnectedRecently(){return this.disconnectedAt&&Sg(this.disconnectedAt){(this.connectionIsStale()||!this.connection.isOpen())&&(Lt.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`),this.connection.reopen())}),200)}};Kf.staleThreshold=6;Kf.reconnectionBackoffRate=.15;var lj={message_types:{welcome:"welcome",disconnect:"disconnect",ping:"ping",confirmation:"confirm_subscription",rejection:"reject_subscription"},disconnect_reasons:{unauthorized:"unauthorized",invalid_request:"invalid_request",server_restart:"server_restart",remote:"remote"},default_mount_path:"/cable",protocols:["actioncable-v1-json","actioncable-unsupported"]},{message_types:Yf,protocols:KS}=lj,eQ=KS.slice(0,KS.length-1),oj=[].indexOf,Gf=class{constructor(t){this.open=this.open.bind(this),this.consumer=t,this.subscriptions=this.consumer.subscriptions,this.monitor=new Kf(this),this.disconnected=!0}send(t){return this.isOpen()?(this.webSocket.send(JSON.stringify(t)),!0):!1}open(){if(this.isActive())return Lt.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`),!1;{let t=[...KS,...this.consumer.subprotocols||[]];return Lt.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${t}`),this.webSocket&&this.uninstallEventHandlers(),this.webSocket=new Ag.WebSocket(this.consumer.url,t),this.installEventHandlers(),this.monitor.start(),!0}}close({allowReconnect:t}={allowReconnect:!0}){if(t||this.monitor.stop(),this.isOpen())return this.webSocket.close()}reopen(){if(Lt.log(`Reopening WebSocket, current state is ${this.getState()}`),this.isActive())try{return this.close()}catch(t){Lt.log("Failed to reopen WebSocket",t)}finally{Lt.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`),setTimeout(this.open,this.constructor.reopenDelay)}else return this.open()}getProtocol(){if(this.webSocket)return this.webSocket.protocol}isOpen(){return this.isState("open")}isActive(){return this.isState("open","connecting")}triedToReconnect(){return this.monitor.reconnectAttempts>0}isProtocolSupported(){return oj.call(eQ,this.getProtocol())>=0}isState(...t){return oj.call(t,this.getState())>=0}getState(){if(this.webSocket){for(let t in Ag.WebSocket)if(Ag.WebSocket[t]===this.webSocket.readyState)return t.toLowerCase()}return null}installEventHandlers(){for(let t in this.events){let r=this.events[t].bind(this);this.webSocket[`on${t}`]=r}}uninstallEventHandlers(){for(let t in this.events)this.webSocket[`on${t}`]=function(){}}};Gf.reopenDelay=500;Gf.prototype.events={message(e){if(!this.isProtocolSupported())return;let{identifier:t,message:r,reason:n,reconnect:a,type:i}=JSON.parse(e.data);switch(this.monitor.recordMessage(),i){case Yf.welcome:return this.triedToReconnect()&&(this.reconnectAttempted=!0),this.monitor.recordConnect(),this.subscriptions.reload();case Yf.disconnect:return Lt.log(`Disconnecting. Reason: ${n}`),this.close({allowReconnect:a});case Yf.ping:return null;case Yf.confirmation:return this.subscriptions.confirmSubscription(t),this.reconnectAttempted?(this.reconnectAttempted=!1,this.subscriptions.notify(t,"connected",{reconnected:!0})):this.subscriptions.notify(t,"connected",{reconnected:!1});case Yf.rejection:return this.subscriptions.reject(t);default:return this.subscriptions.notify(t,"received",r)}},open(){if(Lt.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`),this.disconnected=!1,!this.isProtocolSupported())return Lt.log("Protocol is unsupported. Stopping monitor and disconnecting."),this.close({allowReconnect:!1})},close(e){if(Lt.log("WebSocket onclose event"),!this.disconnected)return this.disconnected=!0,this.monitor.recordDisconnect(),this.subscriptions.notifyAll("disconnected",{willAttemptReconnect:this.monitor.isRunning()})},error(){Lt.log("WebSocket onerror event")}};var tQ=function(e,t){if(t!=null)for(let r in t){let n=t[r];e[r]=n}return e},GS=class{constructor(t,r={},n){this.consumer=t,this.identifier=JSON.stringify(r),tQ(this,n)}perform(t,r={}){return r.action=t,this.send(r)}send(t){return this.consumer.send({command:"message",identifier:this.identifier,data:JSON.stringify(t)})}unsubscribe(){return this.consumer.subscriptions.remove(this)}},XS=class{constructor(t){this.subscriptions=t,this.pendingSubscriptions=[]}guarantee(t){this.pendingSubscriptions.indexOf(t)==-1?(Lt.log(`SubscriptionGuarantor guaranteeing ${t.identifier}`),this.pendingSubscriptions.push(t)):Lt.log(`SubscriptionGuarantor already guaranteeing ${t.identifier}`),this.startGuaranteeing()}forget(t){Lt.log(`SubscriptionGuarantor forgetting ${t.identifier}`),this.pendingSubscriptions=this.pendingSubscriptions.filter((r=>r!==t))}startGuaranteeing(){this.stopGuaranteeing(),this.retrySubscribing()}stopGuaranteeing(){clearTimeout(this.retryTimeout)}retrySubscribing(){this.retryTimeout=setTimeout((()=>{this.subscriptions&&typeof this.subscriptions.subscribe=="function"&&this.pendingSubscriptions.map((t=>{Lt.log(`SubscriptionGuarantor resubscribing ${t.identifier}`),this.subscriptions.subscribe(t)}))}),500)}},ZS=class{constructor(t){this.consumer=t,this.guarantor=new XS(this),this.subscriptions=[]}create(t,r){let n=t,a=typeof n=="object"?n:{channel:n},i=new GS(this.consumer,a,r);return this.add(i)}add(t){return this.subscriptions.push(t),this.consumer.ensureActiveConnection(),this.notify(t,"initialized"),this.subscribe(t),t}remove(t){return this.forget(t),this.findAll(t.identifier).length||this.sendCommand(t,"unsubscribe"),t}reject(t){return this.findAll(t).map((r=>(this.forget(r),this.notify(r,"rejected"),r)))}forget(t){return this.guarantor.forget(t),this.subscriptions=this.subscriptions.filter((r=>r!==t)),t}findAll(t){return this.subscriptions.filter((r=>r.identifier===t))}reload(){return this.subscriptions.map((t=>this.subscribe(t)))}notifyAll(t,...r){return this.subscriptions.map((n=>this.notify(n,t,...r)))}notify(t,r,...n){let a;return typeof t=="string"?a=this.findAll(t):a=[t],a.map((i=>typeof i[r]=="function"?i[r](...n):void 0))}subscribe(t){this.sendCommand(t,"subscribe")&&this.guarantor.guarantee(t)}confirmSubscription(t){Lt.log(`Subscription confirmed ${t}`),this.findAll(t).map((r=>this.guarantor.forget(r)))}sendCommand(t,r){let{identifier:n}=t;return this.consumer.send({command:r,identifier:n})}},QS=class{constructor(t){this._url=t,this.subscriptions=new ZS(this),this.connection=new Gf(this),this.subprotocols=[]}get url(){return rQ(this._url)}send(t){return this.connection.send(t)}connect(){return this.connection.open()}disconnect(){return this.connection.close({allowReconnect:!1})}ensureActiveConnection(){if(!this.connection.isActive())return this.connection.open()}addSubProtocol(t){this.subprotocols=[...this.subprotocols,t]}};function rQ(e){if(typeof e=="function"&&(e=e()),e&&!/^wss?:/i.test(e)){let t=document.createElement("a");return t.href=e,t.href=t.href,t.protocol=t.protocol.replace("http","ws"),t.href}else return e}function sj(e=nQ("url")||lj.default_mount_path){return new QS(e)}function nQ(e){let t=document.head.querySelector(`meta[name='action-cable-${e}']`);if(t)return t.getAttribute("content")}var Ng=null;function aQ(){if(Ng===null)try{Ng=sj()}catch{Ng=!1}return Ng||null}function uj(e,t,r,n=!0){let a=(0,Oo.useRef)(null),i=(0,Oo.useRef)(r);return(0,Oo.useEffect)(()=>{i.current=r},[r]),(0,Oo.useEffect)(()=>{if(!n||!e)return;let l=aQ();return a.current=l.subscriptions.create({channel:e,...t},{connected(){console.log(`[ActionCable] Connected to ${e}`,t)},disconnected(){console.log(`[ActionCable] Disconnected from ${e}`)},received(s){console.log(`[ActionCable] Received on ${e}:`,s),i.current&&i.current(s)},rejected(){console.warn(`[ActionCable] Subscription rejected for ${e}`)}}),()=>{a.current&&(console.log(`[ActionCable] Unsubscribing from ${e}`),a.current.unsubscribe(),a.current=null)}},[e,JSON.stringify(t),n]),{send:(0,Oo.useCallback)((l,s)=>{a.current&&a.current.perform(l,s)},[])}}var _g=[{id:"anthropic",name:"Anthropic",model:"claude-haiku-4-5",color:"bg-orange-500"},{id:"openai",name:"OpenAI",model:"gpt-4o",color:"bg-green-500"},{id:"ollama",name:"Ollama",model:"llama3.1:8b",color:"bg-purple-500"}],iQ={playwright_mcp:[{name:"Screenshot Example.com",task:"Take a screenshot of https://example.com",description:"Navigate to example.com and capture a screenshot"},{name:"Extract Hacker News Headlines",task:"Go to https://news.ycombinator.com and list the top 5 story titles with their scores",description:"Scrape the front page of Hacker News"},{name:"Check Wikipedia",task:"Navigate to https://en.wikipedia.org/wiki/Artificial_intelligence and extract the first paragraph",description:"Extract content from Wikipedia"},{name:"GitHub Trending",task:"Visit https://github.com/trending and list the top 3 trending repositories",description:"Check GitHub's trending repositories"}],comparison:[{name:"Explain Ruby Blocks",task:"Explain how Ruby blocks work with a simple example",description:"Compare how each model explains Ruby concepts"},{name:"Write a Haiku",task:"Write a haiku about programming",description:"Compare creative output across models"},{name:"Debug This Code",task:"What's wrong with this code? def add(a, b); a - b; end; puts add(2, 3)",description:"Compare debugging abilities"},{name:"Summarize AI",task:"Summarize the current state of AI in 3 sentences",description:"Compare factual knowledge and conciseness"}]},cj={max_runs:10,timeout_seconds:300,session_duration_minutes:15};function JS({initialType:e="playwright_mcp",onClose:t}){let[r,n]=(0,B.useState)(e),[a,i]=(0,B.useState)(null),[o,l]=(0,B.useState)(""),[s,u]=(0,B.useState)([]),[c,d]=(0,B.useState)(null),[f,p]=(0,B.useState)(!1),[v,h]=(0,B.useState)(!1),[b,m]=(0,B.useState)(null),[g,x]=(0,B.useState)(null),[A,k]=(0,B.useState)(!1),[_,M]=(0,B.useState)(null),[E,L]=(0,B.useState)(!1),I=(0,B.useRef)(null),[j,z]=(0,B.useState)(["anthropic"]),[X,R]=(0,B.useState)(!1),[Q,y]=(0,B.useState)({}),[C,O]=(0,B.useState)([]),D=(0,B.useCallback)(q=>{switch(console.log("[SandboxRunner] Received ActionCable message:",q),q.type){case"run_started":X?y(ee=>({...ee,[q.provider]:{status:"running",started_at:q.started_at,provider:q.provider}})):d(ee=>({...ee,status:"running",provider:q.provider}));break;case"run_complete":X?(y(ee=>({...ee,[q.provider]:{...q.run,provider:q.provider}})),O(ee=>ee.filter(ie=>ie!==q.provider)),q.sandbox&&(i(q.sandbox),u(q.sandbox.runs||[]))):(d(q.run),p(!1),q.sandbox&&(i(q.sandbox),u(q.sandbox.runs||[]))),O(ee=>{let ie=ee.filter(K=>K!==q.provider);return ie.length===0&&p(!1),ie});break;case"run_error":X?(y(ee=>({...ee,[q.provider]:{status:"failed",error:q.error,provider:q.provider}})),O(ee=>ee.filter(ie=>ie!==q.provider))):(d(ee=>({...ee,status:"failed",error:q.error})),p(!1)),q.sandbox&&i(q.sandbox);break}},[X]);uj("SandboxChannel",{session_id:a?.session_id},D,!!a?.session_id),(0,B.useEffect)(()=>{T(),N()},[r]);let N=async()=>{try{let q=await fetch("/api/usage");if(q.ok){let ee=await q.json();M(ee.usage)}}catch{console.log("Could not fetch usage (user may not be authenticated)")}};(0,B.useEffect)(()=>{if(!a?.expires_at)return;let q=setInterval(()=>{let ee=new Date,ie=new Date(a.expires_at),K=Math.max(0,Math.floor((ie-ee)/1e3));x(K),K===0&&(i(xe=>({...xe,status:"expired"})),clearInterval(q))},1e3);return()=>clearInterval(q)},[a?.expires_at]),(0,B.useEffect)(()=>{I.current&&(I.current.scrollTop=I.current.scrollHeight)},[c?.result]);let T=async()=>{h(!0),m(null);try{let q=await fetch("/api/sandboxes",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sandbox_type:r})});if(!q.ok)throw new Error("Failed to create sandbox session");let ee=await q.json();i(ee.sandbox),u([]),d(null)}catch(q){m(q.message)}finally{h(!1)}},$=async()=>{!o.trim()||f||!a||(X&&j.length>1?await J():await Y(j[0]||"anthropic"))},Y=async q=>{p(!0),d({status:"running",task:o,provider:q,result:"",started_at:new Date().toISOString()});try{let ee=await fetch(`/api/sandboxes/${a.session_id}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({task:o,provider:q})}),ie=await ee.json();if(ee.status===402&&ie.upgrade_required){M(ie.usage),k(!0),d(K=>({...K,status:"failed",error:ie.message||"Plan limit reached"})),p(!1);return}if(!ee.ok)throw new Error(ie.error||"Task execution failed");ie.usage&&M(ie.usage),ge(ie.run_id)}catch(ee){d(ie=>({...ie,status:"failed",error:ee.message})),p(!1)}},J=async()=>{p(!0),O([...j]),y({});let q={};j.forEach(ee=>{q[ee]={status:"running",result:"",started_at:new Date().toISOString()}}),y(q);try{let ee=await fetch("/api/sandboxes/compare",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({task:o,providers:j,sandbox_id:a?.session_id,sandbox_type:r})});if(!ee.ok){let K=await ee.json();throw new Error(K.error||"Comparison failed")}let ie=await ee.json();ie.sandbox&&i(ie.sandbox),setTimeout(()=>{C.length>0&&fe()},6e4)}catch(ee){j.forEach(ie=>{y(K=>({...K,[ie]:{status:"failed",error:ee.message}}))}),p(!1),O([])}},fe=async()=>{if(a?.session_id)try{let ee=await(await fetch(`/api/sandboxes/${a.session_id}`)).json();i(ee.sandbox),u(ee.sandbox.runs||[]),(ee.sandbox.runs||[]).forEach(xe=>{xe.provider&&y(Ae=>({...Ae,[xe.provider]:{...xe,provider:xe.provider}}))}),j.every(xe=>{let Ae=Q[xe];return Ae?.status==="completed"||Ae?.status==="failed"})&&(p(!1),O([]))}catch(q){console.error("Polling error:",q)}},pe=q=>{z(ee=>ee.includes(q)?ee.length===1?ee:ee.filter(ie=>ie!==q):[...ee,q])},ge=async q=>{let ee=async()=>{try{let K=await(await fetch(`/api/sandboxes/${a.session_id}`)).json();i(K.sandbox);let xe=K.sandbox.runs||[],Ae=xe[xe.length-1];if(Ae&&(d(Ae),u(xe),Ae.status==="completed"||Ae.status==="failed")){p(!1);return}setTimeout(ee,1e3)}catch{p(!1)}};ee()},ze=q=>{q.key==="Enter"&&(q.metaKey||q.ctrlKey)&&$()},se=q=>{if(!q)return"--:--";let ee=Math.floor(q/60),ie=q%60;return`${ee}:${ie.toString().padStart(2,"0")}`},Ne=q=>{switch(q){case"completed":return"text-green-600 bg-green-100";case"failed":return"text-red-600 bg-red-100";case"running":return"text-blue-600 bg-blue-100";case"ready":return"text-emerald-600 bg-emerald-100";case"expired":return"text-orange-600 bg-orange-100";default:return"text-gray-600 bg-gray-100"}},F=a?.status==="ready"&&a?.runs_count{L(!0);try{await cc({planSlug:"pro",billingInterval:q})}catch(ee){console.error("Checkout error:",ee),m(ee.message||"Failed to initiate checkout. Please try again."),L(!1)}};return B.default.createElement("div",{className:"h-full flex flex-col bg-gray-50"},B.default.createElement("div",{className:"bg-white border-b border-gray-200 px-6 py-4"},B.default.createElement("div",{className:"flex items-center justify-between"},B.default.createElement("div",{className:"flex items-center space-x-4"},B.default.createElement(Tt,{size:48,appearance:{hat:"fedora",hatAccessory:"theaterMasks",heldItem:"browser"}}),B.default.createElement("div",null,B.default.createElement("h1",{className:"text-xl font-semibold text-gray-900"},"PlaywrightMCP Demo"),B.default.createElement("p",{className:"text-sm text-gray-500"},"Try browser automation for free - no account required"))),B.default.createElement("div",{className:"flex items-center space-x-4"},a&&B.default.createElement("div",{className:"flex items-center space-x-3 text-sm"},B.default.createElement("span",{className:`px-2 py-1 rounded-full text-xs font-medium ${Ne(a.status)}`},a.status),B.default.createElement("span",{className:"text-gray-500"},a.runs_count,"/",a.max_runs," runs"),B.default.createElement("span",{className:`font-mono ${g<60?"text-red-500":"text-gray-500"}`},se(g))),t&&B.default.createElement("button",{onClick:t,className:"text-gray-400 hover:text-gray-600"},B.default.createElement("svg",{className:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},B.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})))))),B.default.createElement("div",{className:"flex-1 p-6 overflow-hidden"},B.default.createElement("div",{className:"grid grid-cols-3 gap-6 h-full"},B.default.createElement("div",{className:"col-span-2 flex flex-col space-y-4"},B.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},B.default.createElement("textarea",{value:o,onChange:q=>l(q.target.value),onKeyDown:ze,placeholder:"Describe what you want the browser agent to do...",rows:3,disabled:!F,className:"w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-rose-500 focus:border-transparent resize-none disabled:bg-gray-100"}),B.default.createElement("div",{className:"flex items-center justify-between mt-3"},B.default.createElement("span",{className:"text-xs text-gray-400"},"Press ",B.default.createElement("kbd",{className:"px-1.5 py-0.5 bg-gray-100 rounded text-gray-600"},"Cmd")," + ",B.default.createElement("kbd",{className:"px-1.5 py-0.5 bg-gray-100 rounded text-gray-600"},"Enter")," to run"),B.default.createElement("button",{onClick:$,disabled:!o.trim()||!F,className:`flex items-center space-x-2 px-6 py-2 rounded-lg transition-colors ${o.trim()&&F?"bg-rose-500 text-white hover:bg-rose-600":"bg-gray-200 text-gray-400 cursor-not-allowed"}`},f?B.default.createElement(B.default.Fragment,null,B.default.createElement("span",{className:"animate-spin"},"\u25E0"),B.default.createElement("span",null,"Running...")):B.default.createElement(B.default.Fragment,null,B.default.createElement("span",null,"\u25B6"),B.default.createElement("span",null,"Run"))))),B.default.createElement("div",{className:"flex-1 bg-white rounded-xl border border-gray-200 flex flex-col overflow-hidden"},B.default.createElement("div",{className:"px-4 py-3 border-b border-gray-200 flex items-center justify-between"},B.default.createElement("h3",{className:"font-medium text-gray-900"},X&&Object.keys(Q).length>0?"Comparison Results":"Output"),c&&!X&&B.default.createElement("span",{className:`px-2 py-0.5 rounded-full text-xs font-medium ${Ne(c.status)}`},c.status),X&&C.length>0&&B.default.createElement("span",{className:"text-xs text-blue-600"},"Running ",C.length," provider(s)...")),B.default.createElement("div",{ref:I,className:"flex-1 overflow-auto bg-gray-50"},v?B.default.createElement("div",{className:"flex items-center justify-center h-full text-gray-500 p-4"},B.default.createElement("span",{className:"animate-pulse"},"Starting sandbox...")):b?B.default.createElement("div",{className:"text-red-600 p-4"},b):X&&Object.keys(Q).length>0?B.default.createElement("div",{className:"grid grid-cols-1 divide-y divide-gray-200"},j.map(q=>{let ee=_g.find(K=>K.id===q),ie=Q[q];return B.default.createElement("div",{key:q,className:"p-4"},B.default.createElement("div",{className:"flex items-center justify-between mb-2"},B.default.createElement("div",{className:"flex items-center space-x-2"},B.default.createElement("div",{className:`w-3 h-3 rounded-full ${ee?.color||"bg-gray-400"}`}),B.default.createElement("span",{className:"font-medium text-gray-900"},ee?.name||q),B.default.createElement("span",{className:"text-xs text-gray-500"},ee?.model)),ie&&B.default.createElement("div",{className:"flex items-center space-x-2"},B.default.createElement("span",{className:`px-2 py-0.5 rounded-full text-xs font-medium ${Ne(ie.status)}`},ie.status),ie.tokens&&B.default.createElement("span",{className:"text-xs text-gray-500"},ie.tokens," tokens"),ie.duration_ms&&B.default.createElement("span",{className:"text-xs text-gray-500"},(ie.duration_ms/1e3).toFixed(1),"s"))),B.default.createElement("div",{className:"bg-white rounded-lg border border-gray-200 p-3 font-mono text-sm max-h-48 overflow-auto"},ie?.status==="running"?B.default.createElement("div",{className:"flex items-center space-x-2 text-gray-500"},B.default.createElement("span",{className:"animate-pulse"},"\u25CF"),B.default.createElement("span",null,"Processing...")):ie?.error?B.default.createElement("div",{className:"text-red-600"},ie.error):ie?.result?B.default.createElement("pre",{className:"whitespace-pre-wrap text-gray-800"},ie.result):B.default.createElement("span",{className:"text-gray-400"},"Waiting...")))})):c?B.default.createElement("div",{className:"p-4 font-mono text-sm"},c.provider&&B.default.createElement("div",{className:"flex items-center space-x-2 mb-3 pb-2 border-b border-gray-200"},B.default.createElement("div",{className:`w-2 h-2 rounded-full ${_g.find(q=>q.id===c.provider)?.color||"bg-gray-400"}`}),B.default.createElement("span",{className:"text-sm text-gray-600"},_g.find(q=>q.id===c.provider)?.name||c.provider)),c.status==="running"?B.default.createElement("div",{className:"flex items-center space-x-2 text-gray-500"},B.default.createElement("span",{className:"animate-pulse"},"\u25CF"),B.default.createElement("span",null,"Executing task...")):c.error?B.default.createElement("div",{className:"text-red-600"},B.default.createElement("div",{className:"font-semibold mb-2"},"Error:"),B.default.createElement("pre",{className:"whitespace-pre-wrap"},c.error)):B.default.createElement("pre",{className:"whitespace-pre-wrap text-gray-800"},c.result||"Processing...")):B.default.createElement("div",{className:"text-gray-400 text-center py-12"},B.default.createElement("p",{className:"mb-2"},"Select a sample task or enter your own"),B.default.createElement("p",{className:"text-xs"},X?`Compare results across ${j.length} providers`:"Task will execute using the selected provider"))))),B.default.createElement("div",{className:"space-y-4 overflow-auto"},B.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},B.default.createElement("div",{className:"flex items-center justify-between mb-3"},B.default.createElement("h4",{className:"font-medium text-gray-900"},"Providers"),B.default.createElement("label",{className:"flex items-center space-x-2 cursor-pointer"},B.default.createElement("input",{type:"checkbox",checked:X,onChange:q=>R(q.target.checked),className:"rounded border-gray-300 text-rose-500 focus:ring-rose-500"}),B.default.createElement("span",{className:"text-xs text-gray-600"},"Compare"))),B.default.createElement("div",{className:"space-y-2"},_g.map(q=>B.default.createElement("label",{key:q.id,className:`flex items-center justify-between p-2 rounded-lg border cursor-pointer transition-colors ${j.includes(q.id)?"border-rose-300 bg-rose-50":"border-gray-200 hover:bg-gray-50"}`},B.default.createElement("div",{className:"flex items-center space-x-3"},B.default.createElement("input",{type:"checkbox",checked:j.includes(q.id),onChange:()=>pe(q.id),className:"rounded border-gray-300 text-rose-500 focus:ring-rose-500"}),B.default.createElement("div",{className:`w-2 h-2 rounded-full ${q.color}`}),B.default.createElement("span",{className:"text-sm font-medium text-gray-900"},q.name)),B.default.createElement("span",{className:"text-xs text-gray-500"},q.model)))),X&&j.length>1&&B.default.createElement("div",{className:"mt-3 p-2 bg-purple-50 rounded-lg"},B.default.createElement("p",{className:"text-xs text-purple-700"},"Comparison mode: Run task on ",j.length," providers simultaneously"))),B.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},B.default.createElement("div",{className:"flex items-center justify-between mb-3"},B.default.createElement("h4",{className:"font-medium text-gray-900"},"Sample Tasks"),X&&B.default.createElement("button",{onClick:()=>n(r==="playwright_mcp"?"comparison":"playwright_mcp"),className:"text-xs text-rose-600 hover:text-rose-700"},r==="comparison"?"Browser Tasks":"Comparison Tasks")),B.default.createElement("div",{className:"space-y-2"},iQ[r]?.map((q,ee)=>B.default.createElement("button",{key:ee,onClick:()=>l(q.task),disabled:!F,className:"w-full text-left px-3 py-2 rounded-lg transition-colors border border-gray-200 hover:bg-gray-50 disabled:opacity-50"},B.default.createElement("div",{className:"font-medium text-sm text-gray-900"},q.name),B.default.createElement("div",{className:"text-xs text-gray-500 mt-0.5"},q.description))))),B.default.createElement("div",{className:"bg-gradient-to-br from-rose-50 to-purple-50 rounded-xl border border-rose-200 p-4"},B.default.createElement("h4",{className:"font-medium text-gray-900 mb-3"},_?.plan==="free"?"Free Tier":`${_?.plan?.toUpperCase()||"FREE"} Plan`),_&&B.default.createElement("div",{className:"mb-4"},B.default.createElement("div",{className:"flex justify-between text-sm mb-1"},B.default.createElement("span",{className:"text-gray-600"},"Agent Runs"),B.default.createElement("span",{className:`font-medium ${_.runs_remaining<=1?"text-rose-600":"text-gray-900"}`},_.runs_used," / ",_.runs_limit)),B.default.createElement("div",{className:"w-full bg-white rounded-full h-2 border border-rose-200"},B.default.createElement("div",{className:`h-2 rounded-full transition-all ${_.runs_remaining<=1?"bg-rose-500":"bg-emerald-500"}`,style:{width:`${Math.min(_.runs_used/_.runs_limit*100,100)}%`}})),_.runs_remaining<=1&&B.default.createElement("p",{className:"text-xs text-rose-600 mt-1"},_.runs_remaining===0?"Limit reached!":"Almost at limit!")),B.default.createElement("dl",{className:"space-y-2 text-sm"},B.default.createElement("div",{className:"flex justify-between"},B.default.createElement("dt",{className:"text-gray-500"},"Session Runs"),B.default.createElement("dd",{className:"text-gray-900"},a?.runs_count||0,"/",a?.max_runs||cj.max_runs)),B.default.createElement("div",{className:"flex justify-between"},B.default.createElement("dt",{className:"text-gray-500"},"Timeout"),B.default.createElement("dd",{className:"text-gray-900"},cj.timeout_seconds,"s per task"))),_?.plan==="free"&&B.default.createElement("div",{className:"mt-4 pt-4 border-t border-rose-200"},B.default.createElement("button",{onClick:()=>k(!0),className:"w-full text-center text-sm text-rose-600 hover:text-rose-700 font-medium"},"Upgrade to PRO \u2192"))),s.length>0&&B.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 overflow-hidden"},B.default.createElement("div",{className:"px-4 py-3 border-b border-gray-100"},B.default.createElement("h4",{className:"font-medium text-gray-900"},"Run History")),B.default.createElement("div",{className:"max-h-48 overflow-auto divide-y divide-gray-100"},s.slice().reverse().map((q,ee)=>B.default.createElement("div",{key:q.id||ee,className:"px-4 py-3 hover:bg-gray-50 cursor-pointer",onClick:()=>d(q)},B.default.createElement("div",{className:"flex items-center justify-between mb-1"},B.default.createElement("span",{className:`px-2 py-0.5 rounded-full text-xs font-medium ${Ne(q.status||"completed")}`},q.status||"completed"),B.default.createElement("span",{className:"text-xs text-gray-400"},q.tokens||0," tokens")),B.default.createElement("p",{className:"text-sm text-gray-600 truncate"},q.task))))),a?.status==="expired"&&B.default.createElement("button",{onClick:T,className:"w-full px-4 py-3 bg-rose-500 text-white rounded-lg hover:bg-rose-600 transition-colors"},"Start New Session")))),A&&B.default.createElement("div",{className:"fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"},B.default.createElement("div",{className:"bg-white rounded-2xl max-w-4xl w-full overflow-hidden shadow-2xl max-h-[90vh] overflow-y-auto"},B.default.createElement("div",{className:"bg-gradient-to-r from-rose-500 to-purple-600 px-6 py-6 text-white text-center"},B.default.createElement("h2",{className:"text-2xl font-bold mb-1"},"Upgrade Your Plan"),B.default.createElement("p",{className:"text-rose-100 text-sm"},"You've used ",_?.runs_used||0," of ",_?.runs_limit||25," trial runs. Choose a plan to continue.")),B.default.createElement("div",{className:"p-6"},B.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4"},B.default.createElement("div",{className:"border border-gray-200 rounded-xl p-5"},B.default.createElement("div",{className:"text-center mb-4"},B.default.createElement("h3",{className:"font-semibold text-gray-900"},"Free"),B.default.createElement("div",{className:"mt-2"},B.default.createElement("span",{className:"text-3xl font-bold text-gray-900"},"$0"),B.default.createElement("span",{className:"text-gray-500"},"/mo"))),B.default.createElement("ul",{className:"space-y-2 text-sm text-gray-600 mb-4"},B.default.createElement("li",{className:"flex items-start"},B.default.createElement("span",{className:"text-gray-400 mr-2"},"-"),"25 trial runs/month"),B.default.createElement("li",{className:"flex items-start"},B.default.createElement("span",{className:"text-gray-400 mr-2"},"-"),"Community support"),B.default.createElement("li",{className:"flex items-start"},B.default.createElement("span",{className:"text-gray-400 mr-2"},"-"),"Basic features")),B.default.createElement("button",{disabled:!0,className:"w-full py-2 px-4 border border-gray-300 text-gray-400 rounded-lg text-sm cursor-not-allowed"},"Current Plan")),B.default.createElement("div",{className:"border-2 border-rose-500 rounded-xl p-5 relative bg-rose-50"},B.default.createElement("div",{className:"absolute -top-3 left-1/2 transform -translate-x-1/2"},B.default.createElement("span",{className:"bg-rose-500 text-white text-xs font-semibold px-3 py-1 rounded-full"},"RECOMMENDED")),B.default.createElement("div",{className:"text-center mb-4 mt-2"},B.default.createElement("h3",{className:"font-semibold text-gray-900"},"Pro"),B.default.createElement("div",{className:"mt-2"},B.default.createElement("span",{className:"text-3xl font-bold text-gray-900"},"$99"),B.default.createElement("span",{className:"text-gray-500"},"/mo")),B.default.createElement("p",{className:"text-xs text-gray-500 mt-1"},"or $995/year (save 16%)")),B.default.createElement("ul",{className:"space-y-2 text-sm text-gray-700 mb-4"},B.default.createElement("li",{className:"flex items-start"},B.default.createElement("span",{className:"text-green-500 mr-2"},"\u2713"),"10,000 agent runs/month"),B.default.createElement("li",{className:"flex items-start"},B.default.createElement("span",{className:"text-green-500 mr-2"},"\u2713"),"Advanced cost tracking"),B.default.createElement("li",{className:"flex items-start"},B.default.createElement("span",{className:"text-green-500 mr-2"},"\u2713"),"25K traces/month"),B.default.createElement("li",{className:"flex items-start"},B.default.createElement("span",{className:"text-green-500 mr-2"},"\u2713"),"Priority email support")),B.default.createElement("button",{onClick:()=>H("monthly"),disabled:E,className:"w-full py-2 px-4 bg-rose-500 text-white rounded-lg text-sm font-semibold hover:bg-rose-600 transition-colors disabled:opacity-50"},E?"Loading...":"Upgrade to Pro")),B.default.createElement("div",{className:"border border-gray-200 rounded-xl p-5"},B.default.createElement("div",{className:"text-center mb-4"},B.default.createElement("h3",{className:"font-semibold text-gray-900"},"Enterprise"),B.default.createElement("div",{className:"mt-2"},B.default.createElement("span",{className:"text-3xl font-bold text-gray-900"},"$269"),B.default.createElement("span",{className:"text-gray-500"},"/mo")),B.default.createElement("p",{className:"text-xs text-gray-500 mt-1"},"or $2,690/year (save 16%)")),B.default.createElement("ul",{className:"space-y-2 text-sm text-gray-600 mb-4"},B.default.createElement("li",{className:"flex items-start"},B.default.createElement("span",{className:"text-green-500 mr-2"},"\u2713"),"Unlimited runs"),B.default.createElement("li",{className:"flex items-start"},B.default.createElement("span",{className:"text-green-500 mr-2"},"\u2713"),"500K traces/month"),B.default.createElement("li",{className:"flex items-start"},B.default.createElement("span",{className:"text-green-500 mr-2"},"\u2713"),"SSO & SOC2/HIPAA"),B.default.createElement("li",{className:"flex items-start"},B.default.createElement("span",{className:"text-green-500 mr-2"},"\u2713"),"Dedicated support")),B.default.createElement("a",{href:"mailto:sales@activeagents.ai",className:"block w-full py-2 px-4 border border-gray-300 text-gray-700 rounded-lg text-sm font-semibold text-center hover:bg-gray-50 transition-colors"},"Contact Sales")))),B.default.createElement("div",{className:"px-6 py-4 bg-gray-50 border-t border-gray-100 flex justify-center"},B.default.createElement("button",{onClick:()=>k(!1),className:"text-gray-500 hover:text-gray-700 text-sm"},"Maybe later")))))}var ve=te(ae());var Cg={SCREENSHOT:"screenshot",IFRAME:"iframe"},dj={navigate:"[->]",click:"[*]",type:"[T]",form_fill:"[F]",key_press:"[K]",snapshot:"[S]",hover:"[~]",select:"[v]",file_upload:"[^]",dialog:"[!]",evaluate:"[js]",wait:"[..]",scroll:"[|]",drag:"[<>]"},RS={navigate:"#3b82f6",click:"#ef4444",type:"#10b981",form_fill:"#8b5cf6",key_press:"#f59e0b",snapshot:"#06b6d4",hover:"#ec4899",select:"#6366f1",file_upload:"#14b8a6",dialog:"#f97316",evaluate:"#a855f7",wait:"#94a3b8",scroll:"#64748b",drag:"#ea580c"},oQ=` + @keyframes ripple { + 0% { transform: translate(-50%, -50%) scale(0.5); opacity: 1; } + 100% { transform: translate(-50%, -50%) scale(2); opacity: 0; } + } + @keyframes typing-cursor { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } + } + @keyframes pulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.5; transform: scale(1.2); } + } +`;function eA({recordingId:e,onHandoff:t,onClose:r}){let{darkMode:n}=pt(),[a,i]=(0,ve.useState)([]),[o,l]=(0,ve.useState)(e),[s,u]=(0,ve.useState)(null),[c,d]=(0,ve.useState)([]),[f,p]=(0,ve.useState)(!0),[v,h]=(0,ve.useState)(null),[b,m]=(0,ve.useState)(!1),[g,x]=(0,ve.useState)(0),[A,k]=(0,ve.useState)(1),[_,M]=(0,ve.useState)(null),E=(0,ve.useRef)(null),L=(0,ve.useRef)(null),I=(0,ve.useRef)(null),[j,z]=(0,ve.useState)(Cg.IFRAME),[X,R]=(0,ve.useState)({x:0,y:0,visible:!1}),[Q,y]=(0,ve.useState)(null);(0,ve.useEffect)(()=>{C()},[]),(0,ve.useEffect)(()=>{o&&T(o)},[o]);let C=async()=>{try{let F=await fetch("/api/session_recordings");if(F.ok){let H=await F.json();i(H.recordings||[]),!o&&H.recordings?.length>0&&l(H.recordings[0].id)}}catch(F){console.error("Failed to load recordings list:",F)}};(0,ve.useEffect)(()=>{if(!b||c.length===0){L.current&&clearTimeout(L.current);return}let F=c[g],H=c[g+1];if(!H){m(!1);return}let q=(H.timestamp_ms-F.timestamp_ms)/A;return L.current=setTimeout(()=>{x(ee=>ee+1)},Math.max(q,50)),()=>{L.current&&clearTimeout(L.current)}},[b,g,c,A]),(0,ve.useEffect)(()=>{let F=c[g];F?.screenshot_url?(M(F.screenshot_url),z(Cg.SCREENSHOT)):j===Cg.IFRAME&&O(F)},[g,c,j]);let O=(0,ve.useCallback)(F=>{if(!F||!I.current)return;let H=I.current;try{let q=s?.metadata?.page_url||"/";switch(H.src!==q&&H.src.includes(q),F.action_type){case"navigate":R({x:50,y:50,visible:!0}),y(null);break;case"click":case"focus":R({x:200,y:300,visible:!0}),y(F.selector),D(F.selector);break;case"type":R({x:200,y:300,visible:!0}),y(F.selector),N(F.selector,F.value);break;case"submit":R({x:250,y:400,visible:!0}),y(F.selector);break;case"scroll":R({x:300,y:200,visible:!0}),y(null);break;case"handoff":R({x:0,y:0,visible:!1}),y(null);break;default:R({x:150,y:250,visible:!0}),y(F.selector)}}catch(q){console.log("Could not simulate action in iframe:",q)}},[s]),D=F=>{if(!(!F||!I.current))try{let H=I.current.contentDocument||I.current.contentWindow?.document;if(!H)return;H.querySelectorAll(".session-replay-highlight").forEach(ee=>{ee.classList.remove("session-replay-highlight"),ee.style.outline=""});let q=H.querySelector(F);q&&(q.classList.add("session-replay-highlight"),q.style.outline="3px solid #ef4444",q.style.outlineOffset="2px",q.scrollIntoView({behavior:"smooth",block:"center"}))}catch{}},N=(F,H)=>{if(!(!F||!H||!I.current))try{let q=I.current.contentDocument||I.current.contentWindow?.document;if(!q)return;let ee=q.querySelector(F);if(ee&&(ee.tagName==="INPUT"||ee.tagName==="TEXTAREA")){let ie=0,K=setInterval(()=>{ie{p(!0),h(null);try{let H=F?`/api/session_recordings/${F}`:"/api/session_recordings/demo",q=await fetch(H);if(!q.ok)throw new Error("Failed to load recording");let ee=await q.json();if(u(ee.recording),x(0),m(!1),ee.recording?.id){let ie=await fetch(`/api/session_recordings/${ee.recording.id}/actions`);if(ie.ok){let K=await ie.json();d(K.actions||ee.recording.timeline||[])}else d(ee.recording.timeline||[])}}catch(H){h(H.message)}finally{p(!1)}},$=()=>{g>=c.length-1&&x(0),m(!b)},Y=()=>{m(!1),x(F=>Math.min(F+1,c.length-1))},J=()=>{m(!1),x(F=>Math.max(F-1,0))},fe=F=>{if(!E.current||c.length===0)return;let H=E.current.getBoundingClientRect(),ie=(F.clientX-H.left)/H.width*(s?.duration_ms||c[c.length-1]?.timestamp_ms||0),K=c.findIndex((xe,Ae)=>{let we=c[Ae+1];return!we||ie{if(s?.id)try{let F=await fetch(`/api/session_recordings/${s.id}/handoff`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!F.ok)throw new Error("Handoff not available");let H=await F.json();t&&t(H)}catch(F){h(F.message)}},ge=F=>{if(!F)return"0:00";let H=Math.floor(F/1e3),q=Math.floor(H/60),ee=H%60;return`${q}:${ee.toString().padStart(2,"0")}`},ze=c[g],se=s?.duration_ms?(ze?.timestamp_ms||0)/s.duration_ms*100:0;return f?ve.default.createElement("div",{className:"flex items-center justify-center h-64"},ve.default.createElement("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-red-500"})):v?ve.default.createElement("div",{className:"flex items-center justify-center h-64"},ve.default.createElement("div",{className:"text-red-500"},v)):s?ve.default.createElement("div",{className:n?"":"bg-white rounded-xl border border-gray-200",style:n?{background:"linear-gradient(180deg, #1a1a2e 0%, #16213e 100%)",borderRadius:"12px",overflow:"hidden",minHeight:"calc(100vh - 200px)"}:{}},ve.default.createElement("style",{dangerouslySetInnerHTML:{__html:oQ}}),ve.default.createElement("div",{className:n?"":"border-b border-gray-200 p-4",style:n?{padding:"16px 24px",borderBottom:"1px solid rgba(255,255,255,0.1)",display:"flex",alignItems:"center",justifyContent:"space-between"}:{display:"flex",alignItems:"center",justifyContent:"space-between"}},ve.default.createElement("div",null,ve.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"12px"}},ve.default.createElement("h2",{className:n?"":"text-xl font-semibold text-gray-900",style:n?{fontSize:"20px",fontWeight:"600",color:"white",margin:0}:{}},s.name||"Session Replay"),a.length>1&&ve.default.createElement("select",{value:o||"",onChange:F=>l(Number(F.target.value)),style:{padding:"6px 12px",borderRadius:"6px",border:n?"1px solid rgba(255,255,255,0.2)":"1px solid #d1d5db",background:n?"rgba(255,255,255,0.1)":"white",color:n?"white":"#374151",fontSize:"14px",cursor:"pointer"}},a.map(F=>ve.default.createElement("option",{key:F.id,value:F.id},F.name," (",new Date(F.created_at).toLocaleDateString(),")")))),ve.default.createElement("p",{className:n?"":"text-sm text-gray-500 mt-1",style:n?{fontSize:"14px",color:"rgba(255,255,255,0.6)",marginTop:"4px"}:{}},s.action_count," actions | ",ge(s.duration_ms)," duration")),ve.default.createElement("div",{className:"flex items-center gap-3"},s.handoff_state&&ve.default.createElement("button",{onClick:pe,className:n?"":"px-4 py-2 bg-gradient-to-r from-red-500 to-rose-500 text-white rounded-lg hover:from-red-600 hover:to-rose-600 font-medium",style:n?{padding:"10px 20px",background:"linear-gradient(90deg, #ef4444 0%, #f43f5e 100%)",color:"white",borderRadius:"8px",border:"none",fontWeight:"500",cursor:"pointer",fontSize:"14px"}:{}},"Take Over Session"),r&&ve.default.createElement("button",{onClick:r,className:n?"":"text-gray-400 hover:text-gray-600",style:n?{color:"rgba(255,255,255,0.6)",background:"none",border:"none",cursor:"pointer"}:{}},ve.default.createElement("span",{style:{fontSize:"20px"}},"x")))),ve.default.createElement("div",{className:n?"":"flex h-[500px]",style:n?{display:"flex",height:"500px"}:{}},ve.default.createElement("div",{className:n?"":"flex-1 bg-gray-100 flex items-center justify-center relative",style:n?{flex:1,background:"rgba(0,0,0,0.3)",display:"flex",alignItems:"center",justifyContent:"center",position:"relative",overflow:"hidden"}:{overflow:"hidden"}},j===Cg.SCREENSHOT&&_?ve.default.createElement("img",{src:_,alt:"Session screenshot",className:"max-w-full max-h-full object-contain"}):ve.default.createElement("div",{style:{width:"100%",height:"100%",position:"relative",overflow:"hidden"}},ve.default.createElement("iframe",{ref:I,src:s?.metadata?.page_url||"/",style:{border:"none",transform:"scale(0.75)",transformOrigin:"top left",width:"133.33%",height:"133.33%",pointerEvents:"none"},sandbox:"allow-same-origin allow-scripts"}),X.visible&&ve.default.createElement("div",{style:{position:"absolute",left:`${X.x}px`,top:`${X.y}px`,width:"24px",height:"24px",pointerEvents:"none",zIndex:100,transition:"all 0.3s ease-out"}},ve.default.createElement("svg",{viewBox:"0 0 24 24",fill:"none",style:{width:"100%",height:"100%"}},ve.default.createElement("path",{d:"M4 4L12 20L14 14L20 12L4 4Z",fill:"#ef4444",stroke:"white",strokeWidth:"2"})),ze?.action_type==="click"&&ve.default.createElement("div",{style:{position:"absolute",top:"50%",left:"50%",transform:"translate(-50%, -50%)",width:"40px",height:"40px",borderRadius:"50%",border:"3px solid #ef4444",animation:"ripple 0.6s ease-out",opacity:0}})),Q&&ve.default.createElement("div",{style:{position:"absolute",bottom:"16px",left:"16px",background:"rgba(0,0,0,0.8)",color:"#60a5fa",padding:"8px 12px",borderRadius:"6px",fontSize:"12px",fontFamily:Oe.mono,zIndex:100}},Q),ze&&ve.default.createElement("div",{style:{position:"absolute",top:"60px",left:"16px",background:"rgba(0,0,0,0.8)",color:"white",padding:"8px 16px",borderRadius:"6px",fontSize:"13px",display:"flex",alignItems:"center",gap:"8px",zIndex:100}},ve.default.createElement("span",{style:{color:RS[ze.action_type]||"#94a3b8",fontWeight:"600"}},dj[ze.action_type]||"[?]"),ve.default.createElement("span",null,ze.action_type),ze.value&&ve.default.createElement("span",{style:{color:"rgba(255,255,255,0.6)",maxWidth:"200px",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}},": ",ze.value))),ve.default.createElement("div",{className:n?"":"absolute top-4 left-4 flex items-center gap-2 text-white px-3 py-1 rounded-full text-sm",style:{position:"absolute",top:"16px",left:"16px",display:"flex",alignItems:"center",gap:"8px",background:s?.status==="recording"?"#ef4444":b?"#10b981":"#6b7280",color:"white",padding:"6px 12px",borderRadius:"999px",fontSize:"13px",fontWeight:"500"}},s?.status==="recording"?ve.default.createElement("span",{style:{width:"8px",height:"8px",borderRadius:"50%",background:"white",animation:"pulse 1.5s ease-in-out infinite"}}):b?ve.default.createElement("svg",{width:"10",height:"12",viewBox:"0 0 10 12",fill:"white"},ve.default.createElement("path",{d:"M0 0L10 6L0 12V0Z"})):ve.default.createElement("svg",{width:"10",height:"12",viewBox:"0 0 10 12",fill:"white"},ve.default.createElement("rect",{x:"0",y:"0",width:"3",height:"12"}),ve.default.createElement("rect",{x:"7",y:"0",width:"3",height:"12"})),s?.status==="recording"?"REC":b?"Playing":"Paused"),ze?.selector&&ve.default.createElement("div",{className:n?"":"absolute bottom-4 left-4 bg-black/75 text-white px-3 py-1 rounded text-sm font-mono",style:n?{position:"absolute",bottom:"16px",left:"16px",background:"rgba(0,0,0,0.75)",color:"white",padding:"6px 12px",borderRadius:"6px",fontSize:"13px",fontFamily:Oe.mono}:{}},ze.selector)),ve.default.createElement("div",{className:n?"":"w-80 border-l border-gray-200 flex flex-col",style:n?{width:"320px",borderLeft:"1px solid rgba(255,255,255,0.1)",display:"flex",flexDirection:"column"}:{}},ve.default.createElement("div",{className:n?"":"flex-1 overflow-auto",style:n?{flex:1,overflowY:"auto"}:{}},ve.default.createElement("div",{className:n?"":"p-3 border-b border-gray-100 font-medium text-sm text-gray-700",style:n?{padding:"12px 16px",borderBottom:"1px solid rgba(255,255,255,0.1)",fontWeight:"500",fontSize:"14px",color:"rgba(255,255,255,0.8)"}:{}},"Actions (",c.length,")"),c.map((F,H)=>{let q=H===g,ee=H{x(H),m(!1)},className:n?"":`px-4 py-3 cursor-pointer border-b border-gray-50 hover:bg-gray-50 ${q?"bg-red-50 border-l-2 border-l-red-500":""}`,style:n?{padding:"12px 16px",cursor:"pointer",borderBottom:"1px solid rgba(255,255,255,0.05)",background:q?"rgba(239, 68, 68, 0.15)":"transparent",borderLeft:q?"3px solid #ef4444":"3px solid transparent",opacity:ee?.6:1}:{opacity:ee?.6:1}},ve.default.createElement("div",{className:"flex items-center gap-2"},ve.default.createElement("span",{style:{color:ie,fontFamily:Oe.mono,fontSize:"12px",fontWeight:"600"}},dj[F.action_type]||"[?]"),ve.default.createElement("span",{className:n?"":"text-sm font-medium text-gray-900",style:n?{fontSize:"14px",fontWeight:"500",color:"white"}:{}},F.action_type),ve.default.createElement("span",{className:n?"":"text-xs text-gray-400 ml-auto",style:n?{fontSize:"12px",color:"rgba(255,255,255,0.4)",marginLeft:"auto"}:{}},ge(F.timestamp_ms))),F.value&&ve.default.createElement("div",{className:n?"":"text-xs text-gray-500 mt-1 truncate",style:n?{fontSize:"12px",color:"rgba(255,255,255,0.5)",marginTop:"4px",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}:{}},F.value.length>50?F.value.substring(0,50)+"...":F.value),F.selector&&ve.default.createElement("div",{className:n?"":"text-xs text-blue-500 mt-1 font-mono truncate",style:n?{fontSize:"11px",color:"#60a5fa",marginTop:"4px",fontFamily:Oe.mono,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}:{}},F.selector))})))),ve.default.createElement("div",{className:n?"":"border-t border-gray-200 p-4",style:n?{borderTop:"1px solid rgba(255,255,255,0.1)",padding:"16px 24px"}:{}},ve.default.createElement("div",{ref:E,onClick:fe,className:n?"":"h-2 bg-gray-200 rounded-full mb-4 cursor-pointer relative",style:n?{height:"8px",background:"rgba(255,255,255,0.1)",borderRadius:"4px",marginBottom:"16px",cursor:"pointer",position:"relative"}:{}},ve.default.createElement("div",{className:n?"":"absolute h-full bg-red-500 rounded-full",style:{width:`${se}%`,height:"100%",background:"#ef4444",borderRadius:"4px",position:"absolute"}}),c.map((F,H)=>{let q=s?.duration_ms?F.timestamp_ms/s.duration_ms*100:0,ee=RS[F.action_type]||"#94a3b8";return ve.default.createElement("div",{key:H,className:n?"":"absolute w-1 h-3 -top-0.5 transform -translate-x-1/2",style:{left:`${q}%`,width:"3px",height:"12px",top:"-2px",transform:"translateX(-50%)",background:H===g?ee:"rgba(255,255,255,0.3)",borderRadius:"2px",position:"absolute"}})})),ve.default.createElement("div",{className:n?"":"flex items-center justify-between",style:n?{display:"flex",alignItems:"center",justifyContent:"space-between"}:{}},ve.default.createElement("div",{className:"flex items-center gap-4"},ve.default.createElement("div",{className:"flex items-center gap-2"},ve.default.createElement("button",{onClick:J,disabled:g===0,className:n?"":"p-2 rounded-lg bg-gray-100 hover:bg-gray-200 disabled:opacity-50",style:n?{padding:"8px 12px",background:"rgba(255,255,255,0.1)",border:"none",borderRadius:"8px",color:"white",cursor:g===0?"not-allowed":"pointer",opacity:g===0?.5:1}:{}},"<<"),ve.default.createElement("button",{onClick:$,className:n?"":"p-3 rounded-full bg-red-500 text-white hover:bg-red-600",style:n?{padding:"12px 24px",background:"#ef4444",border:"none",borderRadius:"999px",color:"white",cursor:"pointer",fontWeight:"500"}:{}},b?"Pause":"Play"),ve.default.createElement("button",{onClick:Y,disabled:g>=c.length-1,className:n?"":"p-2 rounded-lg bg-gray-100 hover:bg-gray-200 disabled:opacity-50",style:n?{padding:"8px 12px",background:"rgba(255,255,255,0.1)",border:"none",borderRadius:"8px",color:"white",cursor:g>=c.length-1?"not-allowed":"pointer",opacity:g>=c.length-1?.5:1}:{}},">>")),ve.default.createElement("span",{className:n?"":"text-sm text-gray-600 font-mono",style:n?{fontSize:"14px",color:"rgba(255,255,255,0.7)",fontFamily:Oe.mono}:{}},ge(ze?.timestamp_ms||0)," / ",ge(s?.duration_ms||0))),ve.default.createElement("div",{className:"flex items-center gap-2"},ve.default.createElement("span",{className:n?"":"text-sm text-gray-500",style:n?{fontSize:"14px",color:"rgba(255,255,255,0.5)"}:{}},"Speed:"),[.5,1,2,4].map(F=>ve.default.createElement("button",{key:F,onClick:()=>k(F),className:n?"":`px-2 py-1 rounded text-sm ${A===F?"bg-red-500 text-white":"bg-gray-100 text-gray-600"}`,style:n?{padding:"4px 10px",background:A===F?"#ef4444":"rgba(255,255,255,0.1)",color:"white",border:"none",borderRadius:"6px",cursor:"pointer",fontSize:"13px"}:{}},F,"x")))))):ve.default.createElement("div",{className:"flex items-center justify-center h-64"},ve.default.createElement("div",{className:"text-gray-500"},"No recording available"))}var de=te(ae());var Eg=e=>e==null?"0":e>=1e6?(e/1e6).toFixed(1)+"M":e>=1e3?(e/1e3).toFixed(1)+"K":e.toString();function tA({account:e,user:t,subscription:r,agentCount:n=0}){let{darkMode:a}=pt(),[i,o]=(0,de.useState)(null),[l,s]=(0,de.useState)(!1),[u,c]=(0,de.useState)(!1),[d,f]=(0,de.useState)(!1),[p,v]=(0,de.useState)(null);(0,de.useEffect)(()=>{fetch("/api/usage").then(k=>k.ok?k.json():null).then(k=>o(k?.usage||null)).catch(()=>o(null))},[]);let h=async()=>{f(!0),v(null);try{await cc({planSlug:"pro"})}catch(k){v(k.message),f(!1)}},b=()=>{e?.telemetry_api_key&&navigator.clipboard.writeText(e.telemetry_api_key).then(()=>{s(!0),setTimeout(()=>s(!1),2e3)})},m={backgroundColor:a?"#1f1f1f":"#ffffff",borderColor:a?"#2a2a2a":"#e5e7eb"},g=r?.plan_name||"Free",x={free:{seats:1,workspaces:1,agents:3,traces:"250/mo trial"},pro:{seats:5,workspaces:3,agents:"Unlimited",traces:"25,000/mo"},enterprise:{seats:"Unlimited",workspaces:"Unlimited",agents:"Unlimited",traces:"500,000+/mo"}},A=x[g.toLowerCase()]||x.free;return de.default.createElement("div",{className:"p-6 space-y-6"},de.default.createElement("div",null,de.default.createElement("h2",{className:`text-2xl font-bold ${a?"text-white":"text-gray-900"}`},"Organization"),de.default.createElement("p",{className:`mt-1 ${a?"text-gray-400":"text-gray-500"}`},"Manage your workspace, team, and billing")),de.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6"},de.default.createElement("div",{className:"border rounded-lg p-6",style:m},de.default.createElement("h3",{className:`text-lg font-semibold mb-4 ${a?"text-white":"text-gray-900"}`},"Workspace"),de.default.createElement("div",{className:"space-y-3"},de.default.createElement("div",{className:"flex justify-between items-center"},de.default.createElement("span",{className:a?"text-gray-400":"text-gray-500"},"Name"),de.default.createElement("span",{className:`font-medium ${a?"text-white":"text-gray-900"}`},e?.name||"My Workspace")),de.default.createElement("div",{className:"flex justify-between items-center"},de.default.createElement("span",{className:a?"text-gray-400":"text-gray-500"},"Owner"),de.default.createElement("span",{className:`font-medium ${a?"text-white":"text-gray-900"}`},t?.email||"you@example.com")),de.default.createElement("div",{className:"flex justify-between items-center"},de.default.createElement("span",{className:a?"text-gray-400":"text-gray-500"},"Created"),de.default.createElement("span",{className:`font-medium ${a?"text-white":"text-gray-900"}`},e?.created_at?new Date(e.created_at).toLocaleDateString():"Today")))),de.default.createElement("div",{className:"border rounded-lg p-6",style:m},de.default.createElement("div",{className:"flex items-center justify-between mb-4"},de.default.createElement("h3",{className:`text-lg font-semibold ${a?"text-white":"text-gray-900"}`},"Current Plan"),de.default.createElement("span",{className:"px-3 py-1 bg-red-100 text-red-700 rounded-full text-sm font-medium"},g)),de.default.createElement("div",{className:"space-y-3"},de.default.createElement("div",{className:"flex justify-between items-center"},de.default.createElement("span",{className:a?"text-gray-400":"text-gray-500"},"Team Seats"),de.default.createElement("span",{className:`font-medium ${a?"text-white":"text-gray-900"}`},A.seats)),de.default.createElement("div",{className:"flex justify-between items-center"},de.default.createElement("span",{className:a?"text-gray-400":"text-gray-500"},"Workspaces"),de.default.createElement("span",{className:`font-medium ${a?"text-white":"text-gray-900"}`},A.workspaces)),de.default.createElement("div",{className:"flex justify-between items-center"},de.default.createElement("span",{className:a?"text-gray-400":"text-gray-500"},"Agents"),de.default.createElement("span",{className:`font-medium ${a?"text-white":"text-gray-900"}`},A.agents)),de.default.createElement("div",{className:"flex justify-between items-center"},de.default.createElement("span",{className:a?"text-gray-400":"text-gray-500"},"Traces"),de.default.createElement("span",{className:`font-medium ${a?"text-white":"text-gray-900"}`},A.traces))),g.toLowerCase()!=="enterprise"&&de.default.createElement(de.default.Fragment,null,de.default.createElement("button",{onClick:h,disabled:d,className:"mt-4 w-full px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"},d?"Redirecting to checkout\u2026":"Upgrade Plan"),p&&de.default.createElement("p",{className:"mt-2 text-sm text-red-500 text-center"},p)))),de.default.createElement("div",{className:"border rounded-lg p-6",style:m},de.default.createElement("div",{className:"flex items-center justify-between mb-4"},de.default.createElement("h3",{className:`text-lg font-semibold ${a?"text-white":"text-gray-900"}`},"Team Members"),de.default.createElement("button",{className:"px-4 py-2 text-sm bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors"},"+ Invite Member")),de.default.createElement("div",{className:"overflow-hidden"},de.default.createElement("table",{className:"min-w-full"},de.default.createElement("thead",null,de.default.createElement("tr",{className:a?"border-b border-gray-700":"border-b border-gray-200"},de.default.createElement("th",{className:`text-left py-3 px-4 text-sm font-medium ${a?"text-gray-400":"text-gray-500"}`},"Member"),de.default.createElement("th",{className:`text-left py-3 px-4 text-sm font-medium ${a?"text-gray-400":"text-gray-500"}`},"Role"),de.default.createElement("th",{className:`text-left py-3 px-4 text-sm font-medium ${a?"text-gray-400":"text-gray-500"}`},"Status"))),de.default.createElement("tbody",null,de.default.createElement("tr",null,de.default.createElement("td",{className:"py-3 px-4"},de.default.createElement("div",{className:"flex items-center space-x-3"},de.default.createElement("div",{className:"w-8 h-8 bg-red-100 rounded-full flex items-center justify-center"},de.default.createElement("span",{className:"text-red-600 font-medium text-sm"},t?.name?.charAt(0).toUpperCase()||"U")),de.default.createElement("div",null,de.default.createElement("p",{className:`font-medium ${a?"text-white":"text-gray-900"}`},t?.name||"You"),de.default.createElement("p",{className:`text-sm ${a?"text-gray-400":"text-gray-500"}`},t?.email)))),de.default.createElement("td",{className:"py-3 px-4"},de.default.createElement("span",{className:"px-2 py-1 bg-purple-100 text-purple-700 rounded text-sm"},"Owner")),de.default.createElement("td",{className:"py-3 px-4"},de.default.createElement("span",{className:"flex items-center text-green-600"},de.default.createElement("span",{className:"w-2 h-2 bg-green-500 rounded-full mr-2"}),"Active"))))))),de.default.createElement("div",{className:"border rounded-lg p-6",style:m},de.default.createElement("h3",{className:`text-lg font-semibold mb-4 ${a?"text-white":"text-gray-900"}`},"Usage This Month"),de.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-4 gap-4"},de.default.createElement("div",{className:"text-center p-4 rounded-lg",style:{backgroundColor:a?"#252525":"#f9fafb"}},de.default.createElement("p",{className:`text-2xl font-bold ${a?"text-white":"text-gray-900"}`},n),de.default.createElement("p",{className:`text-sm ${a?"text-gray-400":"text-gray-500"}`},"Agents")),de.default.createElement("div",{className:"text-center p-4 rounded-lg",style:{backgroundColor:a?"#252525":"#f9fafb"}},de.default.createElement("p",{className:`text-2xl font-bold ${a?"text-white":"text-gray-900"}`},Eg(i?.traces_used),i?.traces_limit>0&&de.default.createElement("span",{className:`text-sm font-normal ${a?"text-gray-500":"text-gray-400"}`}," / ",Eg(i.traces_limit))),de.default.createElement("p",{className:`text-sm ${a?"text-gray-400":"text-gray-500"}`},"Traces")),de.default.createElement("div",{className:"text-center p-4 rounded-lg",style:{backgroundColor:a?"#252525":"#f9fafb"}},de.default.createElement("p",{className:`text-2xl font-bold ${a?"text-white":"text-gray-900"}`},Eg(i?.runs_used)),de.default.createElement("p",{className:`text-sm ${a?"text-gray-400":"text-gray-500"}`},"Executions")),de.default.createElement("div",{className:"text-center p-4 rounded-lg",style:{backgroundColor:a?"#252525":"#f9fafb"}},de.default.createElement("p",{className:`text-2xl font-bold ${a?"text-white":"text-gray-900"}`},Eg(i?.tokens_used)),de.default.createElement("p",{className:`text-sm ${a?"text-gray-400":"text-gray-500"}`},"Tokens")))),de.default.createElement("div",{className:"border rounded-lg p-6",style:m},de.default.createElement("h3",{className:`text-lg font-semibold mb-1 ${a?"text-white":"text-gray-900"}`},"Telemetry"),de.default.createElement("p",{className:`text-sm mb-4 ${a?"text-gray-400":"text-gray-500"}`},"Send traces from your own ActiveAgent app to this workspace. Add this to your"," ",de.default.createElement("code",{className:`px-1 rounded text-xs ${a?"bg-gray-800 text-gray-300":"bg-gray-100 text-gray-700"}`},"config/active_agent.yml"),":"),de.default.createElement("pre",{className:`text-xs rounded-lg p-4 overflow-x-auto mb-4 ${a?"bg-black text-gray-300":"bg-gray-900 text-gray-100"}`},`telemetry: + enabled: true + endpoint: ${window.location.origin}/v1/traces + api_key: <%= ENV["ACTIVEAGENTS_API_KEY"] %>`),de.default.createElement("div",{className:"flex items-center justify-between gap-3"},de.default.createElement("div",{className:"min-w-0 flex-1"},de.default.createElement("p",{className:`text-xs uppercase tracking-wide mb-1 ${a?"text-gray-500":"text-gray-400"}`},"API Key"),de.default.createElement("code",{className:`block truncate text-sm font-mono px-3 py-2 rounded-lg border ${a?"bg-gray-900 border-gray-700 text-gray-300":"bg-gray-50 border-gray-200 text-gray-700"}`},e?.telemetry_api_key?u?e.telemetry_api_key:"\u2022".repeat(24):"Not available")),de.default.createElement("div",{className:"flex gap-2 flex-shrink-0 self-end"},de.default.createElement("button",{onClick:()=>c(!u),className:"px-3 py-2 text-sm bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors"},u?"Hide":"Show"),de.default.createElement("button",{onClick:b,className:"px-3 py-2 text-sm bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors"},l?"Copied!":"Copy")))))}var le=te(ae());var fj={openai:{label:"OpenAI",icon:"\u{1F916}",placeholder:"sk-\u2026"},anthropic:{label:"Anthropic",icon:"\u{1F9E0}",placeholder:"sk-ant-\u2026"},openrouter:{label:"OpenRouter",icon:"\u{1F500}",placeholder:"sk-or-\u2026"},ollama:{label:"Ollama",icon:"\u{1F999}",placeholder:"http://localhost:11434/v1"}};function rA({user:e,account:t}){let{darkMode:r,toggleDarkMode:n}=pt(),[a,i]=(0,le.useState)("profile"),[o,l]=(0,le.useState)([]),[s,u]=(0,le.useState)([]),[c,d]=(0,le.useState)(!1),[f,p]=(0,le.useState)(""),[v,h]=(0,le.useState)(!1),[b,m]=(0,le.useState)(null),[g,x]=(0,le.useState)(!1),[A,k]=(0,le.useState)(null),[_,M]=(0,le.useState)(null),[E,L]=(0,le.useState)(""),[I,j]=(0,le.useState)(!1),z=(0,le.useCallback)(async()=>{try{let[N,T]=await Promise.all([fetch("/api/api_keys"),fetch("/api/provider_keys")]);if(!N.ok||!T.ok)throw new Error("Failed to load keys");let $=await N.json(),Y=await T.json();l($.api_keys||[]),u(Y.provider_keys||[]),k(null)}catch{k("Could not load API keys. Are you signed in?")}finally{d(!0)}},[]);(0,le.useEffect)(()=>{a==="api-keys"&&!c&&z()},[a,c,z]);let X=async()=>{if(!(!f.trim()||v)){h(!0),k(null);try{let N=await fetch("/api/api_keys",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:f.trim()})});if(!N.ok)throw new Error("create failed");let T=await N.json();m(T.api_key),x(!1),p(""),await z()}catch{k("Could not create the API key.")}finally{h(!1)}}},R=async N=>{if(window.confirm("Revoke this API key? Requests using it will stop working immediately."))try{if(!(await fetch(`/api/api_keys/${N}`,{method:"DELETE"})).ok)throw new Error("delete failed");b&&b.id===N&&m(null),await z()}catch{k("Could not revoke the API key.")}},Q=async N=>{if(!(!E.trim()||I)){j(!0),k(null);try{let T=await fetch("/api/provider_keys",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:N,credential:E.trim()})});if(!T.ok){let $=await T.json().catch(()=>({}));throw new Error(Array.isArray($.error)?$.error.join(", "):"save failed")}M(null),L(""),await z()}catch(T){k(`Could not save the ${fj[N]?.label||N} credential${T.message!=="save failed"?`: ${T.message}`:"."}`)}finally{j(!1)}}},y=async N=>{if(window.confirm("Remove this provider credential? Runs will fall back to the platform default credentials, or fail until a key is configured."))try{if(!(await fetch(`/api/provider_keys/${N}`,{method:"DELETE"})).ok)throw new Error("delete failed");await z()}catch{k("Could not remove the provider credential.")}},C=async N=>{try{await navigator.clipboard.writeText(N),x(!0),setTimeout(()=>x(!1),2e3)}catch{}},O={backgroundColor:r?"#1f1f1f":"#ffffff",borderColor:r?"#2a2a2a":"#e5e7eb"};return le.default.createElement("div",{className:"p-6 space-y-6"},le.default.createElement("div",null,le.default.createElement("h2",{className:`text-2xl font-bold ${r?"text-white":"text-gray-900"}`},"Settings"),le.default.createElement("p",{className:`mt-1 ${r?"text-gray-400":"text-gray-500"}`},"Manage your account and preferences")),le.default.createElement("div",{className:"border-b",style:{borderColor:r?"#2a2a2a":"#e5e7eb"}},le.default.createElement("nav",{className:"flex space-x-8"},[{id:"profile",label:"Profile"},{id:"api-keys",label:"API Keys"},{id:"notifications",label:"Notifications"},{id:"billing",label:"Billing"}].map(N=>le.default.createElement("button",{key:N.id,onClick:()=>i(N.id),className:`py-4 px-1 border-b-2 font-medium text-sm transition-colors ${a===N.id?"border-red-500 text-red-500":`border-transparent ${r?"text-gray-400 hover:text-gray-300":"text-gray-500 hover:text-gray-700"}`}`},N.label)))),a==="profile"&&le.default.createElement("div",{className:"space-y-6"},le.default.createElement("div",{className:"border rounded-lg p-6",style:O},le.default.createElement("h3",{className:`text-lg font-semibold mb-4 ${r?"text-white":"text-gray-900"}`},"Profile Information"),le.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4"},le.default.createElement("div",null,le.default.createElement("label",{className:`block text-sm font-medium mb-1 ${r?"text-gray-300":"text-gray-700"}`},"First Name"),le.default.createElement("input",{type:"text",defaultValue:e?.name?.split(" ")[0]||"",className:`w-full px-3 py-2 border rounded-lg ${r?"bg-gray-800 border-gray-700 text-white":"bg-white border-gray-300 text-gray-900"}`})),le.default.createElement("div",null,le.default.createElement("label",{className:`block text-sm font-medium mb-1 ${r?"text-gray-300":"text-gray-700"}`},"Last Name"),le.default.createElement("input",{type:"text",defaultValue:e?.name?.split(" ")[1]||"",className:`w-full px-3 py-2 border rounded-lg ${r?"bg-gray-800 border-gray-700 text-white":"bg-white border-gray-300 text-gray-900"}`})),le.default.createElement("div",{className:"md:col-span-2"},le.default.createElement("label",{className:`block text-sm font-medium mb-1 ${r?"text-gray-300":"text-gray-700"}`},"Email"),le.default.createElement("input",{type:"email",defaultValue:e?.email||"",disabled:!0,className:`w-full px-3 py-2 border rounded-lg cursor-not-allowed ${r?"bg-gray-900 border-gray-700 text-gray-500":"bg-gray-100 border-gray-300 text-gray-500"}`}))),le.default.createElement("button",{className:"mt-4 px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600"},"Save Changes")),le.default.createElement("div",{className:"border rounded-lg p-6",style:O},le.default.createElement("h3",{className:`text-lg font-semibold mb-4 ${r?"text-white":"text-gray-900"}`},"Appearance"),le.default.createElement("div",{className:"flex items-center justify-between"},le.default.createElement("div",null,le.default.createElement("p",{className:`font-medium ${r?"text-white":"text-gray-900"}`},"Dark Mode"),le.default.createElement("p",{className:`text-sm ${r?"text-gray-400":"text-gray-500"}`},"Use dark theme across the dashboard")),le.default.createElement("button",{onClick:n,className:`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${r?"bg-red-500":"bg-gray-300"}`},le.default.createElement("span",{className:`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${r?"translate-x-6":"translate-x-1"}`}))))),a==="api-keys"&&le.default.createElement("div",{className:"space-y-6"},le.default.createElement("div",{className:"border rounded-lg p-6",style:O},le.default.createElement("div",{className:"flex items-center justify-between mb-4"},le.default.createElement("h3",{className:`text-lg font-semibold ${r?"text-white":"text-gray-900"}`},"API Keys"),le.default.createElement("div",{className:"flex items-center space-x-2"},le.default.createElement("input",{type:"text",value:f,onChange:N=>p(N.target.value),onKeyDown:N=>N.key==="Enter"&&X(),placeholder:"Key name (e.g. production)",className:`px-3 py-2 border rounded-lg text-sm ${r?"bg-gray-800 border-gray-700 text-white placeholder-gray-500":"bg-white border-gray-300 text-gray-900"}`}),le.default.createElement("button",{onClick:X,disabled:!f.trim()||v,className:"px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 disabled:opacity-50 whitespace-nowrap"},v?"Creating\u2026":"+ Create New Key"))),le.default.createElement("div",{className:`p-4 rounded-lg mb-4 ${r?"bg-yellow-900/20 border border-yellow-800":"bg-yellow-50 border border-yellow-200"}`},le.default.createElement("p",{className:`text-sm ${r?"text-yellow-300":"text-yellow-800"}`},"\u26A0\uFE0F API keys are secrets. Never share them or commit them to version control.")),A&&le.default.createElement("div",{className:`p-4 rounded-lg mb-4 text-sm ${r?"bg-red-900/20 border border-red-800 text-red-300":"bg-red-50 border border-red-200 text-red-700"}`},A),b&&le.default.createElement("div",{className:`p-4 rounded-lg mb-4 border ${r?"bg-green-900/20 border-green-800":"bg-green-50 border-green-200"}`},le.default.createElement("p",{className:`text-sm font-medium mb-2 ${r?"text-green-300":"text-green-800"}`},"Key \u201C",b.name,"\u201D created. Copy it now \u2014 it won't be shown again."),le.default.createElement("div",{className:"flex items-center space-x-2"},le.default.createElement("code",{className:`flex-1 px-3 py-2 rounded text-sm font-mono break-all ${r?"bg-gray-900 text-green-300":"bg-white text-green-800 border border-green-200"}`},b.token),le.default.createElement("button",{onClick:()=>C(b.token),className:`px-3 py-2 text-sm rounded ${r?"bg-gray-700 text-gray-200 hover:bg-gray-600":"bg-gray-200 text-gray-700 hover:bg-gray-300"}`},g?"Copied!":"Copy"),le.default.createElement("button",{onClick:()=>m(null),className:`px-3 py-2 text-sm rounded ${r?"text-gray-400 hover:text-gray-200":"text-gray-500 hover:text-gray-700"}`},"Dismiss"))),o.length===0?le.default.createElement("div",{className:`text-center py-8 ${r?"text-gray-400":"text-gray-500"}`},le.default.createElement("p",null,c?"No API keys yet":"Loading\u2026"),c&&le.default.createElement("p",{className:"text-sm"},"Create your first API key to authenticate requests")):le.default.createElement("div",{className:"space-y-2"},o.map(N=>le.default.createElement("div",{key:N.id,className:"flex items-center justify-between p-4 rounded-lg",style:{backgroundColor:r?"#252525":"#f9fafb"}},le.default.createElement("div",null,le.default.createElement("p",{className:`font-medium ${r?"text-white":"text-gray-900"}`},N.name),le.default.createElement("p",{className:`text-sm font-mono ${r?"text-gray-400":"text-gray-500"}`},N.masked_token,le.default.createElement("span",{className:"font-sans"}," \xB7 created ",new Date(N.created_at).toLocaleDateString(),N.last_used_at?` \xB7 last used ${new Date(N.last_used_at).toLocaleDateString()}`:" \xB7 never used"))),le.default.createElement("button",{onClick:()=>R(N.id),className:`px-3 py-1 text-sm rounded ${r?"bg-red-900/40 text-red-300 hover:bg-red-900/60":"bg-red-50 text-red-600 hover:bg-red-100"}`},"Revoke"))))),le.default.createElement("div",{className:"border rounded-lg p-6",style:O},le.default.createElement("h3",{className:`text-lg font-semibold mb-4 ${r?"text-white":"text-gray-900"}`},"Provider API Keys"),le.default.createElement("p",{className:`text-sm mb-4 ${r?"text-gray-400":"text-gray-500"}`},"Configure your own LLM provider credentials. Agent runs and evaluations on this account use these instead of the platform defaults. Keys are encrypted at rest."),le.default.createElement("div",{className:"space-y-4"},s.map(({provider:N,host_based:T,configured:$,hint:Y})=>{let J=fj[N]||{label:N,icon:"\u{1F511}",placeholder:""},fe=_===N;return le.default.createElement("div",{key:N,className:"p-4 rounded-lg",style:{backgroundColor:r?"#252525":"#f9fafb"}},le.default.createElement("div",{className:"flex items-center justify-between"},le.default.createElement("div",{className:"flex items-center space-x-3"},le.default.createElement("span",{className:"text-xl"},J.icon),le.default.createElement("div",null,le.default.createElement("p",{className:`font-medium ${r?"text-white":"text-gray-900"}`},J.label),le.default.createElement("p",{className:`text-sm ${$?r?"text-green-400":"text-green-600":r?"text-gray-400":"text-gray-500"}`},$?T?Y:`Configured (${Y})`:"Not configured"))),le.default.createElement("div",{className:"flex items-center space-x-2"},$&&!fe&&le.default.createElement("button",{onClick:()=>y(N),className:`px-3 py-1 text-sm rounded ${r?"text-red-300 hover:bg-red-900/40":"text-red-600 hover:bg-red-50"}`},"Remove"),le.default.createElement("button",{onClick:()=>{M(fe?null:N),L("")},className:`px-3 py-1 text-sm rounded ${r?"bg-gray-700 text-gray-300 hover:bg-gray-600":"bg-gray-200 text-gray-700 hover:bg-gray-300"}`},fe?"Cancel":$?"Update":"Configure"))),fe&&le.default.createElement("div",{className:"mt-3 flex items-center space-x-2"},le.default.createElement("input",{type:T?"text":"password",value:E,onChange:pe=>L(pe.target.value),onKeyDown:pe=>pe.key==="Enter"&&Q(N),placeholder:T?J.placeholder:`${J.label} API key (${J.placeholder})`,autoFocus:!0,className:`flex-1 px-3 py-2 border rounded-lg text-sm font-mono ${r?"bg-gray-800 border-gray-700 text-white placeholder-gray-500":"bg-white border-gray-300 text-gray-900"}`}),le.default.createElement("button",{onClick:()=>Q(N),disabled:!E.trim()||I,className:"px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 disabled:opacity-50 text-sm"},I?"Saving\u2026":"Save")),T&&fe&&le.default.createElement("p",{className:`mt-2 text-xs ${r?"text-gray-500":"text-gray-400"}`},"URL of your Ollama server's OpenAI-compatible endpoint. For a locally running Ollama use http://localhost:11434/v1 (the hosted platform needs a publicly reachable URL, e.g. a tunnel)."))})))),a==="notifications"&&le.default.createElement("div",{className:"border rounded-lg p-6",style:O},le.default.createElement("h3",{className:`text-lg font-semibold mb-4 ${r?"text-white":"text-gray-900"}`},"Email Notifications"),le.default.createElement("div",{className:"space-y-4"},[{label:"Agent execution alerts",desc:"Get notified when agents fail or complete",enabled:!0},{label:"Weekly usage reports",desc:"Summary of traces, costs, and performance",enabled:!0},{label:"Security alerts",desc:"Important security notifications",enabled:!0},{label:"Product updates",desc:"New features and improvements",enabled:!1}].map((N,T)=>le.default.createElement("div",{key:T,className:"flex items-center justify-between py-3 border-b",style:{borderColor:r?"#2a2a2a":"#e5e7eb"}},le.default.createElement("div",null,le.default.createElement("p",{className:`font-medium ${r?"text-white":"text-gray-900"}`},N.label),le.default.createElement("p",{className:`text-sm ${r?"text-gray-400":"text-gray-500"}`},N.desc)),le.default.createElement("button",{className:`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${N.enabled?"bg-red-500":r?"bg-gray-700":"bg-gray-300"}`},le.default.createElement("span",{className:`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${N.enabled?"translate-x-6":"translate-x-1"}`})))))),a==="billing"&&le.default.createElement("div",{className:"space-y-6"},le.default.createElement("div",{className:"border rounded-lg p-6",style:O},le.default.createElement("h3",{className:`text-lg font-semibold mb-4 ${r?"text-white":"text-gray-900"}`},"Subscription"),le.default.createElement("div",{className:"flex items-center justify-between p-4 rounded-lg",style:{backgroundColor:r?"#252525":"#f9fafb"}},le.default.createElement("div",null,le.default.createElement("p",{className:`font-medium ${r?"text-white":"text-gray-900"}`},"Free Plan"),le.default.createElement("p",{className:`text-sm ${r?"text-gray-400":"text-gray-500"}`},"Current plan")),le.default.createElement("button",{className:"px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600"},"Upgrade to Pro"))),le.default.createElement("div",{className:"border rounded-lg p-6",style:O},le.default.createElement("h3",{className:`text-lg font-semibold mb-4 ${r?"text-white":"text-gray-900"}`},"Payment Method"),le.default.createElement("p",{className:`${r?"text-gray-400":"text-gray-500"}`},"No payment method on file"),le.default.createElement("button",{className:`mt-4 px-4 py-2 rounded-lg ${r?"bg-gray-700 text-gray-300":"bg-gray-200 text-gray-700"}`},"Add Payment Method")),le.default.createElement("div",{className:"border rounded-lg p-6",style:O},le.default.createElement("h3",{className:`text-lg font-semibold mb-4 ${r?"text-white":"text-gray-900"}`},"Invoices"),le.default.createElement("p",{className:`${r?"text-gray-400":"text-gray-500"}`},"No invoices yet"))))}function lQ({user:e,initialAgents:t=[],meta:r={},account:n=null,subscription:a=null}){let{darkMode:i}=pt(),[o,l]=(0,He.useState)(t),[s,u]=(0,He.useState)("list"),[c,d]=(0,He.useState)(null),[f,p]=(0,He.useState)(!1),[v,h]=(0,He.useState)(null),[b,m]=(0,He.useState)(!1),[g,x]=(0,He.useState)("recent");(0,He.useEffect)(()=>{let y=()=>{let C=window.location.pathname;if(C.includes("/traces"))u("traces");else if(C.includes("/metrics"))u("metrics");else if(C.includes("/interactions")&&!C.includes("/agents/"))u("interactions");else if(C.includes("/evaluations"))u("evaluations");else if(C.includes("/analytics")&&!C.includes("/agents/"))u("analytics");else if(C.includes("/agents/new"))u("builder");else if(C.match(/\/agents\/\d+\/(interactions|history)/)){let O=C.match(/\/agents\/(\d+)/)?.[1];C.includes("/history")&&window.history.replaceState({},"",C.replace("/history","/interactions")),O&&A(O,"history")}else if(C.match(/\/agents\/\d+\/analytics/)){let O=C.match(/\/agents\/(\d+)/)?.[1];O&&A(O,"agent-analytics")}else if(C.match(/\/agents\/\d+\/edit/)){let O=C.match(/\/agents\/(\d+)/)?.[1];O&&A(O,"editor")}else if(C.match(/\/agents\/\d+\/run/)){let O=C.match(/\/agents\/(\d+)/)?.[1];O&&A(O,"runner")}else if(C.match(/\/agents\/\d+\/?$/)){let O=C.match(/\/agents\/(\d+)/)?.[1];O&&A(O,"history")}else C.includes("/benchmarks")?u("benchmarks"):C.includes("/replay")?u("replay"):C.includes("/sandbox")||C.includes("/demo")?u("sandbox"):C.includes("/organization")?u("organization"):C.includes("/settings")?u("settings"):u("list")};return y(),window.addEventListener("popstate",y),window.addEventListener("dashboard:navigate",y),()=>{window.removeEventListener("popstate",y),window.removeEventListener("dashboard:navigate",y)}},[]);let A=async(y,C)=>{p(!0);try{let D=await(await fetch(`/api/agents/${y}`)).json();d(D.agent),u(C)}catch{M("Failed to load agent","error")}finally{p(!1)}},k=async(y=g)=>{p(!0);try{let O=await(await fetch(`/api/agents?sort=${y}`)).json();l(O.agents)}catch{M("Failed to refresh agents","error")}finally{p(!1)}},_=y=>{x(y),k(y)},M=(y,C="info")=>{h({message:y,type:C}),setTimeout(()=>h(null),3e3)},E=async y=>{p(!0);try{let C=await fetch("/api/agents",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:y})});if(C.ok){let O=await C.json();l([O.agent,...o]),d(O.agent),u("editor"),M("Agent created successfully!","success"),window.history.pushState({},"",`/dashboard/agents/${O.agent.id}/edit`)}else{let O=await C.json();M(O.errors?.join(", ")||"Failed to create agent","error")}}catch{M("Failed to create agent","error")}finally{p(!1)}},L=async(y,C)=>{p(!0);try{let O=await fetch(`/api/agents/${y}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:C})});if(O.ok){let D=await O.json();l(o.map(N=>N.id===y?D.agent:N)),d(D.agent),M("Agent updated!","success")}else{let D=await O.json();M(D.errors?.join(", ")||"Failed to update agent","error")}}catch{M("Failed to update agent","error")}finally{p(!1)}},I=async y=>{if(confirm("Are you sure you want to delete this agent?")){p(!0);try{(await fetch(`/api/agents/${y}`,{method:"DELETE"})).ok&&(l(o.filter(O=>O.id!==y)),d(null),u("list"),M("Agent deleted","success"),window.history.pushState({},"","/dashboard"))}catch{M("Failed to delete agent","error")}finally{p(!1)}}},j=async y=>{p(!0);try{let C=await fetch(`/api/agents/${y}/duplicate`,{method:"POST"});if(C.ok){let O=await C.json();l([O.agent,...o]),M("Agent duplicated!","success")}}catch{M("Failed to duplicate agent","error")}finally{p(!1)}},z=y=>{l([y,...o]),d(y),u("editor"),m(!1),M("Agent created from template!","success"),window.history.pushState({},"",`/dashboard/agents/${y.id}/edit`)},X=["editor","runner","agent-analytics","history"],R=(y,C=null)=>{C?.id&&X.includes(y)&&C.instructions===void 0?A(C.id,y):(d(C),u(y));let O="/dashboard";y==="builder"?O="/dashboard/agents/new":y==="editor"&&C?O=`/dashboard/agents/${C.id}/edit`:y==="runner"&&C?O=`/dashboard/agents/${C.id}/run`:y==="agent-analytics"&&C?O=`/dashboard/agents/${C.id}/analytics`:y==="history"&&C?O=`/dashboard/agents/${C.id}/interactions`:y==="analytics"?O="/dashboard/analytics":y==="traces"?O="/dashboard/traces":y==="metrics"?O="/dashboard/metrics":y==="interactions"?O="/dashboard/interactions":y==="evaluations"?O="/dashboard/evaluations":y==="benchmarks"?O="/dashboard/benchmarks":y==="replay"?O="/dashboard/replay":y==="sandbox"?O="/dashboard/sandbox":y==="organization"?O="/dashboard/organization":y==="settings"&&(O="/dashboard/settings"),window.history.pushState({},"",O)},Q=()=>{switch(s){case"builder":return He.default.createElement(Sb,{meta:r,onSave:E,onCancel:()=>R("list"),isLoading:f});case"editor":return c?He.default.createElement(wg,{key:`editor-${c.id}`,agent:c,meta:r,onSave:y=>L(c.id,y),onDelete:()=>I(c.id),onRun:()=>R("runner",c),onDuplicate:()=>j(c.id),onRunReport:()=>R("history",c),onBack:()=>R("list"),isLoading:f}):null;case"runner":return c?He.default.createElement(US,{agent:c,onBack:()=>R("editor",c)}):null;case"agent-analytics":return c?He.default.createElement(wg,{key:`agent-analytics-${c.id}`,agent:c,meta:r,initialTab:"metrics",onSave:y=>L(c.id,y),onDelete:()=>I(c.id),onRun:()=>R("runner",c),onDuplicate:()=>j(c.id),onRunReport:()=>R("history",c),onBack:()=>R("list"),isLoading:f}):null;case"history":return c?He.default.createElement(HS,{agent:c,onBack:()=>R("editor",c)}):null;case"analytics":return He.default.createElement($S,{onSelectAgent:y=>{A(y.id,"agent-analytics")}});case"traces":return He.default.createElement(Wl,null);case"metrics":return He.default.createElement(WS,null);case"interactions":return He.default.createElement(Mi,null);case"evaluations":return He.default.createElement(Ff,null);case"replay":return He.default.createElement(eA,{onHandoff:y=>{M("Taking over session...","info"),R("sandbox")},onClose:()=>R("list")});case"sandbox":return He.default.createElement(JS,{initialType:"playwright_mcp",onClose:()=>R("list")});case"organization":return He.default.createElement(tA,{user:e,account:n,subscription:a,agentCount:o.length});case"settings":return He.default.createElement(rA,{user:e});default:return He.default.createElement(wb,{agents:o,meta:r,onSelect:y=>R("editor",y),onNew:()=>R("builder"),onBrowseTemplates:()=>m(!0),onDuplicate:j,onDelete:I,onRefresh:k,sort:g,onSortChange:_,isLoading:f})}};return He.default.createElement("div",{className:"min-h-screen flex",style:{backgroundColor:i?"#0f0f0f":"#f9fafb"}},He.default.createElement(FS,{currentView:s,onNavigate:R,agentCount:o.length,account:n,user:e,gemVersion:r.activeagentVersion}),He.default.createElement("div",{className:"flex-1 flex flex-col"},He.default.createElement(qS,{user:e,account:n}),He.default.createElement("main",{className:"flex-1 p-6 overflow-auto"},Q())),b&&He.default.createElement(VS,{onUseTemplate:z,onClose:()=>m(!1)}),v&&He.default.createElement("div",{className:`fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg transition-all transform ${v.type==="error"?"bg-red-500":v.type==="success"?"bg-green-500":"bg-blue-500"} text-white`},v.message),f&&He.default.createElement("div",{className:"fixed inset-0 bg-black bg-opacity-20 flex items-center justify-center z-50"},He.default.createElement("div",{className:`rounded-lg p-4 shadow-xl ${i?"bg-gray-800":"bg-white"}`},He.default.createElement("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-red-500"}))))}function nA(e){return He.default.createElement(rE,null,He.default.createElement(GP,null,He.default.createElement(lQ,{...e})))}var sQ="active-agent-dashboard";function uQ(e){let t=e.replace(/\/$/,"");if(!t)return;let r=window.fetch.bind(window);window.fetch=(n,a)=>{if(typeof n=="string"&&n.startsWith("/api/"))return r(`${t}${n}`,a);if(n instanceof Request&&new URL(n.url,window.location.origin).pathname.startsWith("/api/")){let i=new URL(n.url,window.location.origin);return r(new Request(`${t}${i.pathname}${i.search}`,n),a)}return r(n,a)}}function pj(){let e=document.getElementById(sQ);if(!e)return;let t=JSON.parse(e.dataset.props||"{}");window.ACTIVE_AGENT_DASHBOARD=t,uQ(t.mountPath||"/"),(0,vj.createRoot)(e).render(mj.default.createElement(nA,{...t}))}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",pj):pj(); +/*! Bundled license information: + +react/cjs/react.production.js: + (** + * @license React + * react.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +scheduler/cjs/scheduler.production.js: + (** + * @license React + * scheduler.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +react-dom/cjs/react-dom.production.js: + (** + * @license React + * react-dom.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +react-dom/cjs/react-dom-client.production.js: + (** + * @license React + * react-dom-client.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +use-sync-external-store/cjs/use-sync-external-store-shim.production.js: + (** + * @license React + * use-sync-external-store-shim.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.js: + (** + * @license React + * use-sync-external-store-shim/with-selector.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +use-sync-external-store/cjs/use-sync-external-store-with-selector.production.js: + (** + * @license React + * use-sync-external-store-with-selector.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +decimal.js-light/decimal.js: + (*! decimal.js-light v2.5.1 https://github.com/MikeMcl/decimal.js-light/LICENCE *) +*/ diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/agent_runs_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/agent_runs_controller.rb new file mode 100644 index 00000000..a92c01bb --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/agent_runs_controller.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + class AgentRunsController < BaseController + before_action :set_run, only: [ :show, :cancel ] + + # GET /api/runs/:id + def show + render json: { + run: run_json(@run), + messages: interaction_messages(@run), + agent: { + id: @run.agent.id, + name: @run.agent.name, + slug: @run.agent.slug + } + } + end + + # POST /api/runs/:id/cancel + def cancel + @run.cancel! + render json: { run: @run.summary } + end + + # GET /api/runs + def index + # Scoped to the caller's own agents: this listed (and counted) every + # run in the database regardless of who owned it. + scope = AgentRun.includes(:agent).where(agent: owner_agents).recent + + scope = scope.where(agent_id: params[:agent_id]) if params[:agent_id].present? + scope = scope.where(status: params[:status]) if params[:status].present? + + page = (params[:page] || 1).to_i + per_page = (params[:per_page] || 20).to_i + total = scope.count + runs = scope.offset((page - 1) * per_page).limit(per_page) + + render json: { + runs: runs.map { |run| run_json(run, include_agent: true) }, + meta: { + page: page, + per_page: per_page, + total: total + } + } + end + + private + + def set_run + @run = AgentRun.where(agent: owner_agents).find(params[:id]) + end + + # The run's slice of its agent's conversation stream, for the shared + # InteractionStream UI. User messages carry the run's trace_id in + # provenance (SolidAgent::HasContext); the slice spans from this run's + # user message up to the next user message with a different trace_id. + # Prepended with the system instructions the run executed under + # (captured in output_metadata; falls back to the agent's current ones). + def interaction_messages(run) + return [] if run.trace_id.blank? + + context = AgentContext.for_agents(Agent.where(id: run.agent_id)).order(created_at: :desc).first + return [] unless context + + messages = context.messages.chronological.to_a + start_index = messages.index do |message| + message.role == "user" && message.provenance&.dig("trace_id") == run.trace_id + end + return [] unless start_index + + slice = [ messages[start_index] ] + messages[(start_index + 1)..].each do |message| + trace = message.provenance&.dig("trace_id") + break if message.role == "user" && trace.present? && trace != run.trace_id + + slice << message + end + + serialized = slice.map { |message| AgentMessageSerializer.call(message) } + instructions = run.output_metadata&.dig("instructions").presence || run.agent.instructions + if instructions.present? + serialized.unshift( + id: "run-#{run.id}-system", + role: "system", + content: instructions, + created_at: (run.started_at || run.created_at).iso8601(3) + ) + end + serialized + end + + def run_json(run, include_agent: false) + json = { + id: run.id, + status: run.status, + input_prompt: run.input_prompt, + input_params: run.input_params, + output: run.output, + output_metadata: run.output_metadata, + duration_ms: run.calculated_duration_ms, + input_tokens: run.input_tokens, + output_tokens: run.output_tokens, + total_tokens: run.total_tokens, + error_message: run.error_message, + trace_id: run.trace_id, + logs: run.logs, + started_at: run.started_at, + completed_at: run.completed_at, + created_at: run.created_at + } + + if include_agent + json[:agent] = { + id: run.agent.id, + name: run.agent.name, + slug: run.agent.slug + } + end + + json + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/agents_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/agents_controller.rb new file mode 100644 index 00000000..026ee5fb --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/agents_controller.rb @@ -0,0 +1,413 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + class AgentsController < BaseController + # Ranking for the agent cards. Every dimension except "recent" reads the + # scorecard, which is computed in Ruby over both execution sources, so + # the ordering is applied there rather than in the SQL scope. + LIST_SORTS = { + "recent" => "Recently updated", + "popular" => "Most runs", + "longest" => "Longest average", + "cost" => "Highest cost", + "tokens" => "Most tokens" + }.freeze + DEFAULT_LIST_SORT = "recent" + + before_action :set_agent, only: [ :show, :update, :destroy, :versions, :runs, :execute, :test, :restore, :duplicate, :export, :analytics ] + before_action :require_execution_enabled!, only: [ :execute, :test ] + before_action :require_owner!, only: [ :execute, :test ] + before_action :enforce_execution_quota!, only: [ :execute, :test ] + + # GET /api/agents + def index + @agents = owner_agents.order(updated_at: :desc) + + # Filter by status + @agents = @agents.where(status: params[:status]) if params[:status].present? + + # Filter by provider + @agents = @agents.where(provider: params[:provider]) if params[:provider].present? + + # Search by name + # LOWER(...) LIKE rather than ILIKE: the dashboard is not + # PostgreSQL-only. + if params[:q].present? + @agents = @agents.where("LOWER(name) LIKE ?", "%#{params[:q].to_s.downcase}%") + end + + scorecards = AgentScorecard.for_agents(@agents) + cards = sort_cards( + @agents.map { |agent| agent_json(agent).merge(stats: scorecards[agent.id]) }, + params[:sort] + ) + + render json: { + agents: cards, + meta: { + total: cards.size, + sorts: LIST_SORTS, + sort: list_sort(params[:sort]), + providers: Agent::PROVIDERS, + preset_types: Agent::PRESET_TYPES, + instruction_sets: Agent::INSTRUCTION_SETS, + available_tools: Agent::AVAILABLE_TOOLS + } + } + end + + # GET /api/agents/:id + def show + render json: { + agent: agent_json(@agent, include_details: true), + versions: @agent.agent_versions.recent.limit(10).map { |v| version_json(v) }, + recent_runs: @agent.agent_runs.recent.limit(5).map(&:summary) + } + end + + # POST /api/agents + def create + @agent = owner_agents.build(agent_params) + + if @agent.save + render json: { agent: agent_json(@agent, include_details: true) }, status: :created + else + render json: { errors: @agent.errors.full_messages }, status: :unprocessable_entity + end + end + + # PATCH /api/agents/:id + def update + if @agent.update(agent_params) + render json: { agent: agent_json(@agent, include_details: true) } + else + render json: { errors: @agent.errors.full_messages }, status: :unprocessable_entity + end + end + + # DELETE /api/agents/:id + def destroy + @agent.destroy + render json: { success: true } + end + + # GET /api/agents/:id/versions + def versions + @versions = @agent.agent_versions.recent + + render json: { + versions: @versions.map { |v| version_json(v, include_diff: true) } + } + end + + # POST /api/agents/:id/restore + def restore + version = @agent.agent_versions.find(params[:version_id]) + @agent.restore_from_version!(version) + + render json: { agent: agent_json(@agent, include_details: true) } + end + + # GET /api/agents/:id/runs + # Every execution of this agent, whoever ran it: dashboard runs and + # SDK-reported traces in one list, discriminated by `source`. Agents + # observed from telemetry have no AgentRun rows at all, so a runs-only + # list showed them as empty while their scorecard reported real traffic. + def runs + minutes = params[:minutes].presence&.then { |m| m.to_i.clamp(1, 60 * 24 * 90) } + page = (params[:page] || 1).to_i + per_page = (params[:per_page] || 20).to_i + + executions = AgentExecutions.new( + agents: [ @agent ], + owner: current_owner, + window_minutes: minutes, + source: params[:source], + status: params[:status], + sort: params[:sort] + ).page(page: page, per_page: per_page) + + # One digest->version map for the page; labels each run's instructions + # with the agent version that introduced them where one matches. + digest_versions = @agent.instructions_digest_versions + runs_by_id = AgentRun.where(id: executions[:rows].select { |r| r.source == "dashboard" }.map(&:id)) + .index_by(&:id) + + render json: { + runs: executions[:rows].map { |row| serialize_execution(row, runs_by_id, digest_versions) }, + meta: { + page: page, + per_page: per_page, + total: executions[:total], + sources: AgentExecutions::SOURCES, + sorts: AgentExecutions::SORTS + } + } + end + + # POST /api/agents/:id/execute + def execute + run = @agent.execute( + params[:prompt], + action: params[:action_name], + **params.fetch(:params, {}).to_unsafe_h.symbolize_keys + ) + record_execution_usage + + render json: { run: run.summary }, status: :accepted + end + + # POST /api/agents/:id/test + def test + run = @agent.test_execute( + params[:prompt], + action: params[:action_name], + **params.fetch(:params, {}).to_unsafe_h.symbolize_keys + ) + record_execution_usage + + render json: { run: run.summary, output: run.output } + end + + # POST /api/agents/:id/duplicate + def duplicate + new_agent = @agent.dup + new_agent.name = "#{@agent.name} (Copy)" + new_agent.slug = nil # Will be auto-generated + new_agent.status = :draft + new_agent.save! + + render json: { agent: agent_json(new_agent, include_details: true) }, status: :created + end + + # GET /api/agents/:id/export + def export + render json: { + agent: agent_json(@agent, include_details: true), + code: @agent.to_agent_class_code, + manifest: { + name: @agent.slug, + version: "1.0.0", + model: "#{@agent.provider}/#{@agent.model}", + description: @agent.description, + instructions: @agent.instructions, + tools: @agent.tools, + config: @agent.model_config + } + } + end + + # GET /api/agents/:id/analytics + def analytics + days = (params[:days] || 30).to_i + start_date = days.days.ago.beginning_of_day + + runs = @agent.agent_runs.where("created_at >= ?", start_date) + + # Calculate stats + total_runs = runs.count + completed_runs = runs.where(status: :complete).count + failed_runs = runs.where(status: :failed).count + avg_duration = runs.where.not(duration_ms: nil).average(:duration_ms)&.round || 0 + total_tokens = runs.sum(:total_tokens) + avg_tokens = total_runs > 0 ? (total_tokens.to_f / total_runs).round : 0 + + # Runs by day + runs_by_day = runs.group("DATE(created_at)") + .select("DATE(created_at) as date, COUNT(*) as count, SUM(total_tokens) as tokens") + .order("date") + .map { |r| { date: r.date.to_s, count: r.count, tokens: r.tokens || 0 } } + + # Status breakdown + status_breakdown = runs.group(:status).count.transform_keys(&:to_s) + + # Recent errors + recent_errors = runs.failed_runs.recent.limit(5).map do |run| + { + id: run.id, + error: run.error_message&.truncate(200), + created_at: run.created_at + } + end + + render json: { + period_days: days, + summary: { + total_runs: total_runs, + completed_runs: completed_runs, + failed_runs: failed_runs, + success_rate: total_runs > 0 ? ((completed_runs.to_f / total_runs) * 100).round(1) : 0, + avg_duration_ms: avg_duration, + total_tokens: total_tokens, + avg_tokens_per_run: avg_tokens + }, + runs_by_day: runs_by_day, + status_breakdown: status_breakdown, + recent_errors: recent_errors + } + end + + # GET /api/agents/presets + def presets + presets = Agent::PRESET_TYPES.map do |preset| + { + id: preset, + name: preset.titleize, + appearance: default_appearance_for(preset), + suggested_tools: suggested_tools_for(preset), + suggested_instructions: suggested_instructions_for(preset) + } + end + + render json: { presets: presets } + end + + private + + def list_sort(requested) + LIST_SORTS.key?(requested.to_s) ? requested.to_s : DEFAULT_LIST_SORT + end + + # Descending on the chosen dimension. Agents with nothing to rank (no + # runs, no priced spend) sort last instead of interleaving as zeroes, + # and run count breaks ties. + def sort_cards(cards, requested) + sort = list_sort(requested) + return cards if sort == "recent" + + key = { "popular" => :runs, "longest" => :avg_duration_ms, "cost" => :cost, "tokens" => :tokens }[sort] + + cards.sort_by do |card| + value = card[:stats]&.dig(key) + [ value.nil? ? 1 : 0, -value.to_f, -card[:stats].to_h[:runs].to_i ] + end + end + + # Dashboard runs keep their full summary (logs, previews, instructions + # version); reported executions carry only what a trace knows. `source` + # tells the UI which it is holding. + def serialize_execution(row, runs_by_id, digest_versions) + run = runs_by_id[row.id] if row.source == "dashboard" + + if run + run.summary.merge( + source: "dashboard", + # Priced here rather than on AgentRun so both sources use one + # estimator, and so a run and its trace never quote different costs. + cost: row.cost, + instructions_version: digest_versions[run.instructions_digest] + ) + else + row.as_json.merge(id: row.to_param, record_id: row.id) + end + end + + # Same contract as Api::SandboxesController#run: 402 + usage stats so the + # frontend can show the upgrade prompt. + def set_agent + @agent = owner_agents.find(params[:id]) + end + + def agent_params + params.require(:agent).permit( + :name, :description, :provider, :model, :instructions, + :preset_type, :agent_class_name, :status, + appearance: {}, + action_prompts: [ :name, :prompt, :expose_as_tool ], + instruction_sets: [], + tools: [], + mcp_servers: [], + model_config: {}, + response_format: {} + ) + end + + def agent_json(agent, include_details: false) + json = { + id: agent.id, + name: agent.name, + slug: agent.slug, + description: agent.description, + provider: agent.provider, + model: agent.model, + status: agent.status, + preset_type: agent.preset_type, + appearance: agent.appearance, + version_count: agent.version_count, + created_at: agent.created_at, + updated_at: agent.updated_at + } + + if include_details + json.merge!( + instructions: agent.instructions, + action_prompts: agent.action_prompts, + instruction_sets: agent.instruction_sets, + tools: agent.tools, + mcp_servers: agent.mcp_servers, + model_config: agent.model_config, + response_format: agent.response_format, + agent_class_name: agent.agent_class_name, + telemetry_agent_class: agent.telemetry_agent_class + ) + end + + json + end + + def version_json(version, include_diff: false) + json = { + id: version.id, + version_number: version.version_number, + change_summary: version.change_summary, + created_by: version.created_by, + created_at: version.created_at, + is_latest: version.latest? + } + + if include_diff && version.previous + json[:diff] = version.diff(version.previous) + end + + json + end + + def default_appearance_for(preset) + appearances = { + "terminal" => { hat: "fedora", heldItem: "terminal", theme: "emerald" }, + "webDeveloper" => { hat: "safari", heldItem: "browser", theme: "blue" }, + "documentAnalysis" => { hat: "fedora", heldItem: "document", theme: "amber" }, + "writing" => { hat: "fedora", hatAccessory: "feather", heldItem: "scroll", theme: "purple" }, + "research" => { hat: "safari", heldItem: "magnifyingGlass", theme: "teal" }, + "playwright" => { hat: "fedora", hatAccessory: "theaterMasks", heldItem: "browser", theme: "rose" } + } + appearances[preset] || { theme: "default" } + end + + def suggested_tools_for(preset) + tools = { + "terminal" => %w[terminal filesystem code], + "webDeveloper" => %w[terminal filesystem code playwright], + "documentAnalysis" => %w[filesystem search], + "writing" => %w[edit translate], + "research" => %w[fetch search memory], + "playwright" => %w[playwright filesystem] + } + tools[preset] || [] + end + + def suggested_instructions_for(preset) + instructions = { + "terminal" => %w[github docker kubernetes], + "webDeveloper" => %w[github ruby rails typescript], + "research" => %w[github python], + "playwright" => %w[github typescript] + } + instructions[preset] || [] + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/analytics_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/analytics_controller.rb new file mode 100644 index 00000000..5f1038a1 --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/analytics_controller.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + class AnalyticsController < BaseController + # GET /api/analytics + def index + days = (params[:days] || 30).to_i + start_date = days.days.ago.beginning_of_day + + # Table names are interpolated rather than written literally: the + # engine's tables carry a configurable prefix. + runs_table = AgentRun.table_name + agents_table = ActiveAgent::Dashboard::Agent.table_name + + agents = owner_agents + runs = AgentRun.joins(:agent).where(agent: agents).where("#{runs_table}.created_at >= ?", start_date) + + # Overall stats + total_agents = agents.count + active_agents = agents.where(status: :active).count + total_runs = runs.count + completed_runs = runs.where(status: :complete).count + failed_runs = runs.where(status: :failed).count + + # Token usage + total_tokens = runs.sum(:total_tokens) + total_input_tokens = runs.sum(:input_tokens) + total_output_tokens = runs.sum(:output_tokens) + + # Average metrics + avg_duration = runs.where.not(duration_ms: nil).average(:duration_ms)&.round || 0 + avg_tokens_per_run = total_runs > 0 ? (total_tokens.to_f / total_runs).round : 0 + + # Runs over time + runs_by_day = runs.group("DATE(#{runs_table}.created_at)") + .select("DATE(#{runs_table}.created_at) as date, COUNT(*) as count") + .order("date") + .map { |r| { date: r.date.to_s, count: r.count } } + + # Token usage over time + tokens_by_day = runs.group("DATE(#{runs_table}.created_at)") + .select("DATE(#{runs_table}.created_at) as date, SUM(total_tokens) as tokens") + .order("date") + .map { |r| { date: r.date.to_s, tokens: r.tokens || 0 } } + + # Top agents by usage + top_agents = agents.joins(:agent_runs) + .where("#{runs_table}.created_at >= ?", start_date) + .group("#{agents_table}.id", "#{agents_table}.name", "#{agents_table}.slug") + .select( + "#{agents_table}.id, #{agents_table}.name, #{agents_table}.slug, " \ + "COUNT(#{runs_table}.id) as run_count, SUM(#{runs_table}.total_tokens) as total_tokens" + ) + .order("run_count DESC") + .limit(5) + .map { |a| { id: a.id, name: a.name, slug: a.slug, run_count: a.run_count, total_tokens: a.total_tokens || 0 } } + + # Status distribution + status_breakdown = runs.group(:status).count.transform_keys(&:to_s) + + # Provider distribution + provider_breakdown = runs.joins(:agent) + .group("#{agents_table}.provider") + .count + .transform_keys(&:to_s) + + render json: { + period_days: days, + summary: { + total_agents: total_agents, + active_agents: active_agents, + total_runs: total_runs, + completed_runs: completed_runs, + failed_runs: failed_runs, + success_rate: total_runs > 0 ? ((completed_runs.to_f / total_runs) * 100).round(1) : 0, + avg_duration_ms: avg_duration, + total_tokens: total_tokens, + total_input_tokens: total_input_tokens, + total_output_tokens: total_output_tokens, + avg_tokens_per_run: avg_tokens_per_run + }, + charts: { + runs_by_day: runs_by_day, + tokens_by_day: tokens_by_day + }, + top_agents: top_agents, + status_breakdown: status_breakdown, + provider_breakdown: provider_breakdown + } + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/api_keys_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/api_keys_controller.rb new file mode 100644 index 00000000..366aafee --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/api_keys_controller.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + # Platform API keys (Settings -> API Keys). The full token is returned + # exactly once — in the create response — and masked everywhere else. + class ApiKeysController < BaseController + before_action :require_owner! + + # GET /api/api_keys + def index + render json: { + api_keys: owned(ApiKey).order(created_at: :desc).map { |key| serialize(key) } + } + end + + # POST /api/api_keys + def create + api_key = owned(ApiKey).create!(name: params.require(:name)) + + render json: { + api_key: serialize(api_key).merge(token: api_key.token) + }, status: :created + end + + # DELETE /api/api_keys/:id + def destroy + owned(ApiKey).find(params[:id]).destroy! + head :no_content + end + + private + + def serialize(api_key) + { + id: api_key.id, + name: api_key.name, + masked_token: api_key.masked_token, + created_at: api_key.created_at.iso8601, + last_used_at: api_key.last_used_at&.iso8601 + } + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/base_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/base_controller.rb new file mode 100644 index 00000000..800f92b1 --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/base_controller.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + # Base class for the dashboard's JSON API — everything the React + # dashboard reads and writes. + # + # Authentication is the host app's (ActiveAgent::Dashboard + # .authentication_method), and so is ownership: `owned` scopes any + # dashboard relation to the signed-in user or account depending on what + # the model declared with `owned_by`, which is how a single-user + # install, a per-user install and a multi-tenant platform all read the + # same controllers. + # + # Note this is not the telemetry ingest endpoint — that authenticates + # with a bearer token and lives in Api::TracesController. + class BaseController < ActiveAgent::Dashboard::ApplicationController + skip_forgery_protection + + rescue_from ActiveRecord::RecordNotFound, with: :not_found + rescue_from ActiveRecord::RecordInvalid, with: :unprocessable_entity + rescue_from ActionController::ParameterMissing, with: :bad_request + + private + + # Scopes +relation+ to the caller, following the model's own + # declaration. Unowned models (a single-user install, or a model that + # nothing owns) come back unfiltered. + def owned(relation) + klass = relation.respond_to?(:klass) ? relation.klass : relation + + case klass.owner_association + when :account then relation.where(account_id: current_account&.id) + when :user then relation.where(user_id: current_user&.id) + else relation.all + end + end + + # The agents the caller can see. + def owner_agents + owned(ActiveAgent::Dashboard::Agent) + end + + # Reported traces visible to the caller. Scoped to the tenant in a + # multi-tenant install; every trace otherwise. + def owned_traces + ActiveAgent::Dashboard.trace_model.for_account(current_account) + end + + # The tenant, when the host app has one. + def current_account + return nil unless ActiveAgent::Dashboard.current_account_method + + send(ActiveAgent::Dashboard.current_account_method) + rescue NoMethodError + nil + end + + # Refuses the request when a multi-tenant install can't resolve a + # tenant. Single-tenant installs have nothing to check. + def require_owner! + return unless ActiveAgent::Dashboard.multi_tenant? + return if current_owner.present? + + render json: { error: "No account" }, status: :unauthorized + end + + # Asks the host app whether this owner may do +kind+ (:execution or + # :trace_ingest). Unlimited unless the app said otherwise. + def enforce_quota!(kind) + message = ActiveAgent::Dashboard.quota_denial(current_owner, kind) + return if message.nil? + + render json: { + error: "Plan limit reached", + upgrade_required: true, + message: message + }, status: :payment_required + end + + def enforce_execution_quota! = enforce_quota!(:execution) + + def record_execution_usage + ActiveAgent::Dashboard.record_usage(current_owner, :execution) + end + + # Execution can be turned off entirely, leaving a read-only + # observability dashboard. + def require_execution_enabled! + return if ActiveAgent::Dashboard.execution_enabled? + + render json: { error: "Agent execution is disabled on this dashboard" }, status: :forbidden + end + + def not_found + render json: { error: "Record not found" }, status: :not_found + end + + def unprocessable_entity(exception) + render json: { error: exception.record.errors.full_messages }, status: :unprocessable_entity + end + + def bad_request(exception) + render json: { error: exception.message }, status: :bad_request + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/evaluations_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/evaluations_controller.rb new file mode 100644 index 00000000..e7f65ec2 --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/evaluations_controller.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + # CRUD + execution for agent evaluations, backing the dashboard + # Evaluations view. Scoped to the current user's agents. + class EvaluationsController < BaseController + before_action :require_owner! + + # Default criteria used when none are supplied — all rule-based, so a + # new evaluation produces real scores without provider credentials. + DEFAULT_CRITERIA = [ + { "key" => "response_present", "type" => "response_present", "config" => {} }, + { "key" => "response_length", "type" => "min_length", "config" => { "chars" => 40 } }, + { "key" => "latency", "type" => "max_latency_ms", "config" => { "ms" => 5000 } }, + { "key" => "token_budget", "type" => "token_budget", "config" => { "output_tokens" => 1000 } } + ].freeze + + # GET /api/evaluations + # agent_id scopes to one agent. The filter has to happen before the limit: + # the agent page reads this endpoint, and filtering an account-wide page of + # 50 client-side hides an agent whose evaluations are not among the account's + # 50 most recent. The scope is already restricted to the current user's + # agents, so an id outside it simply returns nothing. + def index + scope = evaluations_scope + scope = scope.where(agent_id: params[:agent_id]) if params[:agent_id].present? + evaluations = scope.includes(:agent, :evaluation_runs).recent.limit(50) + + render json: { evaluations: evaluations.map { |evaluation| serialize(evaluation) } } + end + + # GET /api/evaluations/:id + def show + evaluation = evaluations_scope.find(params[:id]) + + render json: { + evaluation: serialize(evaluation).merge( + runs: evaluation.evaluation_runs.recent.limit(20).map { |run| serialize_run(run) } + ) + } + end + + # POST /api/evaluations + def create + agent = owner_agents.find(params.require(:evaluation)[:agent_id]) + + judge_kind = evaluation_params[:judge_kind].presence || "rules" + config = {} + config["compare_models"] = compare_models_param if compare_models_param.any? + + evaluation = agent.evaluations.new( + name: evaluation_params[:name], + judge_kind: judge_kind, + judge_model: evaluation_params[:judge_model], + sample_size: evaluation_params[:sample_size].presence || 20, + # judge_defined starts with no criteria — the judge authors the + # KPIs on the first run. + criteria: judge_kind == "judge_defined" ? explicit_criteria : normalized_criteria, + config: config + ) + + if evaluation.save + evaluation.run! + render json: { evaluation: serialize(evaluation.reload) }, status: :created + else + render json: { errors: evaluation.errors.full_messages }, status: :unprocessable_entity + end + end + + # POST /api/evaluations/:id/run + def run + evaluation = evaluations_scope.find(params[:id]) + run = evaluation.run! + + render json: { evaluation: serialize(evaluation.reload), run: serialize_run(run) } + end + + # DELETE /api/evaluations/:id + def destroy + evaluations_scope.find(params[:id]).destroy! + head :no_content + end + + private + + def evaluations_scope + Evaluation.joins(:agent).where(agent: owner_agents) + end + + def evaluation_params + params.require(:evaluation).permit(:agent_id, :name, :judge_kind, :judge_model, :sample_size) + end + + def normalized_criteria + explicit_criteria.presence || DEFAULT_CRITERIA.deep_dup + end + + def explicit_criteria + raw = params[:evaluation][:criteria] + return [] if raw.blank? + + raw.map do |criterion| + criterion.permit(:key, :type, config: {}).to_h.tap do |c| + c["key"] = c["key"].presence || c["type"] + c["config"] ||= {} + end + end + end + + def compare_models_param + Array(params[:evaluation][:compare_models]).map(&:to_s).reject(&:blank?) + end + + def serialize(evaluation) + latest = evaluation.latest_run + + { + id: evaluation.id, + name: evaluation.name, + agent: { id: evaluation.agent.id, name: evaluation.agent.name, slug: evaluation.agent.slug }, + judge_kind: evaluation.judge_kind, + judge_model: evaluation.judge_model, + criteria: evaluation.criteria, + compare_models: evaluation.compare_models, + config: evaluation.config, + sample_size: evaluation.sample_size, + created_at: evaluation.created_at.iso8601, + latest_run: latest ? serialize_run(latest) : nil + } + end + + def serialize_run(run) + { + id: run.id, + status: run.status, + scores: run.scores, + average_score: run.average_score, + samples_evaluated: run.samples_evaluated, + samples_passed: run.samples_passed, + error_message: run.error_message, + completed_at: run.completed_at&.iso8601, + created_at: run.created_at.iso8601 + } + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/instance_tiers_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/instance_tiers_controller.rb new file mode 100644 index 00000000..aa6df30b --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/instance_tiers_controller.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + class InstanceTiersController < ApplicationController + before_action :set_tier, only: [ :show ] + + # GET /api/instance_tiers + # List all available instance tiers + def index + tiers = SandboxInstanceTier.available + + # Filter by category if provided + if params[:category].present? + tiers = tiers.select { |t| t.category == params[:category] } + end + + # Filter by GPU if requested + if params[:gpu] == "true" + tiers = tiers.select(&:has_gpu?) + elsif params[:gpu] == "false" + tiers = tiers.reject(&:has_gpu?) + end + + render json: { + tiers: tiers.map(&:as_json), + categories: [ + { id: "free", name: "Free", description: "Basic instances for testing" }, + { id: "pro", name: "Pro", description: "Standard instances for production" }, + { id: "enterprise", name: "Enterprise", description: "High-performance instances" } + ] + } + end + + # GET /api/instance_tiers/:id + # Get a specific instance tier + def show + render json: { tier: @tier.as_json } + end + + # GET /api/instance_tiers/recommend + # Get recommended tier for a sandbox type + def recommend + sandbox_type = params[:sandbox_type] || "default" + + recommended = case sandbox_type + when "playwright_mcp" + SandboxInstanceTier.find(:cpu_medium) + when "research" + SandboxInstanceTier.find(:cpu_small) + when "terminal" + SandboxInstanceTier.find(:free) + else + SandboxInstanceTier.find(:cpu_small) + end + + alternatives = SandboxInstanceTier.available.reject { |t| t.id == recommended.id }.take(3) + + render json: { + recommended: recommended.as_json, + alternatives: alternatives.map(&:as_json), + sandbox_type: sandbox_type + } + end + + # GET /api/instance_tiers/pricing + # Get pricing comparison + def pricing + tiers = SandboxInstanceTier.available + + render json: { + cpu_tiers: tiers.select { |t| !t.has_gpu? }.map { |t| + { + id: t.id, + name: t.name, + specs: t.display_specs, + hourly: t.hourly_cost.to_f, + monthly: t.monthly_cost.to_f, + category: t.category + } + }, + gpu_tiers: tiers.select(&:has_gpu?).map { |t| + { + id: t.id, + name: t.name, + specs: t.display_specs, + hourly: t.hourly_cost.to_f, + monthly: t.monthly_cost.to_f, + category: t.category + } + }, + currency: "USD", + billing_increment: "per second" + } + end + + private + + def set_tier + @tier = SandboxInstanceTier.find(params[:id]) + rescue ArgumentError => e + render json: { error: "Tier not found: #{params[:id]}" }, status: :not_found + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/interactions_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/interactions_controller.rb new file mode 100644 index 00000000..fbe9102f --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/interactions_controller.rb @@ -0,0 +1,164 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + # Read API for agent conversation contexts (solid_agent persistence), + # backing the dashboard Interactions view. + # + # A context is one agent's interaction stream (AgentContext + + # AgentMessages + AgentGenerations, recorded by SolidAgent::HasContext + # during execution), scoped to the current user's agents. + class InteractionsController < BaseController + before_action :require_owner! + + DEFAULT_LIMIT = 50 + MAX_WINDOW_MINUTES = 60 * 24 * 90 + + # GET /api/interactions + def index + limit = params.fetch(:limit, DEFAULT_LIMIT).to_i.clamp(1, 200) + + contexts = interactions_scope + .includes(:contextable) + .then { |scope| window_minutes ? scope.where(updated_at: window_minutes.minutes.ago..) : scope } + .recent + .limit(limit) + + message_counts = AgentMessage.where(agent_context_id: contexts.map(&:id)).group(:agent_context_id).count + generation_counts = AgentGeneration.where(agent_context_id: contexts.map(&:id)).group(:agent_context_id).count + # Latest model per context, so list rows can gauge context-window + # pressure without loading generations. + # DISTINCT ON would do this in one pass on PostgreSQL but has no + # portable equivalent, so take the newest generation id per context + # (they are only ever appended) and read those rows. + newest_ids = AgentGeneration + .where(agent_context_id: contexts.map(&:id)) + .group(:agent_context_id) + .maximum(:id) + latest_models = AgentGeneration.where(id: newest_ids.values) + .pluck(:agent_context_id, :model).to_h + + persisted = contexts.map do |context| + serialize_context(context).merge( + source: "platform", + model: latest_models[context.id], + message_count: message_counts[context.id] || 0, + generation_count: generation_counts[context.id] || 0 + ) + end + + reported = reported_traces(limit).map { |trace| TraceInteractionSerializer.summary(trace) } + + render json: { + interactions: (persisted + reported).sort_by { |row| row[:last_activity_at].to_s }.reverse.first(limit) + } + end + + # GET /api/interactions/:id + def show + if (trace_id = params[:id].to_s[/\Atrace-(\d+)\z/, 1]) + trace = owned_traces.find(trace_id) + return render json: { interaction: TraceInteractionSerializer.detail(trace) } + end + + context = interactions_scope.find(params[:id]) + + render json: { + interaction: serialize_context(context).merge( + source: "platform", + instructions: context.instructions, + messages: context.messages.chronological.map { |message| serialize_message(message) }, + generations: context.generations.order(created_at: :asc).map { |generation| serialize_generation(generation) } + ) + } + end + + private + + # Agents executing outside the platform never write solid_agent contexts — + # they only report traces. Every reported trace is an interaction: one run + # of one agent. Traces without captured content still show their tool calls, + # timings, and generation metadata, so filtering on the presence of a + # prompt would hide most runs (locally: 9 of 11) and make the per-agent + # counts disagree with Traces for the same window. + def reported_traces(limit) + scope = owned_traces.where.not(agent_class: nil) + scope = scope.where(timestamp: window_minutes.minutes.ago..) if window_minutes + # Honor the agent filter the persisted side already applies: without + # this, a per-agent view listed every agent's reported traffic, and an + # agent whose traces are all SDK-reported showed nothing of its own. + scope = scope.merge(traces_for_agent(params[:agent_id])) if params[:agent_id].present? + scope.order(timestamp: :desc).limit(limit) + end + + # Traces belong to an agent by foreign key once AgentRegistrar attributes + # them; older rows predate that, so fall back to the class name. + def traces_for_agent(agent_id) + agent = owner_agents.find_by(id: agent_id) + return ActiveAgent::Dashboard.trace_model.none unless agent + + ActiveAgent::Dashboard.trace_model.where(agent_id: agent.id) + .or(ActiveAgent::Dashboard.trace_model.where(agent_id: nil, agent_class: agent.telemetry_agent_class)) + end + + # The dashboard-wide time window, shared with Traces. Absent means "all". + def window_minutes + return @window_minutes if defined?(@window_minutes) + + raw = params[:minutes].presence + @window_minutes = raw ? raw.to_i.clamp(1, MAX_WINDOW_MINUTES) : nil + end + + def interactions_scope + agents = owner_agents + agents = agents.where(id: params[:agent_id]) if params[:agent_id].present? + AgentContext.for_agents(agents) + end + + def serialize_context(context) + agent = context.contextable + { + id: context.id, + agent_name: context.agent_name, + action_name: context.action_name, + display_name: "#{context.agent_name}##{context.action_name}", + agent: agent.is_a?(Agent) ? { id: agent.id, name: agent.name, slug: agent.slug } : nil, + tokens: { + input: context.total_input_tokens, + output: context.total_output_tokens, + total: context.total_tokens + }, + created_at: context.created_at.iso8601, + last_activity_at: context.updated_at.iso8601 + } + end + + def serialize_message(message) + AgentMessageSerializer.call(message) + end + + def serialize_generation(generation) + { + id: generation.id, + model: generation.model, + provider: generation.provider, + finish_reason: generation.finish_reason, + tokens: { + input: generation.input_tokens, + output: generation.output_tokens, + total: generation.total_tokens, + cached: generation.cached_tokens, + thinking: generation.reasoning_tokens + }, + cache_hit: generation.cache_hit?, + thinking: generation.thinking?, + duration_seconds: generation.duration_seconds, + trace_id: generation.trace_id, + created_at: generation.created_at.iso8601(3) + } + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/mcp_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/mcp_controller.rb new file mode 100644 index 00000000..86d2274f --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/mcp_controller.rb @@ -0,0 +1,220 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + # Exposes the dashboard's agents as an authenticated MCP (Model Context + # Protocol) service over Streamable HTTP JSON-RPC — agents present + # themselves as Resource Agents backed by their ActiveRecord state: + # + # - tools/list & tools/call: each agent is a callable tool (run_) + # that executes a synchronous generation run. + # - resources/list & resources/read: each agent is an agent:// + # resource whose content is its live scorecard (config + stats + memory + # summary from the solid_agent datasets). + # + # Authentication/authorization: Bearer (Settings -> API Keys). + # The key scopes everything to its account — its members' agents and its + # run quotas. + # + # Connect from an MCP client with: + # { "type": "http", "url": "https://activeagents.ai/mcp", + # "headers": { "Authorization": "Bearer aa_..." } } + class McpController < BaseController + # Authenticated by API key rather than by the host app's sessions. + allow_unauthenticated_access + before_action :authenticate_api_key! + + PROTOCOL_VERSION = "2025-03-26" + JSONRPC_METHOD_NOT_FOUND = -32601 + JSONRPC_INVALID_PARAMS = -32602 + JSONRPC_SERVER_ERROR = -32000 + + # POST /mcp + def create + request_id = params[:id] + + # JSON-RPC notifications get no response body. + return head :accepted if request_id.nil? && params[:method].to_s.start_with?("notifications/") + + result = case params[:method] + when "initialize" then initialize_result + when "ping" then {} + when "tools/list" then tools_list + when "tools/call" then tools_call + when "resources/list" then resources_list + when "resources/read" then resources_read + else + return render_error(request_id, JSONRPC_METHOD_NOT_FOUND, "Method not found: #{params[:method]}") + end + + render json: { jsonrpc: "2.0", id: request_id, result: result } + rescue McpError => e + render_error(request_id, e.code, e.message) + rescue StandardError => e + Rails.logger.error("[Api::McpController] #{e.class}: #{e.message}") + render_error(request_id, JSONRPC_SERVER_ERROR, "Internal error") + end + + private + + # JSON-RPC errors ride on HTTP 200 per the MCP Streamable HTTP transport. + def render_error(id, code, message) + render json: { jsonrpc: "2.0", id: id, error: { code: code, message: message } } + end + + class McpError < StandardError + attr_reader :code + + def initialize(message, code = JSONRPC_SERVER_ERROR) + super(message) + @code = code + end + end + + def authenticate_api_key! + token = request.headers["Authorization"].to_s[/\ABearer\s+(.+)\z/i, 1] + api_key = ApiKey.authenticate(token) + + if api_key.nil? + render json: { error: "Unauthorized: pass a dashboard API key as a Bearer token" }, status: :unauthorized + return + end + + api_key.touch_last_used! + @api_key = api_key + @owner = api_key.owner + end + + # The agents this key can reach. A key belongs to whoever owns it, and + # a single-user install has no owner, so the key reaches every agent + # the dashboard holds. + def key_agents + ActiveAgent::Dashboard::Agent.for_owner(@owner).where.not(status: :archived).order(:slug) + end + + def initialize_result + { + protocolVersion: PROTOCOL_VERSION, + capabilities: { tools: {}, resources: {} }, + serverInfo: { name: "activeagents", version: "1.0" }, + instructions: "Each tool runs one of this account's agents. Each agent:// resource returns the agent's live scorecard." + } + end + + MESSAGE_INPUT_SCHEMA = { + type: "object", + properties: { + message: { type: "string", description: "The prompt/message for the agent" } + }, + required: [ "message" ] + }.freeze + + def tools_list + tools = key_agents.flat_map do |agent| + agent_tools = [ { + name: "run_#{agent.slug}", + description: agent.description.presence || "Run the #{agent.name} agent", + inputSchema: MESSAGE_INPUT_SCHEMA + } ] + # Named actions marked expose_as_tool are individually callable — + # the activeagent "actions as tools" pattern over MCP. + Array(agent.action_prompts).select { |ap| ap["expose_as_tool"] }.each do |action| + agent_tools << { + name: "run_#{agent.slug}__#{action['name']}", + description: "Run the #{agent.name} agent's #{action['name']} action", + inputSchema: MESSAGE_INPUT_SCHEMA + } + end + agent_tools + end + + { tools: tools } + end + + def tools_call + name = params.dig(:params, :name).to_s + slug, action = name.delete_prefix("run_").split("__", 2) + agent = key_agents.find_by(slug: slug) + raise McpError.new("Unknown tool: #{name}", JSONRPC_INVALID_PARAMS) unless agent + if action.present? && agent.action_prompt_for(action)&.dig("expose_as_tool") != true + raise McpError.new("Unknown tool: #{name}", JSONRPC_INVALID_PARAMS) + end + + message = params.dig(:params, :arguments, :message).to_s + raise McpError.new("Missing required argument: message", JSONRPC_INVALID_PARAMS) if message.blank? + + unless ActiveAgent::Dashboard.execution_enabled? + raise McpError.new("Agent execution is disabled on this dashboard") + end + if (denial = ActiveAgent::Dashboard.quota_denial(@owner, :execution)) + raise McpError.new(denial) + end + + run = agent.test_execute(message, action: action) + ActiveAgent::Dashboard.record_usage(@owner, :execution) + + if run.failed? + { content: [ { type: "text", text: "Agent run failed: #{run.error_message}" } ], isError: true } + else + { + content: [ { type: "text", text: run.output.to_s } ], + structuredContent: { + run_id: run.id, + trace_id: run.trace_id, + duration_ms: run.duration_ms, + total_tokens: run.total_tokens, + metadata: run.output_metadata + } + } + end + end + + def resources_list + { + resources: key_agents.map do |agent| + { + uri: "agent://#{agent.slug}", + name: agent.name, + description: agent.description.presence || "#{agent.name} scorecard", + mimeType: "application/json" + } + end + } + end + + def resources_read + uri = params.dig(:params, :uri).to_s + slug = uri.delete_prefix("agent://") + agent = key_agents.find_by(slug: slug) + raise McpError.new("Unknown resource: #{uri}", JSONRPC_INVALID_PARAMS) unless agent + + { + contents: [ { + uri: uri, + mimeType: "application/json", + text: agent_resource(agent).to_json + } ] + } + end + + # The Resource Agent card: configuration plus live scorecard stats and + # the agent's memory summary (solid_agent datasets). + def agent_resource(agent) + { + name: agent.name, + slug: agent.slug, + description: agent.description, + provider: agent.provider, + model: agent.model, + status: agent.status, + tools: agent.tools, + instructions: agent.instructions, + stats: AgentScorecard.for_agents([ agent ])[agent.id], + memory: agent.memory.summary_list.last(20) + } + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/metrics_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/metrics_controller.rb new file mode 100644 index 00000000..a476bf06 --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/metrics_controller.rb @@ -0,0 +1,176 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + # Read API for telemetry metrics, backing the dashboard Metrics view. + # + # Exposes the same aggregates as the gem dashboard's metrics page + # (ActiveAgent::Dashboard::TracesController#metrics / #calculate_metrics / + # #agent_statistics): trace counts, token totals, average duration, error + # rate, active agents and per-agent statistics — account-scoped, plus + # previous-period deltas for trend indicators. + class MetricsController < BaseController + before_action :require_owner! + + DEFAULT_WINDOW_HOURS = 24 + MAX_WINDOW_HOURS = 24 * 30 + + # How to rank the per-agent table. Cost is applied after the grouped + # query because pricing happens in Ruby (rates vary per model), so all + # four are ordered in one place rather than half in SQL. + AGENT_SORTS = { + "popular" => "Most requests", + "longest" => "Longest average", + "cost" => "Highest cost", + "tokens" => "Most tokens", + "errors" => "Most errors" + }.freeze + DEFAULT_AGENT_SORT = "popular" + + # GET /api/metrics + def show + hours = params.fetch(:hours, DEFAULT_WINDOW_HOURS).to_i.clamp(1, MAX_WINDOW_HOURS) + now = Time.current + + current = traces_scope.for_date_range(hours.hours.ago(now), now) + previous = traces_scope.for_date_range((hours * 2).hours.ago(now), hours.hours.ago(now)) + + costs = cost_statistics(current) + priced = agent_statistics(current).map { |row| row.merge(cost: costs[:by_agent][row[:name]] || 0.0) } + + render json: { + summary: summary_for(current, previous).merge(total_cost: costs[:total]), + hourly_requests: hourly_requests(current, hours, now), + by_agent: sort_agents(priced, params[:sort]), + window_hours: hours, + sorts: AGENT_SORTS, + sort: agent_sort(params[:sort]) + } + end + + private + + def agent_sort(requested) + AGENT_SORTS.key?(requested.to_s) ? requested.to_s : DEFAULT_AGENT_SORT + end + + # Descending on the chosen dimension; request count breaks ties so the + # table keeps a stable, meaningful secondary order. + def sort_agents(rows, requested) + key = case agent_sort(requested) + when "longest" then :avg_duration_ms + when "cost" then :cost + when "tokens" then :tokens + when "errors" then :errors + else :requests + end + + rows.sort_by { |row| [ -row[key].to_f, -row[:requests].to_i ] } + end + + def traces_scope + ActiveAgent::Dashboard.trace_model.for_account(current_account) + end + + # Same definitions as the gem dashboard's calculate_metrics, with + # previous-period percentage changes layered on top. + def summary_for(current, previous) + total = current.count + previous_total = previous.count + + input_tokens = current.sum(:total_input_tokens) + output_tokens = current.sum(:total_output_tokens) + thinking_tokens = current.sum(:total_thinking_tokens) + + avg_latency = current.average(:total_duration_ms)&.round(0) + previous_avg_latency = previous.average(:total_duration_ms)&.round(0) + + error_rate = total.positive? ? (current.with_errors.count.to_f / total * 100).round(2) : 0.0 + + { + total_requests: total, + requests_change: percent_change(previous_total, total), + avg_latency_ms: avg_latency || 0, + latency_change: percent_change(previous_avg_latency, avg_latency), + error_rate: error_rate, + errors: current.with_errors.count, + unique_agents: current.distinct.count(:agent_class), + tokens_used: input_tokens + output_tokens + thinking_tokens, + tokens_input: input_tokens, + tokens_output: output_tokens, + tokens_thinking: thinking_tokens + } + end + + def hourly_requests(scope, hours, now) + counts = scope.group(Arel.sql("date_trunc('hour', timestamp)")).count.transform_keys(&:to_i) + latencies = scope.group(Arel.sql("date_trunc('hour', timestamp)")).average(:total_duration_ms).transform_keys(&:to_i) + + start_hour = (now - (hours - 1).hours).beginning_of_hour + (0...hours).map do |offset| + bucket = start_hour + offset.hours + { + hour: bucket.strftime("%H:00"), + timestamp: bucket.iso8601, + count: counts[bucket.to_i] || 0, + avg_latency_ms: latencies[bucket.to_i]&.round(0) || 0, + active: offset == hours - 1 + } + end + end + + # Mirrors the gem dashboard's agent_statistics grouped query. + def agent_statistics(scope) + scope + .where.not(agent_class: nil) + .group(:agent_class) + .select( + "agent_class", + "COUNT(*) AS trace_count", + "SUM(total_input_tokens + total_output_tokens + total_thinking_tokens) AS token_sum", + "AVG(total_duration_ms) AS avg_duration", + "SUM(CASE WHEN status = 'ERROR' THEN 1 ELSE 0 END) AS error_count" + ) + .order(Arel.sql("trace_count DESC")) + .map do |row| + { + name: row.agent_class, + requests: row.trace_count, + tokens: row.token_sum.to_i, + avg_duration_ms: row.avg_duration&.round(0) || 0, + errors: row.error_count.to_i + } + end + end + + # Estimated spend for the window, total and per agent class. The model + # lives inside the spans JSON (first llm span). + def cost_statistics(scope) + rows = ActiveAgent::Dashboard.trace_model.pluck_with_llm_model( + scope, :agent_class, :total_input_tokens, :total_output_tokens + ) + + by_agent = Hash.new(0.0) + total = 0.0 + + rows.each do |model, agent_class, input_tokens, output_tokens| + cost = ModelPricing.estimate(model: model, input_tokens: input_tokens, output_tokens: output_tokens) + next unless cost + + total += cost + by_agent[agent_class] += cost if agent_class + end + + { total: total.round(4), by_agent: by_agent.transform_values { |v| v.round(4) } } + end + + def percent_change(previous, current) + return nil if previous.nil? || current.nil? || previous.to_f.zero? + + (((current.to_f - previous.to_f) / previous.to_f) * 100).round(1) + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/provider_keys_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/provider_keys_controller.rb new file mode 100644 index 00000000..9b9c9548 --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/provider_keys_controller.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + # Per-account LLM provider credentials (Settings -> Provider API Keys). + # API keys are write-only: responses carry a masked hint, never the key. + # Ollama's credential is a host URL and is echoed back in full. + class ProviderKeysController < BaseController + before_action :require_owner! + + # GET /api/provider_keys — one row per supported provider, configured or not. + def index + configured = owned(ProviderKey).index_by(&:provider) + + render json: { + provider_keys: ProviderKey::PROVIDERS.map do |provider| + serialize(provider, configured[provider]) + end + } + end + + # POST /api/provider_keys — upserts the credential for a provider. + def create + provider = params.require(:provider) + credential = params.require(:credential) + + record = owned(ProviderKey).find_or_initialize_by(provider: provider) + record.update!(credential: credential) + + render json: { provider_key: serialize(provider, record) }, status: :created + end + + # DELETE /api/provider_keys/:provider + def destroy + owned(ProviderKey).find_by!(provider: params[:provider]).destroy! + head :no_content + end + + private + + def serialize(provider, record) + { + provider: provider, + host_based: ProviderKey::HOST_PROVIDERS.include?(provider), + configured: record.present?, + hint: record&.display_hint, + updated_at: record&.updated_at&.iso8601 + } + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/provider_models_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/provider_models_controller.rb new file mode 100644 index 00000000..a5bd5ff6 --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/provider_models_controller.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + # Model catalogs for the agent builder/editor dropdowns. + # + # Hosted providers get a curated list of current models (kept here, server + # side, so the UI can't drift stale). Ollama is queried live from the + # account's configured host (Settings -> Provider API Keys, falling back to + # the platform config) so locally pulled models appear; OpenRouter is + # queried from its public catalog. Live lookups fall back to the curated + # list on any failure. + class ProviderModelsController < BaseController + before_action :require_owner! + + FETCH_TIMEOUT_SECONDS = 4 + + # Fallbacks when a live lookup isn't possible (reviewed 2026-07). First + # entry is the default the builder preselects. + CURATED = { + "openai" => %w[gpt-5.1 gpt-5 gpt-5-mini gpt-5-nano], + "anthropic" => %w[claude-opus-5 claude-fable-5 claude-sonnet-5 claude-opus-4-8 claude-haiku-4-5], + "openrouter" => %w[anthropic/claude-sonnet-4.5 openai/gpt-5.1 meta-llama/llama-3.3-70b-instruct qwen/qwen3-32b], + "ollama" => %w[qwen3:8b llama3.2 mistral gemma3] + }.freeze + + # GET /api/provider_models?provider=ollama + def index + provider = params[:provider].to_s + unless Agent::PROVIDERS.include?(provider) + return render json: { error: "Unknown provider: #{provider}" }, status: :unprocessable_entity + end + + models, source = case provider + when "ollama" then live_ollama_models + when "openrouter" then live_openrouter_models + when "anthropic" then live_anthropic_models + end + + if models.blank? + models = CURATED.fetch(provider) + source = "curated" + end + + render json: { provider: provider, models: models, source: source } + end + + private + + # The owner's configured Ollama endpoint, else the host app's default + # from config/active_agent.yml. + def ollama_host + key = owner_provider_key("ollama") + host = key&.credential.presence || ActiveAgent.configuration[:ollama]&.dig(:host) + host.presence + end + + def owner_provider_key(provider) + owned(ProviderKey).find_by(provider: provider) + end + + def live_ollama_models + host = ollama_host + return nil unless host + + data = fetch_json(URI.join("#{host.chomp('/')}/", "models")) + ids = Array(data&.dig("data")).filter_map { |model| model["id"] } + [ ids.sort, "live" ] if ids.any? + rescue StandardError => e + Rails.logger.warn("[ProviderModels] ollama lookup failed: #{e.message}") + nil + end + + # Queries the Anthropic Models API with the account's key (newest first, + # as returned by the API) so new model releases appear without a deploy. + def live_anthropic_models + key = current_user_provider_key("anthropic")&.credential + return nil if key.blank? + + data = Rails.cache.fetch("provider_models:anthropic:#{Digest::SHA256.hexdigest(key)}", expires_in: 1.hour) do + uri = URI.parse("https://api.anthropic.com/v1/models?limit=50") + response = Net::HTTP.start( + uri.host, uri.port, + use_ssl: true, open_timeout: FETCH_TIMEOUT_SECONDS, read_timeout: FETCH_TIMEOUT_SECONDS + ) { |http| http.get(uri.request_uri, { "x-api-key" => key, "anthropic-version" => "2023-06-01" }) } + response.code.to_i == 200 ? JSON.parse(response.body) : nil + end + ids = Array(data&.dig("data")).filter_map { |model| model["id"] } + [ ids, "live" ] if ids.any? + rescue StandardError => e + Rails.logger.warn("[ProviderModels] anthropic lookup failed: #{e.message}") + nil + end + + def live_openrouter_models + data = Rails.cache.fetch("provider_models:openrouter", expires_in: 1.hour) do + fetch_json(URI.parse("https://openrouter.ai/api/v1/models")) + end + ids = Array(data&.dig("data")).filter_map { |model| model["id"] } + [ ids.sort.first(100), "live" ] if ids.any? + rescue StandardError => e + Rails.logger.warn("[ProviderModels] openrouter lookup failed: #{e.message}") + nil + end + + def fetch_json(uri) + response = Net::HTTP.start( + uri.host, uri.port, + use_ssl: uri.scheme == "https", + open_timeout: FETCH_TIMEOUT_SECONDS, + read_timeout: FETCH_TIMEOUT_SECONDS + ) { |http| http.get(uri.request_uri) } + return nil unless response.code.to_i == 200 + + JSON.parse(response.body) + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/sandboxes_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/sandboxes_controller.rb new file mode 100644 index 00000000..8d378b11 --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/sandboxes_controller.rb @@ -0,0 +1,218 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + class SandboxesController < BaseController + # Allow anonymous access to sandbox API for free tier + allow_unauthenticated_access + + before_action :set_sandbox, only: [ :show, :run, :destroy ] + + # POST /api/sandboxes/compare + # Run multiple providers in a single sandbox using parallel generation jobs + def compare + providers = params[:providers] || %w[anthropic openai ollama] + task = params[:task] + sandbox_id = params[:sandbox_id] + + return render json: { error: "Task required" }, status: :bad_request unless task.present? + return render json: { error: "At least 2 providers required" }, status: :bad_request if providers.size < 2 + + # Validate providers + invalid = providers - %w[anthropic openai ollama] + return render json: { error: "Invalid providers: #{invalid.join(', ')}" }, status: :bad_request if invalid.any? + + # Use existing sandbox or create a new one (single container per user) + sandbox = if sandbox_id.present? + SandboxSession.find_by!(session_id: sandbox_id) + else + s = SandboxSession.create!( + sandbox_type: params[:sandbox_type] || "playwright_mcp", + user: current_user + ) + s.provision! + s.reload + s + end + + unless sandbox.can_run? + return render json: { + error: sandbox.expired? ? "Session expired" : "Maximum runs exceeded", + sandbox: sandbox.summary + }, status: :unprocessable_entity + end + + sandbox.update!(status: :running) + comparison_id = SecureRandom.uuid + + # Spawn a separate generation job for each provider (all in same sandbox) + runs = providers.map do |provider| + run_id = SecureRandom.uuid + SandboxRunJob.perform_later(sandbox.id, run_id, task, provider) + + { + provider: provider, + run_id: run_id, + status: "running" + } + end + + render json: { + comparison_id: comparison_id, + task: task, + sandbox: sandbox.summary, + runs: runs + }, status: :accepted + end + + # GET /api/sandboxes + # List available sandbox types and sample tasks + def index + render json: { + sandbox_types: SandboxSession::SANDBOX_TYPES, + free_tier_limits: SandboxSession::FREE_TIER_LIMITS, + templates: free_tier_templates, + sample_tasks: sample_tasks + } + end + + # POST /api/sandboxes + # Create a new sandbox session (no auth required for free tier) + def create + @sandbox = SandboxSession.new(sandbox_params) + @sandbox.user = current_user # nil for anonymous users + @sandbox.agent_template = AgentTemplate.find_by(slug: params[:template_slug]) if params[:template_slug] + + if @sandbox.save + @sandbox.provision! + @sandbox.reload # Reload to get updated status after provisioning + render json: { sandbox: @sandbox.summary }, status: :created + else + render json: { errors: @sandbox.errors.full_messages }, status: :unprocessable_entity + end + end + + # GET /api/sandboxes/:session_id + # Get sandbox status and run history + def show + render json: { sandbox: @sandbox.details } + end + + # POST /api/sandboxes/:session_id/run + # Execute a task in the sandbox + def run + unless @sandbox.can_run? + return render json: { + error: @sandbox.expired? ? "Session expired" : "Maximum runs exceeded", + sandbox: @sandbox.summary + }, status: :unprocessable_entity + end + + # Whatever limits the host app imposes on running an agent. + if (denial = ActiveAgent::Dashboard.quota_denial(current_owner, :execution)) + return render json: { + error: "Plan limit reached", + upgrade_required: true, + message: denial + }, status: :payment_required + end + + task = params[:task] + return render json: { error: "Task required" }, status: :bad_request unless task.present? + + # Provider selection (default to anthropic) + provider = params[:provider] || "anthropic" + unless %w[anthropic openai ollama].include?(provider) + return render json: { error: "Invalid provider" }, status: :bad_request + end + + record_execution_usage + + @sandbox.update!(status: :running) + + # Execute via job for async processing + run_id = SecureRandom.uuid + SandboxRunJob.perform_later(@sandbox.id, run_id, task, provider) + + render json: { + run_id: run_id, + status: "running", + provider: provider, + sandbox: @sandbox.summary + }, status: :accepted + end + + # DELETE /api/sandboxes/:session_id + # End sandbox session + def destroy + @sandbox.expire! + render json: { deleted: true } + end + + private + + def set_sandbox + @sandbox = SandboxSession.find_by!(session_id: params[:id]) + end + + def sandbox_params + params.permit(:sandbox_type) + end + + def free_tier_templates + AgentTemplate.free_tier.featured.map do |template| + { + slug: template.slug, + name: template.name, + description: template.description, + icon: template.icon, + sandbox_type: template.preset_type == "playwright" ? "playwright_mcp" : template.preset_type + } + end + end + + def sample_tasks + { + playwright_mcp: [ + { + name: "Screenshot Example.com", + task: "Take a screenshot of https://example.com", + description: "Navigate to example.com and capture a screenshot" + }, + { + name: "Extract Hacker News Headlines", + task: "Go to https://news.ycombinator.com and list the top 5 story titles with their scores", + description: "Scrape the front page of Hacker News" + }, + { + name: "Check Wikipedia", + task: "Navigate to https://en.wikipedia.org/wiki/Artificial_intelligence and extract the first paragraph", + description: "Extract content from Wikipedia" + }, + { + name: "GitHub Trending", + task: "Visit https://github.com/trending and list the top 3 trending repositories", + description: "Check GitHub's trending repositories" + } + ], + terminal: [ + { + name: "System Info", + task: "Show the current system information", + description: "Display OS, memory, and CPU details" + } + ], + research: [ + { + name: "Topic Summary", + task: "Research and summarize the latest developments in AI", + description: "Search and compile information on a topic" + } + ] + } + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/session_recordings_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/session_recordings_controller.rb new file mode 100644 index 00000000..c00a337c --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/session_recordings_controller.rb @@ -0,0 +1,380 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + class SessionRecordingsController < BaseController + # Allow unauthenticated access for user session tracking (lander analytics) + allow_unauthenticated_access only: [ :start_user_session, :record_action, :complete_session, :demo ] + + before_action :set_recording, only: [ :show, :actions, :snapshot, :export, :handoff ] + + # GET /api/session_recordings + # List recordings with optional filters + def index + # Recordings the caller owns, plus the shared demo. Ownership is a + # real column rather than a JSON metadata key, so this works on + # every adapter. + recordings = owned(SessionRecording).or(SessionRecording.where(name: "lander_demo")).recent + + # Filter by status + recordings = recordings.where(status: params[:status]) if params[:status].present? + + # Filter by agent + if params[:agent_id].present? + recordings = recordings.joins(:agent_run) + .where(agent_runs: { agent_id: params[:agent_id] }) + end + + # Filter by sandbox session + if params[:sandbox_session_id].present? + recordings = recordings.where(sandbox_session_id: params[:sandbox_session_id]) + end + + # Pagination + page = (params[:page] || 1).to_i + per_page = [ (params[:per_page] || 20).to_i, 100 ].min + offset = (page - 1) * per_page + + total = recordings.count + recordings = recordings.offset(offset).limit(per_page) + + render json: { + recordings: recordings.map { |r| recording_summary(r) }, + pagination: { + page: page, + per_page: per_page, + total: total, + total_pages: (total.to_f / per_page).ceil + } + } + end + + # GET /api/session_recordings/recent + # Get recent recordings for the current user + def recent + recordings = owned(SessionRecording).recent.limit(10) + + render json: { + recordings: recordings.map { |r| recording_summary(r) } + } + end + + # GET /api/session_recordings/demo + # Get the pre-recorded demo session for the lander + def demo + # Find the featured demo recording or create a placeholder + demo_recording = SessionRecording.find_by(name: "lander_demo") || + SessionRecording.completed.order(:created_at).first + + if demo_recording + render json: { + recording: recording_detail(demo_recording), + handoff_available: demo_recording.metadata["handoff_state"].present? + } + else + render json: { + recording: nil, + message: "No demo recording available" + } + end + end + + # GET /api/session_recordings/:id + # Get full recording details for playback + def show + render json: { + recording: recording_detail(@recording) + } + end + + # GET /api/session_recordings/:id/actions + # Get the action timeline for playback + def actions + actions = @recording.recording_actions.ordered + + # Support pagination for large recordings + if params[:after_sequence].present? + actions = actions.where("sequence > ?", params[:after_sequence].to_i) + end + + limit = [ params[:limit]&.to_i || 100, 500 ].min + actions = actions.limit(limit) + + render json: { + actions: actions.map(&:as_json_for_api), + has_more: actions.count == limit, + total_actions: @recording.action_count + } + end + + # GET /api/session_recordings/:id/snapshot/:action_id + # Get a specific snapshot (screenshot or DOM) + def snapshot + action = @recording.recording_actions.find(params[:action_id]) + + snapshot_type = params[:type] || "screenshot" + + case snapshot_type + when "screenshot" + url = action.screenshot_url + render json: { url: url, type: "screenshot" } + when "dom" + content = action.dom_snapshot_content + render json: { content: content, type: "dom" } + else + render json: { error: "Unknown snapshot type" }, status: :bad_request + end + rescue ActiveRecord::RecordNotFound + render json: { error: "Action not found" }, status: :not_found + end + + # POST /api/session_recordings/:id/export + # Export recording as VCR cassette + def export + format = params[:format] || "json" + + cassette = build_cassette(@recording, format) + + render json: { + cassette: cassette, + filename: "#{@recording.name}_recording.#{format}" + } + end + + # POST /api/session_recordings/start_user_session + # Start a new user takeover session for analytics + def start_user_session + recording = SessionRecording.start_user_session!( + visitor_id: params[:visitor_id] || generate_visitor_id, + parent_demo_id: params[:parent_demo_id], + page_url: params[:page_url] + ) + + # Set user agent from request + recording.update!( + metadata: recording.metadata.merge( + user_agent: request.user_agent, + ip_hash: Digest::SHA256.hexdigest(request.remote_ip.to_s)[0..16] + ) + ) + + # Record the handoff action + recording.record_action!( + action_type: "handoff", + value: "User took over from agent demo", + metadata: { + source: "lander_demo", + step: params[:step] || 4 + } + ) + + render json: { + recording_id: recording.id, + visitor_id: recording.metadata["visitor_id"], + message: "User session started" + }, status: :created + end + + # POST /api/session_recordings/:id/record_action + # Record a user action in an active session + def record_action + recording = SessionRecording.find(params[:id]) + + unless recording.recording? + render json: { error: "Recording already completed" }, status: :unprocessable_entity + return + end + + action = recording.record_action!( + action_type: params[:action_type], + selector: params[:selector], + value: params[:value], + metadata: params[:metadata]&.to_unsafe_h || {} + ) + + render json: { + action_id: action.id, + sequence: action.sequence, + timestamp_ms: action.timestamp_ms + } + end + + # POST /api/session_recordings/:id/complete + # Complete a user session recording + def complete_session + recording = SessionRecording.find(params[:id]) + + unless recording.recording? + render json: { error: "Recording already completed" }, status: :unprocessable_entity + return + end + + # Record the completion action + recording.record_action!( + action_type: "completion", + value: params[:completion_type] || "session_end", + metadata: { + email_submitted: params[:email_submitted], + success: params[:success] + } + ) + + recording.complete! + + render json: { + recording_id: recording.id, + status: recording.status, + action_count: recording.action_count, + duration_ms: recording.duration_ms + } + end + + # POST /api/session_recordings/:id/handoff + # Get handoff state to continue where agent left off + def handoff + handoff_state = @recording.metadata["handoff_state"] + + unless handoff_state + render json: { error: "No handoff state available" }, status: :unprocessable_entity + return + end + + # Create a new recording for the user's continuation + continuation = SessionRecording.create!( + sandbox_session: @recording.sandbox_session, + agent_run: @recording.agent_run, + name: "#{@recording.name}_continuation", + status: :recording, + metadata: { + parent_recording_id: @recording.id, + handoff_from: @recording.action_count, + started_at: Time.current.iso8601, + user_id: current_user&.id + } + ) + + render json: { + handoff_state: handoff_state, + continuation_recording_id: continuation.id, + parent_recording: recording_summary(@recording), + message: "Ready to continue from action #{@recording.action_count}" + } + end + + # DELETE /api/session_recordings/:id + def destroy + @recording = SessionRecording.find(params[:id]) + + # Only allow deletion of own recordings (or any if admin) + unless can_manage_recording?(@recording) + render json: { error: "Not authorized" }, status: :forbidden + return + end + + @recording.destroy! + render json: { message: "Recording deleted" } + end + + private + + def set_recording + @recording = SessionRecording.find(params[:id]) + end + + def can_manage_recording?(recording) + # An install with no owner model owns everything it can see. + return true if SessionRecording.owner_association.nil? + return true if owned(SessionRecording).exists?(id: recording.id) + + # Recordings made inside a sandbox belong to whoever opened it. + session = recording.sandbox_session + return true if session && session.owner.present? && session.owner == current_owner + + false + end + + def recording_summary(recording) + { + id: recording.id, + name: recording.name, + status: recording.status, + action_count: recording.action_count, + duration_ms: recording.duration_ms, + created_at: recording.created_at.iso8601, + thumbnail_url: first_screenshot_url(recording), + agent_name: recording.agent_run&.agent&.name, + sandbox_type: recording.sandbox_session&.sandbox_type + } + end + + def recording_detail(recording) + { + id: recording.id, + name: recording.name, + status: recording.status, + action_count: recording.action_count, + duration_ms: recording.duration_ms, + metadata: safe_metadata(recording.metadata), + created_at: recording.created_at.iso8601, + updated_at: recording.updated_at.iso8601, + timeline: recording.timeline, + handoff_state: recording.metadata["handoff_state"], + agent: recording.agent_run&.agent&.slice(:id, :name), + sandbox_session: recording.sandbox_session&.summary + } + end + + def first_screenshot_url(recording) + action = recording.recording_actions.with_screenshots.first + action&.screenshot_url(expires_in: 1.hour) + end + + def safe_metadata(metadata) + # Remove sensitive data from metadata + metadata.except("cookies", "session_storage", "local_storage") + end + + def generate_visitor_id + # Generate a stable visitor ID based on IP and user agent + fingerprint = "#{request.remote_ip}:#{request.user_agent}" + "v_#{Digest::SHA256.hexdigest(fingerprint)[0..16]}" + end + + def build_cassette(recording, format) + cassette = { + name: recording.name, + recorded_at: recording.created_at.iso8601, + duration_ms: recording.duration_ms, + action_count: recording.action_count, + actions: recording.recording_actions.ordered.map do |action| + { + type: action.action_type, + sequence: action.sequence, + timestamp_ms: action.timestamp_ms, + selector: action.selector, + value: action.value, + metadata: action.metadata + } + end + } + + # Include screenshots as base64 if requested + if params[:include_screenshots] == "true" + cassette[:actions].each_with_index do |action_data, i| + action = recording.recording_actions.find_by(sequence: action_data[:sequence]) + if action&.screenshot_key + snapshot = RecordingSnapshot.find_by(storage_key: action.screenshot_key) + if snapshot&.file&.attached? + action_data[:screenshot_base64] = Base64.encode64(snapshot.file.download) + end + end + end + end + + cassette + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/templates_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/templates_controller.rb new file mode 100644 index 00000000..24f3dee0 --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/templates_controller.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + class TemplatesController < BaseController + allow_unauthenticated_access only: [ :index, :show ] + + # GET /api/templates + def index + @templates = AgentTemplate.public_templates.order(usage_count: :desc) + + # Filter by category + @templates = @templates.by_category(params[:category]) if params[:category].present? + + # Featured only + @templates = @templates.featured if params[:featured].present? + + render json: { + templates: @templates.map { |t| template_json(t) }, + categories: AgentTemplate::CATEGORIES + } + end + + # GET /api/templates/:id + def show + @template = AgentTemplate.find(params[:id]) + render json: { template: template_json(@template, include_details: true) } + end + + # POST /api/templates/:id/use + def use + @template = AgentTemplate.find(params[:id]) + + agent = @template.create_agent_for( + current_user, + name: params[:name] || @template.name + ) + + if agent.persisted? + render json: { agent: agent_json(agent) }, status: :created + else + render json: { errors: agent.errors.full_messages }, status: :unprocessable_entity + end + end + + private + + def template_json(template, include_details: false) + json = { + id: template.id, + name: template.name, + slug: template.slug, + description: template.description, + category: template.category, + provider: template.provider, + model: template.model, + preset_type: template.preset_type, + appearance: template.appearance, + icon: template.icon, + usage_count: template.usage_count, + featured: template.featured, + tools: template.tools + } + + if include_details + json.merge!( + instructions: template.instructions, + instruction_sets: template.instruction_sets, + model_config: template.model_config + ) + end + + json + end + + def agent_json(agent) + { + id: agent.id, + name: agent.name, + slug: agent.slug, + description: agent.description, + provider: agent.provider, + model: agent.model, + status: agent.status, + preset_type: agent.preset_type, + appearance: agent.appearance + } + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/trace_reports_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/trace_reports_controller.rb new file mode 100644 index 00000000..e7eb7fe4 --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/trace_reports_controller.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + # Read API for telemetry traces, backing the dashboard's Traces view. + # + # Query logic mirrors the server-rendered TracesController (same scopes + # on ActiveAgent::Dashboard.trace_model), scoped to the caller's tenant. + # Named apart from Api::TracesController because that one is the ingest + # endpoint: same /api/traces path, different verb and different auth. + class TraceReportsController < BaseController + before_action :require_owner! + + DEFAULT_WINDOW_MINUTES = 60 + MAX_WINDOW_MINUTES = 60 * 24 * 90 + DEFAULT_LIMIT = 500 + + # GET /api/traces + def index + window = params.fetch(:minutes, DEFAULT_WINDOW_MINUTES).to_i.clamp(1, MAX_WINDOW_MINUTES) + window_scope = traces_scope.for_date_range(window.minutes.ago, Time.current) + + scope = window_scope + scope = scope.for_agent(params[:agent]) if params[:agent].present? + scope = scope.for_service(params[:service]) if params[:service].present? + scope = scope.with_errors if params[:status] == "error" + + limit = params.fetch(:limit, DEFAULT_LIMIT).to_i.clamp(1, 1000) + traces = scope.recent.limit(limit) + + render json: { + traces: traces.map { |trace| TelemetryTraceSerializer.summary(trace) }, + agents: window_scope.distinct.pluck(:agent_class).compact.sort, + # agent_class => platform Agent id, so the Traces view can link a + # trace back to the agent it belongs to (AgentRegistrar attributes + # SDK-reported traces to an Agent record on ingest). + agent_ids: agent_ids_by_class(window_scope), + window_minutes: window + } + end + + # GET /api/traces/:id — accepts the record id or the OTel trace_id + # (full or 8-char short form), so generation refs can deep-link. + def show + trace = traces_scope.find_by(trace_id: params[:id]) || + traces_scope.where("trace_id LIKE ?", "#{ActiveAgent::Dashboard.trace_model.sanitize_sql_like(params[:id].to_s)}%").first || + traces_scope.find(params[:id]) + render json: { trace: TelemetryTraceSerializer.detail(trace) } + end + + private + + def traces_scope + ActiveAgent::Dashboard.trace_model.for_account(current_account) + end + + # One grouped query. A class can appear under several agent records + # (same class, different action); the most recently active one wins, + # since that's what the operator most likely means by "this agent". + def agent_ids_by_class(scope) + scope + .where.not(agent_id: nil, agent_class: nil) + .group(:agent_class) + .maximum(:agent_id) + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb index 89b34be4..5dc48d2e 100644 --- a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb @@ -18,6 +18,14 @@ class ApplicationController < ActionController::Base helper_method :current_user, :current_owner + # Opts an action out of the host app's authentication — for the handful + # of endpoints that are deliberately public (the template gallery, the + # demo sandbox). Mirrors the Rails 8 authentication generator's helper + # so controllers read the same either side of the extraction. + def self.allow_unauthenticated_access(**options) + skip_before_action :authenticate_dashboard!, **options + end + private def authenticate_dashboard! diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/dashboard_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/dashboard_controller.rb index 9286b9bd..99305c86 100644 --- a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/dashboard_controller.rb +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/dashboard_controller.rb @@ -2,76 +2,57 @@ module ActiveAgent module Dashboard - # Main dashboard controller showing overview metrics and recent activity. + # Serves the React dashboard. # + # Every path under the mount renders this one action; routing below it + # happens in the browser. Initial state is handed over as a JSON data + # attribute rather than through Inertia, so the engine works in any host + # app without adding a frontend framework to it. class DashboardController < ApplicationController - def index - unless ActiveAgent::Dashboard.use_inertia && defined?(InertiaRails) - # No ERB overview ships yet — the traces list is the dashboard. - # Loading agent/run data here would also require the full - # dashboard tables, which telemetry-only installs don't have. - return redirect_to(traces_path) - end + # The React dashboard brings its own chrome, so it does not use the + # server-rendered layout the traces views share. + layout -> { ActiveAgent::Dashboard.layout || "active_agent/dashboard/react" } - @agents = fetch_agents.limit(10) - @recent_runs = fetch_recent_runs.limit(10) - @recent_traces = fetch_recent_traces.limit(10) - @metrics = calculate_metrics - - render inertia: "Dashboard", props: { - agents: serialize_agents(@agents), - recentRuns: serialize_runs(@recent_runs), - recentTraces: serialize_traces(@recent_traces), - metrics: @metrics, - user: current_user_props, - account: current_account_props - } + def index + render "active_agent/dashboard/dashboard/index", locals: { props: props } end private - def fetch_agents - agents = Agent.order(updated_at: :desc) - agents = agents.for_owner(current_owner) if current_owner - agents - end - - def fetch_recent_runs - runs = AgentRun.includes(:agent).recent - if current_owner && ActiveAgent::Dashboard.multi_tenant? - runs = runs.joins(:agent).where(agents: { account_id: current_owner.id }) - end - runs + def props + { + user: current_user_props, + account: current_account_props, + initialAgents: serialize_agents(owner_agents.order(updated_at: :desc).limit(20)), + meta: meta_props, + mountPath: active_agent_dashboard_mount_path, + # Billing is the host app's business; the dashboard renders no + # subscription chrome unless it is told about one. + subscription: nil + } end - def fetch_recent_traces - traces = ActiveAgent::Dashboard.trace_model.recent - traces = traces.for_account(current_owner) if current_owner && ActiveAgent::Dashboard.multi_tenant? - traces + def owner_agents + Agent.for_owner(current_owner) end - def calculate_metrics - traces_24h = ActiveAgent::Dashboard.trace_model.where("created_at > ?", 24.hours.ago) - runs_24h = AgentRun.where("created_at > ?", 24.hours.ago) - - if current_owner && ActiveAgent::Dashboard.multi_tenant? - traces_24h = traces_24h.for_account(current_owner) - runs_24h = runs_24h.joins(:agent).where(agents: { account_id: current_owner.id }) - end - + def meta_props { - total_agents: fetch_agents.count, - active_agents: fetch_agents.active_agents.count, - total_runs_24h: runs_24h.count, - successful_runs_24h: runs_24h.successful.count, - failed_runs_24h: runs_24h.failed_runs.count, - total_traces_24h: traces_24h.count, - total_tokens_24h: traces_24h.sum(:total_input_tokens).to_i + traces_24h.sum(:total_output_tokens).to_i, - avg_duration_ms: runs_24h.where.not(duration_ms: nil).average(:duration_ms)&.round || 0 + activeagentVersion: ActiveAgent::VERSION, + providers: Agent::PROVIDERS, + presetTypes: Agent::PRESET_TYPES, + instructionSets: Agent::INSTRUCTION_SETS, + availableTools: Agent::AVAILABLE_TOOLS, + executionEnabled: ActiveAgent::Dashboard.execution_enabled?, + multiTenant: ActiveAgent::Dashboard.multi_tenant?, + upgradeUrl: ActiveAgent::Dashboard.upgrade_url } end def serialize_agents(agents) + agents = agents.to_a + scorecards = AgentScorecard.for_agents(agents) + agents.map do |agent| { id: agent.id, @@ -82,48 +63,44 @@ def serialize_agents(agents) model: agent.model, status: agent.status, presetType: agent.preset_type, + appearance: agent.appearance, versionCount: agent.version_count, - updatedAt: agent.updated_at.iso8601 - } - end - end - - def serialize_runs(runs) - runs.map(&:summary) - end - - def serialize_traces(traces) - traces.map do |trace| - { - id: trace.id, - traceId: trace.trace_id, - displayName: trace.display_name, - status: trace.status, - duration: trace.formatted_duration, - tokens: trace.formatted_tokens, - timestamp: trace.timestamp&.iso8601 + createdAt: agent.created_at, + updatedAt: agent.updated_at, + stats: scorecards[agent.id] } end + rescue ActiveRecord::StatementInvalid + # Telemetry-only installs have the traces table but not the rest of + # the dashboard schema; the app still boots and shows an empty list. + [] end def current_user_props - return nil unless current_user + return { name: "Guest" } unless current_user { id: current_user.id, - name: current_user.try(:display_name) || current_user.try(:name) || current_user.try(:email), - email: current_user.try(:email) + name: current_user.try(:display_name) || current_user.try(:name) || current_user.try(:email_address), + email: current_user.try(:email_address) || current_user.try(:email) } end def current_account_props - return nil unless current_owner && ActiveAgent::Dashboard.multi_tenant? + return nil unless current_owner { id: current_owner.id, - name: current_owner.try(:name) + name: current_owner.try(:name), + telemetry_api_key: current_owner.try(:telemetry_api_key) } end + + # Where the engine is mounted, so the React app can build API URLs + # without assuming a path. + def active_agent_dashboard_mount_path + request.script_name.presence || "/" + end end end end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb index 098a6a1f..a3ec6284 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb @@ -4,6 +4,7 @@ module ActiveAgent module Dashboard class Agent < ApplicationRecord include Ownable + owned_by :user, :account has_many :agent_versions, dependent: :destroy has_many :agent_runs, dependent: :destroy diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/api_key.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/api_key.rb index c0dadfa4..827bed40 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/api_key.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/api_key.rb @@ -15,6 +15,7 @@ class ApiKey < ApplicationRecord TOKEN_PREFIX = "aa_" include Ownable + owned_by :account, :user encrypts :token, deterministic: true if ActiveAgent::Dashboard.encrypt_credentials diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb index 3530427f..f7bf0327 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb @@ -18,31 +18,11 @@ class ApplicationRecord < ::ActiveRecord::Base self.abstract_class = true + # Models that are not themselves owned still answer the ownership + # questions, so callers can scope any dashboard relation uniformly. class << self - # Returns the owner association name based on configuration. - # In multi-tenant mode, this returns :account. - # In local mode, this returns :user (optional). - def owner_association - if ActiveAgent::Dashboard.multi_tenant? - :account - else - :user - end - end - - # Scopes records to the current owner (account or user). - # No-op in local mode without owner configuration. - def for_owner(owner) - return all if owner.nil? - - if ActiveAgent::Dashboard.multi_tenant? && column_names.include?("account_id") - where(account_id: owner.id) - elsif column_names.include?("user_id") - where(user_id: owner.id) - else - all - end - end + def owner_association = nil + def for_owner(_owner) = all end end end diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/provider_key.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/provider_key.rb index d6809af3..fb96f6bf 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/provider_key.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/provider_key.rb @@ -19,6 +19,7 @@ class ProviderKey < ApplicationRecord PROVIDERS = (KEY_PROVIDERS + HOST_PROVIDERS).freeze include Ownable + owned_by :account, :user encrypts :credential if ActiveAgent::Dashboard.encrypt_credentials diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb index 67f763b1..51914547 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb @@ -4,6 +4,7 @@ module ActiveAgent module Dashboard class SandboxSession < ApplicationRecord include Ownable + owned_by :user, :account belongs_to :agent_template, optional: true diff --git a/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb b/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb index 35b93a70..fb649bc9 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb @@ -3,6 +3,9 @@ module ActiveAgent module Dashboard class SessionRecording < ApplicationRecord + include Ownable + owned_by :user, :account + belongs_to :agent_run, optional: true belongs_to :sandbox_session, optional: true diff --git a/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb b/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb index 1916978f..487d4617 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb +++ b/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb @@ -52,6 +52,28 @@ class TelemetryTrace < ::ActiveRecord::Base # @param sdk_info [Hash] SDK metadata # @param account [Object, nil] Optional account for multi-tenant mode # @return [TelemetryTrace] The created trace + # Plucks [llm_model, *columns] per trace, where llm_model comes from the + # first llm span. PostgreSQL digs into the spans jsonb in SQL so span + # payloads never reach Ruby; other adapters read the column back and dig + # in Ruby, which costs more but keeps the dashboard adapter-agnostic. + def self.pluck_with_llm_model(scope, *columns) + if postgres? + scope.pluck( + Arel.sql( + "(SELECT s.value -> 'attributes' ->> 'llm.model' " \ + "FROM jsonb_array_elements(spans) AS s " \ + "WHERE s.value ->> 'type' = 'llm' LIMIT 1)" + ), + *columns + ) + else + scope.pluck(:spans, *columns).map do |spans, *rest| + llm = Array(spans).find { |span| span.is_a?(Hash) && span["type"].to_s == "llm" } + [ llm&.dig("attributes", "llm.model"), *rest ] + end + end + end + def self.create_from_payload(trace, sdk_info = {}, account: nil) spans = trace["spans"] || [] root_span = spans.find { |s| s["parent_span_id"].nil? } || spans.first || {} diff --git a/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/ownable.rb b/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/ownable.rb index a0b9f103..04b6f81c 100644 --- a/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/ownable.rb +++ b/lib/active_agent/dashboard/app/models/concerns/active_agent/dashboard/ownable.rb @@ -4,45 +4,74 @@ module ActiveAgent module Dashboard # Attaches a dashboard record to whatever the host app calls an owner. # - # Multi-tenant installs own records through an Account, single-tenant - # installs through a User, and a single-user self-hosted install owns - # nothing at all — the associations simply aren't declared, so the engine - # boots in an app with no User model. + # Each model names the associations that could own it, most preferred + # first, and the first one the host app has actually configured wins: # - # Both foreign keys exist in the engine's schema either way, so switching - # a deployment between modes is a configuration change, not a migration. + # class Agent < ApplicationRecord + # include Ownable + # owned_by :user, :account + # end + # + # So an app with users scopes agents per user, an app with only accounts + # scopes them per account, and a single-user self-hosted install declares + # neither association and owns everything implicitly. That ordering is + # per-model on purpose: an app can reasonably keep agents per user while + # keeping API keys per account, and the platform does exactly that. + # + # Both foreign keys exist in the engine's schema either way, so moving a + # deployment between shapes is a configuration change, not a migration. module Ownable extend ActiveSupport::Concern - included do - if ActiveAgent::Dashboard.account_class - belongs_to :account, class_name: ActiveAgent::Dashboard.account_class, optional: true + CLASS_FOR = { account: :account_class, user: :user_class }.freeze + + class_methods do + # Declares the candidate owners for this model, most preferred first. + def owned_by(*candidates) + @owner_candidates = candidates.map(&:to_sym) + + @owner_candidates.each do |candidate| + class_name = ActiveAgent::Dashboard.public_send(CLASS_FOR.fetch(candidate)) + next if class_name.blank? + + belongs_to candidate, class_name: class_name, optional: true + end end - if ActiveAgent::Dashboard.user_class - belongs_to :user, class_name: ActiveAgent::Dashboard.user_class, optional: true + def owner_candidates + @owner_candidates || [] end - scope :owned_by, ->(owner) { for_owner(owner) } + # The association this install owns the model through, or nil when the + # host app configured no owner model at all. + def owner_association + owner_candidates.find { |c| ActiveAgent::Dashboard.public_send(CLASS_FOR.fetch(c)).present? } + end + + # Scopes to records owned by +owner+. A nil owner means "everything", + # which is the single-user case; an unowned model is never filtered. + def for_owner(owner) + return all if owner.nil? + + case owner_association + when :account then where(account_id: owner.id) + when :user then where(user_id: owner.id) + else all + end + end end - # The record's owner under the current mode, or nil when unowned. + # The record's owner under the current configuration, or nil. def owner - if ActiveAgent::Dashboard.multi_tenant? - respond_to?(:account) ? account : nil - else - respond_to?(:user) ? user : nil - end + association = self.class.owner_association + association && public_send(association) end - # Assigns +owner+ to whichever association this mode uses. Ignored when - # the host app configured no owner model. + # Assigns +owner+ to whichever association this install uses. A no-op + # when the host app configured no owner model. def owner=(record) - if ActiveAgent::Dashboard.multi_tenant? - self.account = record if respond_to?(:account=) - elsif respond_to?(:user=) - self.user = record - end + association = self.class.owner_association + public_send(:"#{association}=", record) if association end end end diff --git a/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_scorecard.rb b/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_scorecard.rb index ed4cf1cf..57f8daba 100644 --- a/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_scorecard.rb +++ b/lib/active_agent/dashboard/app/services/active_agent/dashboard/agent_scorecard.rb @@ -134,29 +134,13 @@ def self.run_models(runs) end private_class_method :run_models - # The same, for reported traces: the model is on the first llm span, - # dug out of the spans JSON. + # The same, for reported traces: the model is on the first llm span. def self.trace_models(traces) table = ActiveAgent::Dashboard.trace_model.table_name - if ActiveAgent::Dashboard.trace_model.postgres? - traces.pluck( - Arel.sql("#{table}.agent_id"), - Arel.sql( - "(SELECT s.value -> 'attributes' ->> 'llm.model' " \ - "FROM jsonb_array_elements(spans) AS s " \ - "WHERE s.value ->> 'type' = 'llm' LIMIT 1)" - ), - Arel.sql("total_input_tokens"), - Arel.sql("total_output_tokens") - ) - else - traces.pluck(Arel.sql("#{table}.agent_id"), :spans, :total_input_tokens, :total_output_tokens) - .map do |agent_id, spans, input, output| - llm = Array(spans).find { |span| span.is_a?(Hash) && span["type"].to_s == "llm" } - [ agent_id, llm&.dig("attributes", "llm.model"), input, output ] - end - end + ActiveAgent::Dashboard.trace_model.pluck_with_llm_model( + traces, Arel.sql("#{table}.agent_id"), :total_input_tokens, :total_output_tokens + ).map { |model, agent_id, input, output| [ agent_id, model, input, output ] } end private_class_method :trace_models diff --git a/lib/active_agent/dashboard/app/views/active_agent/dashboard/dashboard/index.html.erb b/lib/active_agent/dashboard/app/views/active_agent/dashboard/dashboard/index.html.erb new file mode 100644 index 00000000..73b6f037 --- /dev/null +++ b/lib/active_agent/dashboard/app/views/active_agent/dashboard/dashboard/index.html.erb @@ -0,0 +1,5 @@ +<%# + The React dashboard's mount point. Initial state rides along as a JSON data + attribute — no Inertia, no framework requirement on the host app. +%> +
diff --git a/lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/react.html.erb b/lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/react.html.erb new file mode 100644 index 00000000..e5b7c352 --- /dev/null +++ b/lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/react.html.erb @@ -0,0 +1,19 @@ + + + + + + Active Agent + <%= csrf_meta_tags %> + <%# + Bundles ship prebuilt in the gem (see lib/active_agent/dashboard/frontend) + and are added to the host app's asset paths by the engine, so mounting the + dashboard never asks the host to run a JavaScript build. + %> + <%= stylesheet_link_tag "active_agent_dashboard", "data-turbo-track": "reload" %> + <%= javascript_include_tag "active_agent_dashboard", type: "module", defer: true %> + + + <%= yield %> + + diff --git a/lib/active_agent/dashboard/config/routes.rb b/lib/active_agent/dashboard/config/routes.rb index dfab612f..06590c14 100644 --- a/lib/active_agent/dashboard/config/routes.rb +++ b/lib/active_agent/dashboard/config/routes.rb @@ -1,19 +1,124 @@ # frozen_string_literal: true ActiveAgent::Dashboard::Engine.routes.draw do - # The functional dashboard surface: traces + metrics. - root to: "traces#index" + # The React dashboard. Every path below it is routed client-side, so the + # controller renders the same page for all of them and the browser's back + # button keeps working. + root to: "dashboard#index" get "dashboard", to: "dashboard#index" + get "dashboard/*path", to: "dashboard#index" - resources :traces, only: [ :index, :show ] do + # The server-rendered traces surface. Older installs linked straight to it, + # and it stays useful where the JS bundle can't run. + resources :traces, only: [ :index, :show ], controller: "traces" do collection do get :metrics end end - # Telemetry ingestion, relative to wherever the engine is mounted: - # /api/traces (e.g. /activeagents/api/traces at the default mount). + # The dashboard's own JSON API, read and written by the React app. namespace :api do + # Telemetry ingestion, relative to wherever the engine is mounted: + # /api/traces (e.g. /activeagents/api/traces at the default mount). + # Authenticated with a bearer token, not a session. resources :traces, only: [ :create ] + + # A JSON API has no :new or :edit forms to serve. + resources :agents, except: [ :new, :edit ] do + member do + get :versions + post :restore + get :runs + post :execute + post :test + post :duplicate + get :export + get :analytics + end + collection do + get :presets + end + end + + resources :templates, only: [ :index, :show ] do + member do + post :use + end + end + + resources :runs, controller: "agent_runs", only: [ :index, :show ] do + member do + post :cancel + end + end + + # Sandboxes. The engine ships the in-memory backend; an operator registers + # real ones (see ActiveAgent::Dashboard.sandbox_backends). + resources :sandboxes, param: :id, only: [ :index, :create, :show, :destroy ] do + collection do + post :compare + end + member do + post :run + end + end + + resources :instance_tiers, only: [ :index, :show ] do + collection do + get :recommend + get :pricing + end + end + + resources :session_recordings, only: [ :index, :show, :destroy ] do + member do + get :actions + get "snapshot/:action_id", action: :snapshot, as: :snapshot + post :export + post :handoff + post :record_action + post :complete, action: :complete_session + end + collection do + get :recent + get :demo + post :start_user_session + end + end + + resource :analytics, only: [], controller: "analytics" do + get "/", action: :index + end + + # Reading traces is the same path as ingesting them, separated by verb: + # POST is the SDK's authenticated-by-token ingest above, GET is the + # dashboard's session-authenticated read. + resources :traces, only: [ :index, :show ], controller: "trace_reports", as: :trace_reports + resource :metrics, only: [ :show ], controller: "metrics" + + # Conversations (contexts, messages, generations) behind Interactions. + resources :interactions, only: [ :index, :show ] + + # Agent output evaluations. + resources :evaluations, only: [ :index, :show, :create, :destroy ] do + member do + post :run + end + end + + # Credentials: dashboard API keys (token shown once on create) and the + # owner's own LLM provider credentials, both encrypted at rest. + resources :api_keys, only: [ :index, :create, :destroy ] + resources :provider_keys, only: [ :index, :create, :destroy ], param: :provider + + # Model catalogs for the agent builder (Ollama queried live from the + # configured host; hosted providers curated). + resources :provider_models, only: [ :index ] end + + # The account's agents presented as an authenticated MCP server (tools + + # agent:// resources) over Streamable HTTP JSON-RPC. Authenticated with a + # dashboard API key rather than a session, so it sits outside the api + # namespace's session-authenticated controllers. + post "mcp", to: "api/mcp#create" end diff --git a/lib/active_agent/dashboard/db/migrate/20260812000001_create_active_agent_dashboard_tables.rb b/lib/active_agent/dashboard/db/migrate/20260812000001_create_active_agent_dashboard_tables.rb index 6cad0289..7761ebbf 100644 --- a/lib/active_agent/dashboard/db/migrate/20260812000001_create_active_agent_dashboard_tables.rb +++ b/lib/active_agent/dashboard/db/migrate/20260812000001_create_active_agent_dashboard_tables.rb @@ -247,9 +247,13 @@ def change t.integer :action_count, default: 0 t.integer :duration_ms t.column :metadata, json_type, default: {} + t.bigint :user_id + t.bigint :account_id t.timestamps t.index :agent_run_id t.index :sandbox_session_id + t.index :user_id + t.index :account_id end create_table "#{prefix}recording_actions" do |t| diff --git a/lib/active_agent/dashboard/engine.rb b/lib/active_agent/dashboard/engine.rb index 13fb0a03..69914810 100644 --- a/lib/active_agent/dashboard/engine.rb +++ b/lib/active_agent/dashboard/engine.rb @@ -38,6 +38,19 @@ def self.dashboard_root end config.active_agent_dashboard = ActiveSupport::OrderedOptions.new + + # The dashboard's JS and CSS ship prebuilt in the gem. Adding the + # directory to the host app's asset paths is what lets a plain + # `mount ActiveAgent::Dashboard::Engine` work without the host running + # a JavaScript build — or having a JavaScript build at all. + initializer "active_agent.dashboard.assets", before: :append_assets_path do |app| + builds = root.join("app", "assets", "builds").to_s + next unless File.directory?(builds) + + if app.config.respond_to?(:assets) && app.config.assets.respond_to?(:paths) + app.config.assets.paths << builds unless app.config.assets.paths.include?(builds) + end + end end end end diff --git a/lib/active_agent/dashboard/frontend/components/AgentAvatar.jsx b/lib/active_agent/dashboard/frontend/components/AgentAvatar.jsx new file mode 100644 index 00000000..8366f73e --- /dev/null +++ b/lib/active_agent/dashboard/frontend/components/AgentAvatar.jsx @@ -0,0 +1,197 @@ +import React from 'react'; + +/** + * Agent Avatar Component + * + * Renders the ActiveAgent mascot (red face, sunglasses, hat) matching + * the hero SVG from the landing page. Supports decorative overlays + * for instructions (above) and tools (below) to match the lander's + * agent builder preview pattern. + * + * @example + * + * + */ + +// Instruction/tool metadata matching the lander's Stimulus controller +export const INSTRUCTIONS = { + github: { emoji: '\u{1F419}', label: 'GitHub' }, + ruby: { emoji: '\u{1F48E}', label: 'Ruby' }, + rails: { emoji: '\u{1F6E4}\uFE0F', label: 'Rails' }, + aws: { emoji: '\u2601\uFE0F', label: 'AWS' }, + gcp: { emoji: '\u{1F310}', label: 'GCP' }, + python: { emoji: '\u{1F40D}', label: 'Python' }, + typescript: { emoji: '\u{1F4D8}', label: 'TypeScript' }, + docker: { emoji: '\u{1F433}', label: 'Docker' }, + kubernetes: { emoji: '\u2638\uFE0F', label: 'Kubernetes' }, +}; + +export const TOOLS = { + terminal: { emoji: '\u{1F4BB}', label: 'Terminal' }, + playwright: { emoji: '\u{1F3AD}', label: 'Playwright' }, + filesystem: { emoji: '\u{1F4C1}', label: 'Filesystem' }, + code: { emoji: '\u{1F468}\u200D\u{1F4BB}', label: 'Code' }, + database: { emoji: '\u{1F5C4}\uFE0F', label: 'Database' }, + slack: { emoji: '\u{1F4AC}', label: 'Slack' }, + fetch: { emoji: '\u{1F310}', label: 'Fetch' }, + search: { emoji: '\u{1F50D}', label: 'Search' }, + edit: { emoji: '\u270F\uFE0F', label: 'Edit' }, + translate: { emoji: '\u{1F30D}', label: 'Translate' }, + memory: { emoji: '\u{1F9E0}', label: 'Memory' }, +}; + +// Preset configurations matching the lander's agent builder presets +export const AGENT_PRESETS = { + terminal: { instructions: ['github'], tools: ['terminal', 'code'] }, + webDeveloper: { instructions: ['typescript'], tools: ['playwright', 'fetch'] }, + documentAnalysis: { instructions: ['python'], tools: ['database', 'search', 'code'] }, + writing: { instructions: ['ruby'], tools: ['edit', 'memory'] }, + translation: { instructions: ['ruby', 'python', 'typescript'], tools: ['translate', 'code'] }, + playwright: { instructions: ['typescript'], tools: ['playwright', 'fetch'] }, + research: { instructions: ['github'], tools: ['fetch', 'search', 'memory'] }, + imageAnalysis: { instructions: ['python'], tools: ['filesystem', 'code'] }, + computerUse: { instructions: ['github'], tools: ['terminal', 'playwright'] }, + productDesign: { instructions: ['typescript'], tools: ['playwright', 'code'] }, +}; + +// Keep legacy theme exports for any existing references +export const AGENT_THEMES = { + default: { primary: '#EB5555', secondary: '#D43B3B', highlight: '#FF6B6B' }, + terminal: { primary: '#EB5555', secondary: '#D43B3B', highlight: '#FF6B6B' }, + research: { primary: '#EB5555', secondary: '#D43B3B', highlight: '#FF6B6B' }, + translation: { primary: '#EB5555', secondary: '#D43B3B', highlight: '#FF6B6B' }, + writing: { primary: '#EB5555', secondary: '#D43B3B', highlight: '#FF6B6B' }, +}; + +/** + * The core mascot SVG - matches activeagent-hero.svg from the landing page. + * Red pentagon face, black hat, sunglasses with eyes behind them. + */ +const MascotSVG = ({ id = 'mascot' }) => ( + + + {/* Face */} + + + + {/* Mouth */} + + {/* Hat */} + + {/* Eyes (behind lenses) */} + + + + + + + + + + + {/* Lenses */} + + + + + + + {/* Frames */} + + + + {/* Sparkles */} + + + + + + +); + +/** + * AgentAvatar - Renders the ActiveAgent mascot matching the landing page design + * + * @param {number} size - Size in pixels (default: 200) + * @param {string[]} instructions - Array of instruction IDs to show above + * @param {string[]} tools - Array of tool IDs to show below + * @param {boolean} showDecorations - Whether to show instruction/tool badges + * @param {string} className - Additional CSS classes + */ +export default function AgentAvatar({ + size = 200, + instructions = [], + tools = [], + showDecorations = false, + className = '', + // Legacy props (kept for backward compatibility) + hat, + hatAccessory, + heldItem, + theme, + customColors, +}) { + return ( +
+ {showDecorations && instructions.length > 0 && ( +
+ {instructions.map(id => { + const instr = INSTRUCTIONS[id]; + return instr ? ( + {instr.emoji} + ) : null; + })} +
+ )} + + ActiveAgent + + + {showDecorations && tools.length > 0 && ( +
+ {tools.map(id => { + const tool = TOOLS[id]; + return tool ? ( + {tool.emoji} + ) : null; + })} +
+ )} +
+ ); +} + +// Helper component for using presets +export function PresetAgentAvatar({ preset, size = 200, className = '' }) { + const config = AGENT_PRESETS[preset] || AGENT_PRESETS.terminal; + return ( + + ); +} diff --git a/lib/active_agent/dashboard/frontend/components/dashboard/AgentAnalytics.jsx b/lib/active_agent/dashboard/frontend/components/dashboard/AgentAnalytics.jsx new file mode 100644 index 00000000..1f97153b --- /dev/null +++ b/lib/active_agent/dashboard/frontend/components/dashboard/AgentAnalytics.jsx @@ -0,0 +1,361 @@ +import React, { useState, useEffect } from 'react'; +import { useTimeWindow } from '../../contexts/TimeWindowContext'; +import { useTheme } from '../../contexts/ThemeContext'; +import { paletteFor } from '../../utils/dashboardTheme'; +import TimeWindowSelector from './TimeWindowSelector'; +import TracesView from './TracesView'; +import InteractionsView from './InteractionsView'; + +const TABS = [ + { id: 'overview', label: 'Overview' }, + { id: 'traces', label: 'Traces' }, + { id: 'interactions', label: 'Interactions' } +]; + +// embedded drops the page header and the nested tab bar: when this renders +// inside the agent detail page's Metrics tab, that page already owns the +// heading and the tab row, and Traces/Interactions are siblings there. +export default function AgentAnalytics({ agent, onBack, embedded = false }) { + // Embedded into the agent page, this renders inside a themed shell, so it + // resolves the same palette rather than staying light-only. + const { darkMode } = useTheme(); + const colors = paletteFor(darkMode); + const [analytics, setAnalytics] = useState(null); + const [isLoading, setIsLoading] = useState(true); + // This endpoint takes whole days; the shared window rounds up so a + // sub-day selection still returns the current day rather than nothing. + const { timeWindow, days: period } = useTimeWindow(); + const [activeTab, setActiveTab] = useState('overview'); + + // Correlation key between this Agent record and its telemetry traces + // (mirrors Agent#telemetry_agent_class for shallow agent objects). + const telemetryAgentClass = agent.telemetry_agent_class || + (() => { + const base = agent.agent_class_name || + `${(agent.name || '').replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase()).replace(/^./, c => c.toUpperCase()).replace(/[^a-zA-Z0-9]/g, '')}`; + return base.endsWith('Agent') ? base : `${base}Agent`; + })(); + + useEffect(() => { + loadAnalytics(); + }, [agent?.id, period]); + + const loadAnalytics = async () => { + if (!agent?.id) return; + + setIsLoading(true); + try { + const response = await fetch(`/api/agents/${agent.id}/analytics?days=${period}`); + const data = await response.json(); + setAnalytics(data); + } catch (error) { + console.error('Failed to load analytics:', error); + } finally { + setIsLoading(false); + } + }; + + const formatDuration = (ms) => { + if (!ms) return '-'; + if (ms < 1000) return `${ms}ms`; + return `${(ms / 1000).toFixed(2)}s`; + }; + + const formatNumber = (num) => { + if (!num) return '0'; + if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`; + if (num >= 1000) return `${(num / 1000).toFixed(1)}K`; + return num.toString(); + }; + + const getStatusColor = (status) => { + switch (status) { + case 'complete': return 'bg-green-500'; + case 'failed': return 'bg-red-500'; + case 'running': return 'bg-blue-500'; + case 'pending': return 'bg-yellow-500'; + case 'cancelled': return 'bg-gray-500'; + default: return 'bg-gray-400'; + } + }; + + if (isLoading && !analytics) { + return ( +
+
+
+ ); + } + + const maxRuns = analytics?.runs_by_day?.length > 0 + ? Math.max(...analytics.runs_by_day.map(d => d.count)) + : 1; + + // Embedded, this is only ever the overview — the host page owns the tabs. + const shownTab = embedded ? 'overview' : activeTab; + + return ( +
+ {/* Header */} + {embedded ? ( +
+ {timeWindow.minutes < 1440 && ( + + showing 1 day + + )} + +
+ ) : ( +
+
+ +
+

{agent.name} Analytics

+

Performance metrics and usage data

+
+
+ +
+ {timeWindow.minutes < 1440 && ( + + showing 1 day + + )} + +
+
+ )} + + {/* Shared-view tabs: Traces and Interactions are the same components + as the global observability views, scoped to this agent. */} + {!embedded && ( +
+ {TABS.map(tab => ( + + ))} +
+ )} + + {shownTab === 'traces' && ( + + )} + + {shownTab === 'interactions' && ( + + )} + + {shownTab === 'overview' && (<> + {/* Stats Cards */} +
+ + + + } + color="rose" + colors={colors} + darkMode={darkMode} + /> + + + + } + color="green" + colors={colors} + darkMode={darkMode} + /> + + + + } + color="blue" + colors={colors} + darkMode={darkMode} + /> + + + + } + color="purple" + colors={colors} + darkMode={darkMode} + /> +
+ +
+ {/* Runs Chart */} +
+

Runs Over Time

+ {analytics?.runs_by_day?.length > 0 ? ( +
+
+ {analytics.runs_by_day.map((day, i) => ( +
+
+
0 ? '4px' : '0' }} + >
+
+ {day.count} runs +
+
+ {(i === 0 || i === analytics.runs_by_day.length - 1 || analytics.runs_by_day.length <= 7) && ( + + {new Date(day.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} + + )} +
+ ))} +
+
+ ) : ( +
+ No data for this period +
+ )} +
+ + {/* Status Breakdown */} +
+

Status Breakdown

+ {Object.keys(analytics?.status_breakdown || {}).length > 0 ? ( +
+ {Object.entries(analytics.status_breakdown).map(([status, count]) => { + const total = analytics?.summary?.total_runs || 1; + const percentage = ((count / total) * 100).toFixed(1); + return ( +
+
+ {status} + {count} ({percentage}%) +
+
+
+
+
+ ); + })} +
+ ) : ( +
+ No runs yet +
+ )} +
+
+ + {/* Recent Errors */} + {analytics?.recent_errors?.length > 0 && ( +
+

Recent Errors

+
+ {analytics.recent_errors.map((error) => ( +
+ + + +
+

{error.error || 'Unknown error'}

+

+ {new Date(error.created_at).toLocaleString()} +

+
+
+ ))} +
+
+ )} + + {/* Quick Stats */} +
+
+

Completed Runs

+

{analytics?.summary?.completed_runs || 0}

+
+
+

Failed Runs

+

{analytics?.summary?.failed_runs || 0}

+
+
+

Avg Tokens/Run

+

{formatNumber(analytics?.summary?.avg_tokens_per_run)}

+
+
+

Period

+

{analytics?.period_days || 30} days

+
+
+ )} +
+ ); +} + +// The icon tints stay hue-coded but resolve their surface per theme: a solid +// -100 tint reads as a bright patch on a dark card. +const STAT_TINTS = { + rose: ['#dc2626', 'rgba(220,38,38,0.18)', '#fee2e2'], + green: ['#16a34a', 'rgba(22,163,74,0.18)', '#dcfce7'], + blue: ['#2563eb', 'rgba(37,99,235,0.18)', '#dbeafe'], + purple: ['#7c3aed', 'rgba(124,58,237,0.18)', '#ede9fe'] +}; + +function StatCard({ title, value, icon, color, colors, darkMode }) { + const [hue, darkTint, lightTint] = STAT_TINTS[color] || STAT_TINTS.rose; + + return ( +
+
+
+

{title}

+

{value}

+
+
+ {icon} +
+
+
+ ); +} diff --git a/lib/active_agent/dashboard/frontend/components/dashboard/AgentBuilder.jsx b/lib/active_agent/dashboard/frontend/components/dashboard/AgentBuilder.jsx new file mode 100644 index 00000000..bbcc02f6 --- /dev/null +++ b/lib/active_agent/dashboard/frontend/components/dashboard/AgentBuilder.jsx @@ -0,0 +1,456 @@ +import React, { useState, useEffect } from 'react'; +import AgentAvatar, { AGENT_PRESETS, INSTRUCTIONS, TOOLS } from '../AgentAvatar'; +import { ICONS } from '../../utils/designTokens'; +import { FALLBACK_PROVIDER_MODELS, fetchProviderModels } from '../../utils/providerModels'; + +const STEPS = [ + { id: 'basics', label: 'Basics', icon: '1' }, + { id: 'configure', label: 'Configure', icon: '2' }, + { id: 'review', label: 'Review', icon: '3' } +]; + +export default function AgentBuilder({ meta, onSave, onCancel, isLoading }) { + const [currentStep, setCurrentStep] = useState(0); + const [providerModels, setProviderModels] = useState(FALLBACK_PROVIDER_MODELS); + const [formData, setFormData] = useState({ + name: '', + description: '', + provider: 'openai', + model: FALLBACK_PROVIDER_MODELS.openai[0], + instructions: '', + preset_type: 'terminal', + instruction_sets: ['github'], + tools: ['terminal', 'code'], + model_config: { + temperature: 0.7 + } + }); + + // Load the provider's current model catalog (Ollama/OpenRouter live, + // hosted providers curated server-side); keep the selection valid. + useEffect(() => { + let cancelled = false; + fetchProviderModels(formData.provider).then(models => { + if (cancelled || models.length === 0) return; + setProviderModels(prev => ({ ...prev, [formData.provider]: models })); + setFormData(prev => models.includes(prev.model) ? prev : { ...prev, model: models[0] }); + }); + return () => { cancelled = true; }; + }, [formData.provider]); + + const updateField = (field, value) => { + setFormData(prev => ({ ...prev, [field]: value })); + }; + + const toggleArrayItem = (field, item) => { + setFormData(prev => { + const current = prev[field] || []; + const updated = current.includes(item) + ? current.filter(i => i !== item) + : [...current, item]; + return { ...prev, [field]: updated }; + }); + }; + + const selectPreset = (presetId) => { + updateField('preset_type', presetId); + if (AGENT_PRESETS[presetId]) { + const preset = AGENT_PRESETS[presetId]; + updateField('instruction_sets', [...preset.instructions]); + updateField('tools', [...preset.tools]); + } + }; + + const canProceed = () => { + switch (currentStep) { + case 0: return formData.name.trim().length >= 2; + case 1: return true; + case 2: return true; + default: return false; + } + }; + + const handleSubmit = () => { + onSave(formData); + }; + + return ( +
+ {/* Progress Steps */} +
+
+ {STEPS.map((step, index) => ( +
+
+ {index < currentStep ? '✓' : step.icon} +
+ + {step.label} + + {index < STEPS.length - 1 && ( +
+ )} +
+ ))} +
+
+ + {/* Step Content */} +
+ {currentStep === 0 && ( + + )} + {currentStep === 1 && ( + + )} + {currentStep === 2 && ( + + )} +
+ + {/* Navigation */} +
+ + + +
+
+ ); +} + +// Step 1: Basics +function BasicsStep({ formData, updateField, providerModels }) { + return ( +
+

Basic Information

+

Give your agent a name and configure its AI provider.

+ +
+
+ + updateField('name', e.target.value)} + placeholder="My Awesome Agent" + className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent" + /> +
+ +
+ +