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/AGENTS.md b/AGENTS.md index 589ac2ae..3c648152 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,6 +11,8 @@ | Provider implementations | `lib/active_agent/providers/` | | Agent concerns/mixins | `lib/active_agent/concerns/` | | Rails generators | `lib/generators/active_agent/` | +| Dashboard engine (`actionagent` gem) | `actionagent/` | +| Dashboard React source | `actionagent/frontend/` | | Test suite | `test/` | | Test Rails app | `test/dummy/` | | Documentation source | `docs/` | @@ -79,8 +81,11 @@ end ### Template Structure -Templates live in `app/views/agents/{agent_name}/`: -- `instructions.md.erb` - System prompt (shared across actions) +Templates live in `app/views/agents/{name}/`, where `{name}` is the class name +underscored with the `_agent` suffix dropped — `TravelAgent` looks in +`app/views/agents/travel/` (`_prefixes` in `lib/active_agent/concerns/view.rb` +also searches the full underscored class name, `app/views/travel_agent/`): +- `instructions.md` - System prompt (shared across actions); `.md.erb` works too - `{action_name}.md.erb` - Action-specific prompt template ### Provider Configuration @@ -102,11 +107,13 @@ development: rails generate active_agent:agent AgentName action1 action2 ``` -Creates: +Creates (the view directory drops the `_agent` suffix, the agent file keeps it): - `app/agents/agent_name_agent.rb` -- `app/views/agents/agent_name_agent/instructions.md.erb` -- `app/views/agents/agent_name_agent/action1.md.erb` -- `app/views/agents/agent_name_agent/action2.md.erb` +- `app/views/agents/agent_name/instructions.md` +- `app/views/agents/agent_name/action1.md.erb` +- `app/views/agents/agent_name/action2.md.erb` +- `test/agents/agent_name_agent_test.rb` and + `test/docs/previews/agent_name_agent_preview.rb` ### Adding a Tool to an Agent @@ -167,12 +174,28 @@ end ### Adding a New Provider -1. Create `lib/active_agent/providers/my_provider.rb` -2. Create `lib/active_agent/providers/my_provider/` directory with: - - `client.rb` - API client wrapper - - `request.rb` - Request building - - `response.rb` - Response parsing -3. Register in `lib/active_agent/providers.rb` +1. Create `lib/active_agent/providers/{service}_provider.rb` defining + `ActiveAgent::Providers::{Service}Provider` — `gemini_provider.rb` holds + `GeminiProvider`. Subclass `BaseProvider` (`providers/_base_provider.rb`) or + an existing provider. If it needs a client gem, call + `require_gem!(:key, __FILE__)` at the top — the key must exist in + `GEM_LOADERS` at the top of `_base_provider.rb` (`:anthropic`, `:openai`, + `:ruby_llm` today), which is also where the gem's version requirement + lives, so a new client gem means a new entry there +2. Put the supporting pieces in `lib/active_agent/providers/{service}/` — the + shipped providers keep `options.rb`, `request.rb`, `_types.rb` and any + transforms there rather than in one file (see `providers/anthropic/`) +3. There is no registry file to edit. `provider_load` + (`lib/active_agent/concerns/provider.rb`) requires + `active_agent/providers/#{service_name.underscore}_provider` and then + const_gets `ActiveAgent::Providers::#{Service}Provider`, so the file name is + the registration. `service_name` is the config's `service:` value, or the + provider key camelized when the config omits it (`:openai` → `"Openai"`). + `PROVIDER_SERVICE_NAMES_REMAPS`, in that same file, fixes only the constant + half when that string doesn't camelize to your class name (`"Openai"` → + `"OpenAI"`); the require still uses the un-remapped name, which is why + `openai_provider.rb` exists next to `open_ai_provider.rb` as a one-line + `require_relative`. A remap without that alias file is a LoadError ## File Naming Conventions @@ -186,7 +209,7 @@ end ## Testing ```bash -# Run all tests +# Run the framework's tests (bare bin/test collects test/**/*_test.rb only) bin/test # Run specific test file @@ -194,8 +217,14 @@ bin/test test/path/to/test.rb # Run tests for a specific provider bin/test test/integration/open_ai/ + +# Run both trees — the framework's test/ and the engine's actionagent/test/ +bundle exec rake test ``` +The engine's tests live in `actionagent/test/` and are outside `bin/test`'s +default glob; the Rakefile's `test` task is what sweeps both. + ### Test Fixtures - VCR cassettes in `test/fixtures/vcr_cassettes/` @@ -230,6 +259,56 @@ bin/test test/integration/open_ai/ - Model ID determines which provider is used automatically - Supports prompts, embeddings, tool calling, and streaming +## The dashboard: a second gem in this repo + +This repo ships **two** gems. `activeagent` is the framework — agents, +providers, generation, telemetry reporting, and no Active Record. `actionagent` +is the dashboard: a mountable Rails engine under `actionagent/`, with its own +gemspec, holding traces and metrics, the agent builder, runs, conversations, +evaluations, scorecards, sandboxes, session recordings, and the +agents-as-MCP-server endpoint. + +They are separate because the dashboard needs `activerecord` and `solid_agent`, +neither of which the framework requires — and `solid_agent` depends on +`activeagent`, so the framework could never declare that second one without a +cycle. (Both gemspecs declare `railties`; that one is not part of the split.) + +| What | Where | +|------|-------| +| Gemspec | `actionagent/actionagent.gemspec` | +| Engine + configuration seams | `actionagent/lib/action_agent/engine.rb`, `actionagent/lib/action_agent.rb` | +| Routes | `actionagent/config/routes.rb` | +| Models, controllers, jobs, services, queries, serializers, views | `actionagent/app/` | +| React source (entry `index.jsx`) | `actionagent/frontend/` | +| Prebuilt JS/CSS (committed) | `actionagent/app/assets/builds/` | +| Install generator | `actionagent/lib/generators/action_agent/install_generator.rb` | +| Engine tests | `actionagent/test/` | + +- Everything in the engine is namespaced `ActionAgent::` (`ActionAgent::Agent`, + `ActionAgent::AgentRun`, `ActionAgent::AgentExecutionService`, + `ActionAgent::TelemetryTrace`, …). The engine is a normal gem root, so Rails + finds `app/` and `config/routes.rb` without help. +- `ActionAgent::Compatibility` keeps the pre-split names resolving with a + deprecation, so existing initializers and already-enqueued jobs keep + working: `ActiveAgent::Dashboard` → `ActionAgent`, + `ActiveAgent::TelemetryTrace` → `ActionAgent::TelemetryTrace`, + `ActiveAgent::ProcessTelemetryTracesJob` → + `ActionAgent::ProcessTelemetryTracesJob`. It prepends a `const_missing` onto + `ActiveAgent` rather than aliasing eagerly, so the old names don't load the + engine's models — and drag Active Record in at boot — just by existing. +- Paths are relative to wherever the host mounts the engine (the generator + writes `/activeagents`): `/api/...` for the JSON API, `/mcp` + for the MCP endpoint, `/console/traces` for the server-rendered + views; every other path outside `/api` renders the React app for + client-side routing. +- The frontend is built with `npm run build` inside `actionagent/frontend/` and the + output is committed, so host apps never run a JavaScript build. Initial + state reaches React through a JSON data attribute, not Inertia. +- Host integration goes through `ActionAgent.configure` seams + (authentication, `current_user_resolver`, `multi_tenant`, + `table_name_prefix`, `execution_enabled`, sandbox backends, quotas); all + are optional and unset means single-user self-hosted behaviour. + ## Common Gotchas 1. **Generation is lazy** - Nothing happens until `generate_now` or `prompt_later` @@ -248,6 +327,11 @@ rails generate active_agent:install # Generate agent rails generate active_agent:agent MyAgent action1 action2 +# Install the dashboard engine (migrations, mount, initializer). +# Needs the actionagent gem — it does not come with activeagent. +bundle add actionagent +rails generate action_agent:install + # Run tests bin/test @@ -260,6 +344,12 @@ bin/rubocop - Ruby 3.1+ - Rails 7.2+ / 8.0+ / 8.1+ - Provider gems (optional): `openai`, `anthropic`, `ruby_llm` +- `activeagent` depends on actionpack, actionview, activesupport, activemodel, + activejob, railties and `activeagents-telemetry` — deliberately **not** + activerecord +- `actionagent` (optional — the dashboard) adds `activerecord` and + `solid_agent` on top of `activeagent`. Installing the framework does not + install it: `bundle add actionagent` before running its generator ## Links diff --git a/CHANGELOG.md b/CHANGELOG.md index adbb278b..063edf65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,59 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.0] - Unreleased + +### ⚠️ Breaking: the dashboard is now a separate gem + +The dashboard engine that shipped inside `activeagent` has moved to a new +gem, **`actionagent`**. `activeagent` is now the framework alone — it no +longer defines `ActiveAgent::Dashboard`, and no longer pulls Active Record +into apps that do not use it. + +**If you mount the dashboard, add the new gem before upgrading:** + +```ruby +gem "activeagent", "~> 2.0" +gem "actionagent", "~> 2.0" # required if you mount the dashboard +``` + +Upgrading `activeagent` alone will fail at boot with +`NameError: uninitialized constant ActiveAgent::Dashboard`, raised by your +own initializer or by the `mount ActiveAgent::Dashboard::Engine` line in +`config/routes.rb`. This is a major version precisely so that a +`~> 1.1` constraint will not pick it up on its own. + +With `actionagent` installed, the old constants keep resolving through +`ActionAgent::Compatibility` with a deprecation warning: + +- `ActiveAgent::Dashboard` → `ActionAgent` +- `ActiveAgent::TelemetryTrace` → `ActionAgent::TelemetryTrace` +- `ActiveAgent::ProcessTelemetryTracesJob` → `ActionAgent::ProcessTelemetryTracesJob` + +That last one matters beyond tidiness: Active Job serializes the class name +into the queue payload, so jobs enqueued before the upgrade still resolve +after it. + +Other changes for mounted installs: + +- **The server-rendered traces console moves from `/traces` to + `/console/traces`.** `/traces` is now the React traces view — the same + data, with more of it. +- **The mount is authenticated everywhere but development and test.** The + sandbox API, the session-recording capture endpoints and the template + endpoints previously allowed anonymous access; they no longer do. The + `GET /api/session_recordings/demo` endpoint is removed. +- **`current_user_method` / `current_account_method` are superseded by + `current_user_resolver` / `current_account_resolver`.** The engine's + controllers are their own base class, so a host app's `current_user` + helper is not available to them. +- **An unresolved owner now scopes to nothing rather than to everything.** + If you configure `user_class` or `account_class`, make sure the matching + resolver actually returns a record, or the dashboard will show no data. +- Existing installs upgrading from the in-gem dashboard: re-run + `rails generate action_agent:install`. It detects the migrations you + already have and emits only what is missing. + ## [1.1.0] - 2026-08-12 ### Dashboard — self-hosted (enterprise) mount readiness diff --git a/Gemfile b/Gemfile index 1c5df889..a06e67b2 100644 --- a/Gemfile +++ b/Gemfile @@ -4,3 +4,6 @@ gem "debug" unless ENV["CI"] == "true" gem "rubocop-rails-omakase" gemspec + +# The dashboard engine, a sibling gem in this repo. +gemspec path: "actionagent", name: "actionagent" diff --git a/README.md b/README.md index e3f10e50..b8684c83 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,10 @@ Use bundler to add activeagent to your Gemfile and install: bundle add activeagent ``` +That is the framework — agents, providers, generation and telemetry +reporting. The dashboard is a second gem, `actionagent`, added separately +when you want it; see [Dashboard & Dev Console](#dashboard--dev-console). + Add the generation provider gem you want to use: ```bash @@ -127,14 +131,19 @@ development: service: "RubyLLM" ``` -## Dev Console & Observability +## Dashboard & Dev Console -Active Agent includes a local dev console — traces with span waterfalls, -token usage, and per-agent metrics — as a mountable Rails engine, so you -can watch your agents while you build: +The dashboard is its own gem, `actionagent`: a mountable Rails engine with +traces and span waterfalls, token usage and per-agent metrics, plus the agent +builder, runs, conversations, evaluations, scorecards and cost estimates — so +you can watch and drive your agents while you build. It ships separately +because its models are Active Record models and it runs agents through +[solid_agent](https://github.com/activeagents/solid_agent) — neither of which +`activeagent` depends on, so an app that only runs agents installs neither. ```bash -rails generate active_agent:dashboard:install +bundle add actionagent +rails generate action_agent:install rails db:migrate ``` @@ -145,13 +154,14 @@ telemetry: local_storage: true ``` -Open `/activeagents` and every generation appears as a trace. See +The generator mounts the engine at `/activeagents` — open it and every +generation appears as a trace. See [docs/framework/dashboard.md](docs/framework/dashboard.md) for -authentication, remote ingestion, and multi-tenant mode. For production -monitoring — evaluations, cost estimates, retention, team workspaces — -point telemetry at the hosted platform at -[activeagents.ai](https://activeagents.ai); every workspace starts with a -free low-volume trial. +authentication, remote ingestion, and multi-tenant mode. The hosted +platform at [activeagents.ai](https://activeagents.ai) runs this same +engine multi-tenant, adding what a hosted product has to have — accounts, +plans, billing, quotas and managed sandboxes; every workspace starts with +a free low-volume trial. ## Features @@ -199,12 +209,12 @@ response = prompt.generate_now - [Documentation](https://docs.activeagents.ai) - [Getting Started Guide](https://docs.activeagents.ai/getting_started) -- [API Reference](https://docs.activeagents.ai/docs/framework) -- [Examples](https://docs.activeagents.ai/docs/agents) +- [API Reference](https://docs.activeagents.ai/framework) +- [Examples](https://docs.activeagents.ai/agents) ## Contributing -We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. +We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.MD) for details. ## License diff --git a/Rakefile b/Rakefile index 58009e57..5d943950 100644 --- a/Rakefile +++ b/Rakefile @@ -4,10 +4,40 @@ require "rake/testtask" Rake::TestTask.new(:test) do |t| t.libs << "test" - t.test_files = FileList["test/**/*_test.rb"] + t.test_files = FileList["test/**/*_test.rb", "actionagent/test/**/*_test.rb"] .exclude("test/**/integration_test.rb") .exclude("test/dummy/tmp/**/*") t.verbose = true end task default: :test + +# bundler/gem_tasks only discovers the gemspec at the repository root, so the +# dashboard gem needs its own build path. It must be built from its own +# directory: RubyGems resolves a gemspec's file list against the working +# directory, so `gem build actionagent/actionagent.gemspec` from here would +# not find the files it lists. +namespace :actionagent do + desc "Build the actionagent gem into pkg/" + task :build do + require "fileutils" + root = File.expand_path(__dir__) + FileUtils.mkdir_p(File.join(root, "pkg")) + + Dir.chdir(File.join(root, "actionagent")) do + sh "gem build actionagent.gemspec" + version = File.read("lib/action_agent/version.rb")[/VERSION = "([^"]+)"/, 1] + gem_file = "actionagent-#{version}.gem" + + # A gem missing its own entry point is the failure mode this guards + # against — it installs and resolves, then dies on require. + contents = `tar -xOf #{gem_file} data.tar.gz | tar -tzf -` + %w[lib/action_agent.rb config/routes.rb app/assets/builds/action_agent.js].each do |required| + raise "actionagent gem is missing #{required}" unless contents.include?(required) + end + + FileUtils.mv(gem_file, File.join(root, "pkg", gem_file)) + puts "actionagent #{version} -> pkg/#{gem_file}" + end + end +end diff --git a/actionagent/LICENSE b/actionagent/LICENSE new file mode 100644 index 00000000..b063fb61 --- /dev/null +++ b/actionagent/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Active Agents AI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/actionagent/README.md b/actionagent/README.md new file mode 100644 index 00000000..ba3227a0 --- /dev/null +++ b/actionagent/README.md @@ -0,0 +1,103 @@ +# Action Agent + +The Active Agent dashboard, as a mountable Rails engine. + +Build and run agents, read their conversations, score them with evaluations, +and watch traces, metrics and costs — served from your own database, on your +own domain. + +This is a sibling gem to [`activeagent`](https://github.com/activeagents/activeagent), +released from the same repository the way `actionpack` and `actionmailer` are +released from `rails/rails`. The dependency runs one way: `actionagent` needs +`activeagent`, never the reverse. Installing the framework does not install the +dashboard, and an app that only generates with agents never loads Active +Record on its behalf. + +## Install + +```ruby +# Gemfile +gem "activeagent" +gem "actionagent" +``` + +```sh +bin/rails generate action_agent:install +bin/rails db:migrate +``` + +The generator copies the migrations, writes +`config/initializers/action_agent.rb`, and mounts the engine: + +```ruby +# config/routes.rb +mount ActionAgent::Engine => "/activeagents" +``` + +Mount it wherever you like — the client-side routes are resolved relative to +the mount point, so `/activeagents`, `/admin/agents` and `/dashboard` all work. + +### Options + +| Flag | Effect | +| --- | --- | +| `--traces-only` | Install trace ingestion alone, without the agent, run and evaluation tables | +| `--multi-tenant` | Scope traces to an account (adds `account_id` to the migration) | +| `--skip-migrations` | Don't copy the migrations | +| `--skip-routes` | Don't add the mount to `routes.rb` | + +## Configuration + +Every integration point is a lambda or a class name, so the engine adapts to +whatever your app already calls things: + +```ruby +# config/initializers/action_agent.rb +ActionAgent.configure do |config| + config.user_class = "User" + + # Resolve the signed-in user from your own session or auth library. The + # engine's controllers are their own base class, so your app's + # `current_user` helper is not available on them — and a resolver that + # calls `controller.current_user` reaches the engine's own accessor + # rather than yours, which resolves to nobody. + config.current_user_resolver = ->(controller) { + User.find_by(id: controller.session[:user_id]) + } + + # Restrict what a given owner can see. + config.agent_scope_resolver = ->(owner) { ActionAgent::Agent.where(user: owner) } +end +``` + +Once `user_class` (or `account_class`) is set, a request whose owner does not +resolve sees nothing rather than everything. If a signed-in user gets an empty +dashboard, the resolver above returned `nil`. + +See [the self-hosted observability guide](https://docs.activeagents.ai/framework/self-hosted-observability) +for the full list. + +## Assets + +The dashboard's JavaScript and CSS ship prebuilt in the gem, under +`app/assets/builds`. Host apps never run a JavaScript build — there is nothing +to install, compile or configure. The React sources live in `frontend/` in the +repository and are deliberately excluded from the packaged gem. + +## Upgrading from activeagent <= 1.1.0 + +The dashboard used to live inside the framework gem as `ActiveAgent::Dashboard`. +The old constants still resolve and warn through the deprecator: + +| Old | New | +| --- | --- | +| `ActiveAgent::Dashboard` | `ActionAgent` | +| `ActiveAgent::TelemetryTrace` | `ActionAgent::TelemetryTrace` | +| `ActiveAgent::ProcessTelemetryTracesJob` | `ActionAgent::ProcessTelemetryTracesJob` | + +They are removed in the next major. Add `gem "actionagent"` to your Gemfile and +rename your references. + +## License + +MIT. See [LICENSE](LICENSE). diff --git a/actionagent/actionagent.gemspec b/actionagent/actionagent.gemspec new file mode 100644 index 00000000..d7db4215 --- /dev/null +++ b/actionagent/actionagent.gemspec @@ -0,0 +1,62 @@ +require_relative "lib/action_agent/version" + +Gem::Specification.new do |spec| + spec.name = "actionagent" + spec.version = ActionAgent::VERSION + spec.summary = "The Active Agent dashboard, as a mountable Rails engine" + spec.description = "Mount a dashboard for your agents in any Rails app: build and run agents, " \ + "read their conversations, score them with evaluations, and watch traces, metrics and costs — " \ + "served from your own database, on your own domain." + spec.authors = [ "Justin Bowen" ] + spec.email = "jusbowen@gmail.com" + + # The React sources under frontend/ build into app/assets/builds, which is + # what ships. Host apps never run a JavaScript build, so the sources (and + # their node_modules) stay out of the gem. + # Globbed relative to this file, not the working directory: `gem build + # actionagent/actionagent.gemspec` from the repository root would otherwise + # sweep up the framework's lib/ and ship it as this gem, with no app/ at all. + spec.files = Dir.chdir(__dir__) do + Dir[ + "app/**/*", + "config/**/*", + "lib/**/*", + "README.md", + "LICENSE" + ] + end + spec.require_paths = "lib" + spec.homepage = "https://activeagents.ai" + spec.license = "MIT" + spec.required_ruby_version = ">= 3.1.0" + + spec.metadata = { + "bug_tracker_uri" => "https://github.com/activeagents/activeagent/issues", + "documentation_uri" => "https://docs.activeagents.ai/framework/self-hosted-observability", + "source_code_uri" => "https://github.com/activeagents/activeagent", + "rubygems_mfa_required" => "true" + } + + # The dashboard executes agents through the framework. The floor is 2.0 + # deliberately: activeagent 1.x still contains the in-gem dashboard this + # engine replaces, and lacks ActiveAgent::Telemetry::ToolOrigin, which the + # Tools view calls. Resolving against 1.x would load two dashboards and + # leave ActiveAgent::Dashboard defined, so the compatibility shim would + # never fire. + spec.add_dependency "activeagent", ">= 2.0", "< 3" + + # It is a Rails engine, so it needs railties — as does the framework, which + # declares it too. Active Record is the one that matters here: `activeagent` + # deliberately does without it, and keeping it on this side is what lets the + # framework stay usable in an app that has no database. + spec.add_dependency "railties", ">= 7.2", "<= 9.0" + spec.add_dependency "activerecord", ">= 7.2", "<= 9.0" + + # Conversation persistence. AgentExecutionService mixes SolidAgent::HasContext + # into the class it builds for a run, so this is a hard requirement — and one + # `activeagent` could never declare, since solid_agent depends on it. + spec.add_dependency "solid_agent", ">= 0.1" + + spec.add_development_dependency "minitest", "~> 5.0" + spec.add_development_dependency "sqlite3" +end diff --git a/actionagent/app/assets/builds/action_agent.css b/actionagent/app/assets/builds/action_agent.css new file mode 100644 index 00000000..5363af7a --- /dev/null +++ b/actionagent/app/assets/builds/action_agent.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/actionagent/app/assets/builds/action_agent.js b/actionagent/app/assets/builds/action_agent.js new file mode 100644 index 00000000..d2cad541 --- /dev/null +++ b/actionagent/app/assets/builds/action_agent.js @@ -0,0 +1,163 @@ +var Sj=Object.create;var Ig=Object.defineProperty;var Aj=Object.getOwnPropertyDescriptor;var Nj=Object.getOwnPropertyNames;var _j=Object.getPrototypeOf,Cj=Object.prototype.hasOwnProperty;var Or=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Ej=(e,t)=>{for(var r in t)Ig(e,r,{get:t[r],enumerable:!0})},kj=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Nj(t))!Cj.call(e,a)&&a!==r&&Ig(e,a,{get:()=>t[a],enumerable:!(n=Aj(t,a))||n.enumerable});return e};var ee=(e,t,r)=>(r=e!=null?Sj(_j(e)):{},kj(t||!e||!e.__esModule?Ig(r,"default",{value:e,enumerable:!0}):r,e));var AA=Or(Be=>{"use strict";var Bg=Symbol.for("react.transitional.element"),Oj=Symbol.for("react.portal"),Tj=Symbol.for("react.fragment"),Mj=Symbol.for("react.strict_mode"),Dj=Symbol.for("react.profiler"),Pj=Symbol.for("react.consumer"),jj=Symbol.for("react.context"),Ij=Symbol.for("react.forward_ref"),zj=Symbol.for("react.suspense"),Lj=Symbol.for("react.memo"),gA=Symbol.for("react.lazy"),Bj=Symbol.for("react.activity"),pA=Symbol.iterator;function Uj(e){return e===null||typeof e!="object"?null:(e=pA&&e[pA]||e["@@iterator"],typeof e=="function"?e:null)}var yA={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},xA=Object.assign,bA={};function Ql(e,t,r){this.props=e,this.context=t,this.refs=bA,this.updater=r||yA}Ql.prototype.isReactComponent={};Ql.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")};Ql.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function wA(){}wA.prototype=Ql.prototype;function Ug(e,t,r){this.props=e,this.context=t,this.refs=bA,this.updater=r||yA}var $g=Ug.prototype=new wA;$g.constructor=Ug;xA($g,Ql.prototype);$g.isPureReactComponent=!0;var mA=Array.isArray;function Lg(){}var wt={H:null,A:null,T:null,S:null},SA=Object.prototype.hasOwnProperty;function Hg(e,t,r){var n=r.ref;return{$$typeof:Bg,type:e,key:t,ref:n!==void 0?n:null,props:r}}function $j(e,t){return Hg(e.type,t,e.props)}function Vg(e){return typeof e=="object"&&e!==null&&e.$$typeof===Bg}function Hj(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(r){return t[r]})}var vA=/\/+/g;function zg(e,t){return typeof e=="object"&&e!==null&&e.key!=null?Hj(""+e.key):t.toString(36)}function Vj(e){switch(e.status){case"fulfilled":return e.value;case"rejected":throw e.reason;default:switch(typeof e.status=="string"?e.then(Lg,Lg):(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 Zl(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 Bg:case Oj:o=!0;break;case gA:return o=e._init,Zl(o(e._payload),t,r,n,a)}}if(o)return a=a(e),o=n===""?"."+zg(e,0):n,mA(a)?(r="",o!=null&&(r=o.replace(vA,"$&/")+"/"),Zl(a,t,r,"",function(u){return u})):a!=null&&(Vg(a)&&(a=$j(a,r+(a.key==null||e&&e.key===a.key?"":(""+a.key).replace(vA,"$&/")+"/")+o)),t.push(a)),1;o=0;var l=n===""?".":n+":";if(mA(e))for(var s=0;s{"use strict";NA.exports=AA()});var jA=Or(Ot=>{"use strict";function Wg(e,t){var r=e.length;e.push(t);e:for(;0>>1,a=e[n];if(0>>1;ntp(l,r))stp(u,l)?(e[n]=u,e[s]=r,n=s):(e[n]=l,e[o]=r,n=o);else if(stp(u,r))e[n]=u,e[s]=r,n=s;else break e}}return t}function tp(e,t){var r=e.sortIndex-t.sortIndex;return r!==0?r:e.id-t.id}Ot.unstable_now=void 0;typeof performance=="object"&&typeof performance.now=="function"?(_A=performance,Ot.unstable_now=function(){return _A.now()}):(Fg=Date,CA=Fg.now(),Ot.unstable_now=function(){return Fg.now()-CA});var _A,Fg,CA,Va=[],Ii=[],Yj=1,Dn=null,Tr=3,Kg=!1,fc=!1,pc=!1,Gg=!1,OA=typeof setTimeout=="function"?setTimeout:null,TA=typeof clearTimeout=="function"?clearTimeout:null,EA=typeof setImmediate<"u"?setImmediate:null;function rp(e){for(var t=ya(Ii);t!==null;){if(t.callback===null)np(Ii);else if(t.startTime<=e)np(Ii),t.sortIndex=t.expirationTime,Wg(Va,t);else break;t=ya(Ii)}}function Xg(e){if(pc=!1,rp(e),!fc)if(ya(Va)!==null)fc=!0,Rl||(Rl=!0,Jl());else{var t=ya(Ii);t!==null&&Zg(Xg,t.startTime-e)}}var Rl=!1,mc=-1,MA=5,DA=-1;function PA(){return Gg?!0:!(Ot.unstable_now()-DAe&&PA());){var n=Dn.callback;if(typeof n=="function"){Dn.callback=null,Tr=Dn.priorityLevel;var a=n(Dn.expirationTime<=e);if(e=Ot.unstable_now(),typeof a=="function"){Dn.callback=a,rp(e),t=!0;break t}Dn===ya(Va)&&np(Va),rp(e)}else np(Va);Dn=ya(Va)}if(Dn!==null)t=!0;else{var i=ya(Ii);i!==null&&Zg(Xg,i.startTime-e),t=!1}}break e}finally{Dn=null,Tr=r,Kg=!1}t=void 0}}finally{t?Jl():Rl=!1}}}var Jl;typeof EA=="function"?Jl=function(){EA(qg)}:typeof MessageChannel<"u"?(Yg=new MessageChannel,kA=Yg.port2,Yg.port1.onmessage=qg,Jl=function(){kA.postMessage(null)}):Jl=function(){OA(qg,0)};var Yg,kA;function Zg(e,t){mc=OA(function(){e(Ot.unstable_now())},t)}Ot.unstable_IdlePriority=5;Ot.unstable_ImmediatePriority=1;Ot.unstable_LowPriority=4;Ot.unstable_NormalPriority=3;Ot.unstable_Profiling=null;Ot.unstable_UserBlockingPriority=2;Ot.unstable_cancelCallback=function(e){e.callback=null};Ot.unstable_forceFrameRate=function(e){0>e||125n?(e.sortIndex=r,Wg(Ii,e),ya(Va)===null&&e===ya(Ii)&&(pc?(TA(mc),mc=-1):pc=!0,Zg(Xg,r-n))):(e.sortIndex=a,Wg(Va,e),fc||Kg||(fc=!0,Rl||(Rl=!0,Jl()))),e};Ot.unstable_shouldYield=PA;Ot.unstable_wrapCallback=function(e){var t=Tr;return function(){var r=Tr;Tr=t;try{return e.apply(this,arguments)}finally{Tr=r}}}});var zA=Or((hQ,IA)=>{"use strict";IA.exports=jA()});var BA=Or(Vr=>{"use strict";var Wj=ne();function LA(e){var t="https://react.dev/errors/"+e;if(1{"use strict";function UA(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(UA)}catch(e){console.error(e)}}UA(),$A.exports=BA()});var JC=Or(Tm=>{"use strict";var or=zA(),fN=ne(),Xj=ip();function oe(e){var t="https://react.dev/errors/"+e;if(1os||(e.current=Iy[os],Iy[os]=null,os--)}function xt(e,t){os++,Iy[os]=e.current,e.current=t}var Sa=Aa(null),Hc=Aa(null),Gi=Aa(null),Up=Aa(null);function $p(e,t){switch(xt(Gi,t),xt(Hc,e),xt(Sa,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?X5(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=X5(t),e=BC(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}hr(Sa),xt(Sa,e)}function Ns(){hr(Sa),hr(Hc),hr(Gi)}function zy(e){e.memoizedState!==null&&xt(Up,e);var t=Sa.current,r=BC(t,e.type);t!==r&&(xt(Hc,e),xt(Sa,r))}function Hp(e){Hc.current===e&&(hr(Sa),hr(Hc)),Up.current===e&&(hr(Up),Jc._currentValue=Bo)}var Qg,FA;function jo(e){if(Qg===void 0)try{throw Error()}catch(r){var t=r.stack.trim().match(/\n( *(at )?)/);Qg=t&&t[1]||"",FA=-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{Jg=!1,Error.prepareStackTrace=r}return(r=e?e.displayName||e.name:"")?jo(r):""}function eI(e,t){switch(e.tag){case 26:case 27:case 5:return jo(e.type);case 16:return jo("Lazy");case 13:return e.child!==t&&t!==null?jo("Suspense Fallback"):jo("Suspense");case 19:return jo("SuspenseList");case 0:case 15:return Rg(e.type,!1);case 11:return Rg(e.type.render,!1);case 1:return Rg(e.type,!0);case 31:return jo("Activity");default:return""}}function qA(e){try{var t="",r=null;do t+=eI(e,r),r=e,e=e.return;while(e);return t}catch(n){return` +Error generating stack: `+n.message+` +`+n.stack}}var Ly=Object.prototype.hasOwnProperty,Ex=or.unstable_scheduleCallback,ey=or.unstable_cancelCallback,tI=or.unstable_shouldYield,rI=or.unstable_requestPaint,vn=or.unstable_now,nI=or.unstable_getCurrentPriorityLevel,xN=or.unstable_ImmediatePriority,bN=or.unstable_UserBlockingPriority,Vp=or.unstable_NormalPriority,aI=or.unstable_LowPriority,wN=or.unstable_IdlePriority,iI=or.log,oI=or.unstable_setDisableYieldValue,rd=null,hn=null;function Fi(e){if(typeof iI=="function"&&oI(e),hn&&typeof hn.setStrictMode=="function")try{hn.setStrictMode(rd,e)}catch{}}var gn=Math.clz32?Math.clz32:uI,lI=Math.log,sI=Math.LN2;function uI(e){return e>>>=0,e===0?32:31-(lI(e)/sI|0)|0}var lp=256,sp=262144,up=4194304;function Io(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 mm(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=Io(n):(o&=l,o!==0?a=Io(o):r||(r=l&~e,r!==0&&(a=Io(r))))):(l=n&~i,l!==0?a=Io(l):o!==0?a=Io(o):r||(r=n&~e,r!==0&&(a=Io(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 nd(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function cI(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 SN(){var e=up;return up<<=1,(up&62914560)===0&&(up=4194304),e}function ty(e){for(var t=[],r=0;31>r;r++)t.push(e);return t}function ad(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function dI(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 gI=/[\n"\\]/g;function Ln(e){return e.replace(gI,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function $y(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=""+jn(t)):e.value!==""+jn(t)&&(e.value=""+jn(t)):o!=="submit"&&o!=="reset"||e.removeAttribute("value"),t!=null?Hy(e,o,jn(t)):r!=null?Hy(e,o,jn(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=""+jn(l):e.removeAttribute("name")}function MN(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)){Uy(e);return}r=r!=null?""+jn(r):"",t=t!=null?""+jn(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),Uy(e)}function Hy(e,t,r){t==="number"&&Fp(e.ownerDocument)===e||e.defaultValue===""+r||(e.defaultValue=""+r)}function ys(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"),Fy=!1;if(ri)try{es={},Object.defineProperty(es,"passive",{get:function(){Fy=!0}}),window.addEventListener("test",es,es),window.removeEventListener("test",es,es)}catch{Fy=!1}var es,qi=null,Px=null,Cp=null;function zN(){if(Cp)return Cp;var e,t=Px,r=t.length,n,a="value"in qi?qi.value:qi.textContent,i=a.length;for(e=0;e=kc),t5=" ",r5=!1;function BN(e,t){switch(e){case"keyup":return YI.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function UN(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var us=!1;function KI(e,t){switch(e){case"compositionend":return UN(t);case"keypress":return t.which!==32?null:(r5=!0,t5);case"textInput":return e=t.data,e===t5&&r5?null:e;default:return null}}function GI(e,t){if(us)return e==="compositionend"||!Ix&&BN(e,t)?(e=zN(),Cp=Px=qi=null,us=!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=o5(r)}}function FN(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?FN(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function qN(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Fp(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=Fp(e.document)}return t}function zx(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 r7=ri&&"documentMode"in document&&11>=document.documentMode,cs=null,qy=null,Tc=null,Yy=!1;function s5(e,t,r){var n=r.window===r?r.document:r.nodeType===9?r:r.ownerDocument;Yy||cs==null||cs!==Fp(n)||(n=cs,"selectionStart"in n&&zx(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}),Tc&&qc(Tc,n)||(Tc=n,n=lm(qy,"onSelect"),0>=o,a-=o,xa=1<<32-gn(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&&Ga(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&&Ga(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&&Ga(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&&Ga(m,E),k}function b(m,g,x,A){if(typeof x=="object"&&x!==null&&x.type===is&&x.key===null&&(x=x.props.children),typeof x=="object"&&x!==null){switch(x.$$typeof){case op:e:{for(var k=x.key;g!==null;){if(g.key===k){if(k=x.type,k===is){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===Li&&zo(k)===g.type){r(m,g.sibling),A=a(g,x.props),yc(A,x),A.return=m,m=A;break e}r(m,g);break}else t(m,g);g=g.sibling}x.type===is?(A=Uo(x.props.children,m.mode,A,x.key),A.return=m,m=A):(A=kp(x.type,x.key,x.props,null,m.mode,A),yc(A,x),A.return=m,m=A)}return o(m);case Ac: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=uy(x,m.mode,A),A.return=m,m=A}return o(m);case Li:return x=zo(x),b(m,g,x,A)}if(Nc(x))return v(m,g,x,A);if(hc(x)){if(k=hc(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,vp(x),A);if(x.$$typeof===Za)return b(m,g,mp(m,x),A);hp(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=sy(x,m.mode,A),A.return=m,m=A),o(m)):r(m,g)}return function(m,g,x,A){try{Kc=0;var k=b(m,g,x,A);return ws=null,k}catch(M){if(M===Bs||M===bm)throw M;var _=pn(29,M,null,m.mode);return _.lanes=A,_.return=m,_}}}var Yo=i_(!0),o_=i_(!1),Bi=!1;function Yx(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Jy(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 Zi(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Qi(e,t,r){var n=e.updateQueue;if(n===null)return null;if(n=n.shared,(it&2)!==0){var a=n.pending;return a===null?t.next=t:(t.next=a.next,a.next=t),n.pending=t,t=Yp(e),QN(e,null,r),t}return xm(e,n,t,r),Yp(e)}function Dc(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,NN(e,r)}}function dy(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 Ry=!1;function Pc(){if(Ry){var e=bs;if(e!==null)throw e}}function jc(e,t,r,n){Ry=!1;var a=e.updateQueue;Bi=!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===Es&&(Ry=!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=Nt({},d,f);break e;case 2:Bi=!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),oo|=o,e.lanes=o,e.memoizedState=d}}function l_(e,t){if(typeof e!="function")throw Error(oe(191,e));e.call(t)}function s_(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,ab(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=d7(s,n);Ic(e,t,c,yn(e))}else Ic(e,t,n,yn(e))}catch(d){Ic(e,t,{then:function(){},status:"rejected",reason:d},yn())}finally{ot.p=i,o!==null&&l.types!==null&&(o.types=l.types),De.T=o}}function g7(){}function ax(e,t,r,n){if(e.tag!==5)throw Error(oe(476));var a=P_(e).queue;D_(e,a,t,Bo,r===null?g7:function(){return j_(e),r(n)})}function P_(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:Bo,baseState:Bo,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ai,lastRenderedState:Bo},next:null};var r={};return t.next={memoizedState:r,baseState:r,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ai,lastRenderedState:r},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function j_(e){var t=P_(e);t.next===null&&(t=e.alternate.memoizedState),Ic(e,t.next.queue,{},yn())}function nb(){return Nr(Jc)}function I_(){return Wt().memoizedState}function z_(){return Wt().memoizedState}function y7(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var r=yn();e=Zi(r);var n=Qi(t,e,r);n!==null&&(Rr(n,t,r),Dc(n,t,r)),t={cache:Vx()},e.payload=t;return}t=t.return}}function x7(e,t,r){var n=yn();r={lane:n,revertLane:0,gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},Nm(e)?B_(t,r):(r=Bx(e,t,r,n),r!==null&&(Rr(r,e,n),U_(r,t,n)))}function L_(e,t,r){var n=yn();Ic(e,t,r,n)}function Ic(e,t,r,n){var a={lane:n,revertLane:0,gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null};if(Nm(e))B_(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,xn(l,o))return xm(e,t,a,0),ht===null&&ym(),!1}catch{}if(r=Bx(e,t,a,n),r!==null)return Rr(r,e,n),U_(r,t,n),!0}return!1}function ab(e,t,r,n){if(n={lane:2,revertLane:pb(),gesture:null,action:n,hasEagerState:!1,eagerState:null,next:null},Nm(e)){if(t)throw Error(oe(479))}else t=Bx(e,r,n,2),t!==null&&Rr(t,e,2)}function Nm(e){var t=e.alternate;return e===Ue||t!==null&&t===Ue}function B_(e,t){Ss=Qp=!0;var r=e.pending;r===null?t.next=t:(t.next=r.next,r.next=t),e.pending=t}function U_(e,t,r){if((r&4194048)!==0){var n=t.lanes;n&=e.pendingLanes,r|=n,t.lanes=r,NN(e,r)}}var Xc={readContext:Nr,use:Sm,useCallback:Ut,useContext:Ut,useEffect:Ut,useImperativeHandle:Ut,useLayoutEffect:Ut,useInsertionEffect:Ut,useMemo:Ut,useReducer:Ut,useRef:Ut,useState:Ut,useDebugValue:Ut,useDeferredValue:Ut,useTransition:Ut,useSyncExternalStore:Ut,useId:Ut,useHostTransitionStatus:Ut,useFormState:Ut,useActionState:Ut,useOptimistic:Ut,useMemoCache:Ut,useCacheRefresh:Ut};Xc.useEffectEvent=Ut;var $_={readContext:Nr,use:Sm,useCallback:function(e,t){return Fr().memoizedState=[e,t===void 0?null:t],e},useContext:Nr,useEffect:A5,useImperativeHandle:function(e,t,r){r=r!=null?r.concat([e]):null,Mp(4194308,4,E_.bind(null,t,e),r)},useLayoutEffect:function(e,t){return Mp(4194308,4,e,t)},useInsertionEffect:function(e,t){Mp(4,2,e,t)},useMemo:function(e,t){var r=Fr();t=t===void 0?null:t;var n=e();if(Wo){Fi(!0);try{e()}finally{Fi(!1)}}return r.memoizedState=[n,t],n},useReducer:function(e,t,r){var n=Fr();if(r!==void 0){var a=r(t);if(Wo){Fi(!0);try{r(t)}finally{Fi(!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=x7.bind(null,Ue,e),[n.memoizedState,e]},useRef:function(e){var t=Fr();return e={current:e},t.memoizedState=e},useState:function(e){e=rx(e);var t=e.queue,r=L_.bind(null,Ue,t);return t.dispatch=r,[e.memoizedState,r]},useDebugValue:tb,useDeferredValue:function(e,t){var r=Fr();return rb(r,e,t)},useTransition:function(){var e=rx(!1);return e=D_.bind(null,Ue,e.queue,!0,!1),Fr().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,r){var n=Ue,a=Fr();if(Xe){if(r===void 0)throw Error(oe(407));r=r()}else{if(r=t(),ht===null)throw Error(oe(349));(Ye&127)!==0||p_(n,t,r)}a.memoizedState=r;var i={value:r,getSnapshot:t};return a.queue=i,A5(v_.bind(null,n,i,e),[e]),n.flags|=2048,Os(9,{destroy:void 0},m_.bind(null,n,i,r,t),null),r},useId:function(){var e=Fr(),t=ht.identifierPrefix;if(Xe){var r=ba,n=xa;r=(n&~(1<<32-gn(n)-1)).toString(32)+r,t="_"+t+"R_"+r,r=Jp++,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[Sr]=t,i[en]=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(_r(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&&qa(t)}}return St(t),xy(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,r),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==n&&qa(t);else{if(typeof n!="string"&&t.stateNode===null)throw Error(oe(166));if(e=Gi.current,ts(t)){if(e=t.stateNode,r=t.memoizedProps,n=null,a=Ar,a!==null)switch(a.tag){case 27:case 5:n=a.memoizedProps}e[Sr]=t,e=!!(e.nodeValue===r||n!==null&&n.suppressHydrationWarning===!0||LC(e.nodeValue,r)),e||ao(t,!0)}else e=sm(e).createTextNode(n),e[Sr]=t,t.stateNode=e}return St(t),null;case 31:if(r=t.memoizedState,e===null||e.memoizedState!==null){if(n=ts(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[Sr]=t}else Fo(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;St(t),e=!1}else r=cy(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=r),e=!0;if(!e)return t.flags&256?(fn(t),t):(fn(t),null);if((t.flags&128)!==0)throw Error(oe(558))}return St(t),null;case 13:if(n=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(a=ts(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[Sr]=t}else Fo(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;St(t),a=!1}else a=cy(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return t.flags&256?(fn(t),t):(fn(t),null)}return fn(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),gp(t,t.updateQueue),St(t),null);case 4:return Ns(),e===null&&mb(t.stateNode.containerInfo),St(t),null;case 10:return ei(t.type),St(t),null;case 19:if(hr(Yt),n=t.memoizedState,n===null)return St(t),null;if(a=(t.flags&128)!==0,i=n.rendering,i===null)if(a)xc(n,!1);else{if($t!==0||e!==null&&(e.flags&128)!==0)for(e=t.child;e!==null;){if(i=Zp(e),i!==null){for(t.flags|=128,xc(n,!1),e=i.updateQueue,t.updateQueue=e,gp(t,e),t.subtreeFlags=0,e=r,r=t.child;r!==null;)JN(r,e),r=r.sibling;return xt(Yt,Yt.current&1|2),Xe&&Ga(t,n.treeForkCount),t.child}e=e.sibling}n.tail!==null&&vn()>rm&&(t.flags|=128,a=!0,xc(n,!1),t.lanes=4194304)}else{if(!a)if(e=Zp(i),e!==null){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,gp(t,e),xc(n,!0),n.tail===null&&n.tailMode==="hidden"&&!i.alternate&&!Xe)return St(t),null}else 2*vn()-n.renderingStartTime>rm&&r!==536870912&&(t.flags|=128,a=!0,xc(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=vn(),e.sibling=null,r=Yt.current,xt(Yt,a?r&1|2:r&1),Xe&&Ga(t,n.treeForkCount),e):(St(t),null);case 22:case 23:return fn(t),Wx(),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&&(St(t),t.subtreeFlags&6&&(t.flags|=8192)):St(t),r=t.updateQueue,r!==null&&gp(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&&hr($o),null;case 24:return r=null,e!==null&&(r=e.memoizedState.cache),t.memoizedState.cache!==r&&(t.flags|=2048),ei(er),St(t),null;case 25:return null;case 30:return null}throw Error(oe(156,t.tag))}function N7(e,t){switch(Hx(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return ei(er),Ns(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return Hp(t),null;case 31:if(t.memoizedState!==null){if(fn(t),t.alternate===null)throw Error(oe(340));Fo()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(fn(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(oe(340));Fo()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return hr(Yt),null;case 4:return Ns(),null;case 10:return ei(t.type),null;case 22:case 23:return fn(t),Wx(),e!==null&&hr($o),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return ei(er),null;case 25:return null;default:return null}}function J_(e,t){switch(Hx(t),t.tag){case 3:ei(er),Ns();break;case 26:case 27:case 5:Hp(t);break;case 4:Ns();break;case 31:t.memoizedState!==null&&fn(t);break;case 13:fn(t);break;case 19:hr(Yt);break;case 10:ei(t.type);break;case 22:case 23:fn(t),Wx(),e!==null&&hr($o);break;case 24:ei(er)}}function ud(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){ct(t,t.return,l)}}function io(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){ct(a,s,c)}}}n=n.next}while(n!==i)}}catch(c){ct(t,t.return,c)}}function R_(e){var t=e.updateQueue;if(t!==null){var r=e.stateNode;try{s_(t,r)}catch(n){ct(e,e.return,n)}}}function eC(e,t,r){r.props=Ko(e.type,e.memoizedProps),r.state=e.memoizedState;try{r.componentWillUnmount()}catch(n){ct(e,t,n)}}function zc(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){ct(e,t,a)}}function wa(e,t){var r=e.ref,n=e.refCleanup;if(r!==null)if(typeof n=="function")try{n()}catch(a){ct(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){ct(e,t,a)}else r.current=null}function tC(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){ct(e,e.return,a)}}function by(e,t,r){try{var n=e.stateNode;q7(n,e.type,r,t),n[en]=t}catch(a){ct(e,e.return,a)}}function rC(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&so(e.type)||e.tag===4}function wy(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||rC(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&&so(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 ux(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=Qa));else if(n!==4&&(n===27&&so(e.type)&&(r=e.stateNode,t=null),e=e.child,e!==null))for(ux(e,t,r),e=e.sibling;e!==null;)ux(e,t,r),e=e.sibling}function tm(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&&so(e.type)&&(r=e.stateNode),e=e.child,e!==null))for(tm(e,t,r),e=e.sibling;e!==null;)tm(e,t,r),e=e.sibling}function nC(e){var t=e.stateNode,r=e.memoizedProps;try{for(var n=e.type,a=t.attributes;a.length;)t.removeAttributeNode(a[0]);_r(t,n,r),t[Sr]=e,t[en]=r}catch(i){ct(e,e.return,i)}}var Xa=!1,Rt=!1,Sy=!1,z5=typeof WeakSet=="function"?WeakSet:Set,mr=null;function _7(e,t){if(e=e.containerInfo,hx=fm,e=qN(e),zx(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(gx={focusedElem:e,selectionRange:r},fm=!1,mr=t;mr!==null;)if(t=mr,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,mr=e;else for(;mr!==null;){switch(t=mr,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"))),_r(i,n,r),i[Sr]=e,vr(i),n=i;break e;case"link":var o=aN("link","href",a).get(n+(r.href||""));if(o){for(var l=0;lb&&(o=b,b=h,h=o);var m=l5(l,h),g=l5(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=fx,fx=null;var i=Ri,o=ti;if(ir=0,Ms=Ri=null,ti=0,(it&6)!==0)throw Error(oe(331));var l=it;if(it|=4,mC(i.current),dC(i,i.current,o,r),it=l,cd(0,!1),hn&&typeof hn.onPostCommitFiberRoot=="function")try{hn.onPostCommitFiberRoot(rd,i)}catch{}return!0}finally{ot.p=a,De.T=n,OC(e,t)}}function $5(e,t,r){t=Bn(r,t),t=ox(e.stateNode,t,2),e=Qi(e,t,2),e!==null&&(ad(e,2),Na(e))}function ct(e,t,r){if(e.tag===3)$5(e,e,r);else for(;t!==null;){if(t.tag===3){$5(t,e,r);break}else if(t.tag===1){var n=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof n.componentDidCatch=="function"&&(Ji===null||!Ji.has(n))){e=Bn(r,e),r=Y_(2),n=Qi(t,r,2),n!==null&&(W_(r,n,t,e),ad(n,2),Na(n));break}}t=t.return}}function Ny(e,t,r){var n=e.pingCache;if(n===null){n=e.pingCache=new k7;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)||(cb=!0,a.add(r),e=P7.bind(null,e,t,r),t.then(e,e))}function P7(e,t,r){var n=e.pingCache;n!==null&&n.delete(t),e.pingedLanes|=e.suspendedLanes&r,e.warmLanes&=~r,ht===e&&(Ye&r)===r&&($t===4||$t===3&&(Ye&62914560)===Ye&&300>vn()-_m?(it&2)===0&&Ds(e,0):db|=r,Ts===Ye&&(Ts=0)),Na(e)}function MC(e,t){t===0&&(t=SN()),e=Qo(e,t),e!==null&&(ad(e,t),Na(e))}function j7(e){var t=e.memoizedState,r=0;t!==null&&(r=t.retryLane),MC(e,r)}function I7(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),MC(e,r)}function z7(e,t){return Ex(e,t)}var im=null,as=null,mx=!1,om=!1,_y=!1,Ki=0;function Na(e){e!==as&&e.next===null&&(as===null?im=as=e:as=as.next=e),om=!0,mx||(mx=!0,B7())}function cd(e,t){if(!_y&&om){_y=!0;do for(var r=!1,n=im;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-gn(42|e)+1)-1,i&=a&~(o&~l),i=i&201326741?i&201326741|1:i?i|2:0}i!==0&&(r=!0,H5(n,i))}else i=Ye,i=mm(n,n===ht?i:0,n.cancelPendingCommit!==null||n.timeoutHandle!==-1),(i&3)===0||nd(n,i)||(r=!0,H5(n,i));n=n.next}while(r);_y=!1}}function L7(){DC()}function DC(){om=mx=!1;var e=0;Ki!==0&&W7()&&(e=Ki);for(var t=vn(),r=null,n=im;n!==null;){var a=n.next,i=PC(n,t);i===0?(n.next=null,r===null?im=a:r.next=a,a===null&&(as=r)):(r=n,(e!==0||(i&3)!==0)&&(om=!0)),n=a}ir!==0&&ir!==5||cd(e,!1),Ki!==0&&(Ki=0)}function PC(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&&G5(d)&&(s=s.responseEnd,o+=c*(s"u"?null:document;function VC(e,t,r){var n=$s;if(n&&typeof t=="string"&&t){var a=Ln(t);a='link[rel="'+e+'"][href="'+a+'"]',typeof r=="string"&&(a+='[crossorigin="'+r+'"]'),tN.has(a)||(tN.add(a),e={rel:e,crossOrigin:r,href:t},n.querySelector(a)===null&&(t=n.createElement("link"),_r(t,"link",e),vr(t),n.head.appendChild(t)))}}function tz(e){li.D(e),VC("dns-prefetch",e,null)}function rz(e,t){li.C(e,t),VC("preconnect",e,t)}function nz(e,t,r){li.L(e,t,r);var n=$s;if(n&&e&&t){var a='link[rel="preload"][as="'+Ln(t)+'"]';t==="image"&&r&&r.imageSrcSet?(a+='[imagesrcset="'+Ln(r.imageSrcSet)+'"]',typeof r.imageSizes=="string"&&(a+='[imagesizes="'+Ln(r.imageSizes)+'"]')):a+='[href="'+Ln(e)+'"]';var i=a;switch(t){case"style":i=Ps(e);break;case"script":i=Hs(e)}Vn.has(i)||(e=Nt({rel:"preload",href:t==="image"&&r&&r.imageSrcSet?void 0:e,as:t},r),Vn.set(i,e),n.querySelector(a)!==null||t==="style"&&n.querySelector(dd(i))||t==="script"&&n.querySelector(fd(i))||(t=n.createElement("link"),_r(t,"link",e),vr(t),n.head.appendChild(t)))}}function az(e,t){li.m(e,t);var r=$s;if(r&&e){var n=t&&typeof t.as=="string"?t.as:"script",a='link[rel="modulepreload"][as="'+Ln(n)+'"][href="'+Ln(e)+'"]',i=a;switch(n){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":i=Hs(e)}if(!Vn.has(i)&&(e=Nt({rel:"modulepreload",href:e},t),Vn.set(i,e),r.querySelector(a)===null)){switch(n){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(r.querySelector(fd(i)))return}n=r.createElement("link"),_r(n,"link",e),vr(n),r.head.appendChild(n)}}}function iz(e,t,r){li.S(e,t,r);var n=$s;if(n&&e){var a=gs(n).hoistableStyles,i=Ps(e);t=t||"default";var o=a.get(i);if(!o){var l={loading:0,preload:null};if(o=n.querySelector(dd(i)))l.loading=5;else{e=Nt({rel:"stylesheet",href:e,"data-precedence":t},r),(r=Vn.get(i))&&vb(e,r);var s=o=n.createElement("link");vr(s),_r(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,Ip(o,t,n)}o={type:"stylesheet",instance:o,count:1,state:l},a.set(i,o)}}}function oz(e,t){li.X(e,t);var r=$s;if(r&&e){var n=gs(r).hoistableScripts,a=Hs(e),i=n.get(a);i||(i=r.querySelector(fd(a)),i||(e=Nt({src:e,async:!0},t),(t=Vn.get(a))&&hb(e,t),i=r.createElement("script"),vr(i),_r(i,"link",e),r.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},n.set(a,i))}}function lz(e,t){li.M(e,t);var r=$s;if(r&&e){var n=gs(r).hoistableScripts,a=Hs(e),i=n.get(a);i||(i=r.querySelector(fd(a)),i||(e=Nt({src:e,async:!0,type:"module"},t),(t=Vn.get(a))&&hb(e,t),i=r.createElement("script"),vr(i),_r(i,"link",e),r.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},n.set(a,i))}}function rN(e,t,r,n){var a=(a=Gi.current)?um(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=Ps(r.href),r=gs(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=Ps(r.href);var i=gs(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(dd(e)))&&!i._p&&(o.instance=i,o.state.loading=5),Vn.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},Vn.set(e,r),i||sz(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=Hs(r),r=gs(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 Ps(e){return'href="'+Ln(e)+'"'}function dd(e){return'link[rel="stylesheet"]['+e+"]"}function FC(e){return Nt({},e,{"data-precedence":e.precedence,precedence:null})}function sz(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}),_r(t,"link",r),vr(t),e.head.appendChild(t))}function Hs(e){return'[src="'+Ln(e)+'"]'}function fd(e){return"script[async]"+e}function nN(e,t,r){if(t.count++,t.instance===null)switch(t.type){case"style":var n=e.querySelector('style[data-href~="'+Ln(r.href)+'"]');if(n)return t.instance=n,vr(n),n;var a=Nt({},r,{"data-href":r.href,"data-precedence":r.precedence,href:null,precedence:null});return n=(e.ownerDocument||e).createElement("style"),vr(n),_r(n,"style",a),Ip(n,r.precedence,e),t.instance=n;case"stylesheet":a=Ps(r.href);var i=e.querySelector(dd(a));if(i)return t.state.loading|=4,t.instance=i,vr(i),i;n=FC(r),(a=Vn.get(a))&&vb(n,a),i=(e.ownerDocument||e).createElement("link"),vr(i);var o=i;return o._p=new Promise(function(l,s){o.onload=l,o.onerror=s}),_r(i,"link",n),t.state.loading|=4,Ip(i,r.precedence,e),t.instance=i;case"script":return i=Hs(r.src),(a=e.querySelector(fd(i)))?(t.instance=a,vr(a),a):(n=r,(a=Vn.get(i))&&(n=Nt({},r),hb(n,a)),e=e.ownerDocument||e,a=e.createElement("script"),vr(a),_r(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,Ip(n,r.precedence,e));return t.instance}function Ip(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 uz(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 qC(e){return!(e.type==="stylesheet"&&(e.state.loading&3)===0)}function cz(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=Ps(n.href),i=t.querySelector(dd(a));if(i){t=i._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(e.count++,e=cm.bind(e),t.then(e,e)),r.state.loading|=4,r.instance=i,vr(i);return}i=t.ownerDocument||t,n=FC(n),(a=Vn.get(a))&&vb(n,a),i=i.createElement("link"),vr(i);var o=i;o._p=new Promise(function(l,s){o.onload=l,o.onerror=s}),_r(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=cm.bind(e),t.addEventListener("load",r),t.addEventListener("error",r))}}var Oy=0;function dz(e,t){return e.stylesheets&&e.count===0&&Lp(e,e.stylesheets),0Oy?50:800)+t);return e.unsuspend=r,function(){e.unsuspend=null,clearTimeout(n),clearTimeout(a)}}:null}function cm(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Lp(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var dm=null;function Lp(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,dm=new Map,t.forEach(fz,e),dm=null,cm.call(e))}function fz(e,t){if(!(t.state.loading&4)){var r=dm.get(e);if(r)var n=r.get(null);else{r=new Map,dm.set(e,r);for(var a=e.querySelectorAll("link[data-precedence],style[data-precedence]"),i=0;i{"use strict";function RC(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(RC)}catch(e){console.error(e)}}RC(),eE.exports=JC()});var RE=Or(JE=>{"use strict";var iu=ne();function r9(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var n9=typeof Object.is=="function"?Object.is:r9,a9=iu.useState,i9=iu.useEffect,o9=iu.useLayoutEffect,l9=iu.useDebugValue;function s9(e,t){var r=t(),n=a9({inst:{value:r,getSnapshot:t}}),a=n[0].inst,i=n[1];return o9(function(){a.value=r,a.getSnapshot=t,e1(a)&&i({inst:a})},[e,r,t]),i9(function(){return e1(a)&&i({inst:a}),e(function(){e1(a)&&i({inst:a})})},[e]),l9(r),r}function e1(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!n9(e,r)}catch{return!0}}function u9(e,t){return t()}var c9=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?u9:s9;JE.useSyncExternalStore=iu.useSyncExternalStore!==void 0?iu.useSyncExternalStore:c9});var tk=Or((ate,ek)=>{"use strict";ek.exports=RE()});var nk=Or(rk=>{"use strict";var vv=ne(),d9=tk();function f9(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var p9=typeof Object.is=="function"?Object.is:f9,m9=d9.useSyncExternalStore,v9=vv.useRef,h9=vv.useEffect,g9=vv.useMemo,y9=vv.useDebugValue;rk.useSyncExternalStoreWithSelector=function(e,t,r,n,a){var i=v9(null);if(i.current===null){var o={hasValue:!1,value:null};i.current=o}else o=i.current;i=g9(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,p9(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=m9(e,i[0],i[1]);return h9(function(){o.hasValue=!0,o.value=l},[l]),y9(l),l}});var ik=Or((ote,ak)=>{"use strict";ak.exports=nk()});var KO=Or(WO=>{"use strict";var Md=ne();function SB(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var AB=typeof Object.is=="function"?Object.is:SB,NB=Md.useSyncExternalStore,_B=Md.useRef,CB=Md.useEffect,EB=Md.useMemo,kB=Md.useDebugValue;WO.useSyncExternalStoreWithSelector=function(e,t,r,n,a){var i=_B(null);if(i.current===null){var o={hasValue:!1,value:null};i.current=o}else o=i.current;i=EB(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,AB(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=NB(e,i[0],i[1]);return CB(function(){o.hasValue=!0,o.value=l},[l]),kB(l),l}});var XO=Or((ine,GO)=>{"use strict";GO.exports=KO()});var K1=Or((y3,gh)=>{(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,H,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=H=O+3;;)if(T=D,D=T.plus(x(Y,T,H+2)).times(.5),g(T.d).slice(0,H)===(C=g(D.d)).slice(0,H)){if(C=C.slice(H-3,H+1),N==H&&C=="4999"){if(I(T,O+1,0),T.times(T).eq(Y)){D=T;break}}else if(C!="9999")break;H+=4}return n=!0,I(D,O)},h.times=h.mul=function(y){var C,O,D,N,T,H,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[--H];)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,H,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,H=C>=O,T=Y.s,H){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,H,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,H=y.e,N=C.e,J=J.slice(),T=H-N,T){for(T<0?(D=J,T=-T,Y=fe.length):(D=fe,N=H,Y=J.length),H=Math.ceil(ge/f),Y=H>Y?H+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="",H=y[0];if(N>0){for(T+=H,C=1;CH?1:-1;else for(Y=J=0;YN[Y]?1:-1;break}return J}function O(D,N,T){for(var H=0;T--;)D[T]-=H,H=D[T]1;)D.shift()}return function(D,N,T,H){var Y,J,fe,pe,ge,ze,se,Ne,F,$,q,ae,ie,G,xe,Ae,we,Ke,br=D.constructor,Mo=D.s==N.s?1:-1,Gr=D.d,zt=N.d;if(!D.s)return new br(D);if(!N.s)throw Error(a+"Division by zero");for(J=D.e-N.e,we=zt.length,xe=Gr.length,se=new br(Mo),Ne=se.d=[],fe=0;zt[fe]==(Gr[fe]||0);)++fe;if(zt[fe]>(Gr[fe]||0)&&--J,T==null?ae=T=br.precision:H?ae=T+(k(D)-k(N))+1:ae=T,ae<0)return new br(0);if(ae=ae/f+2|0,fe=0,we==1)for(pe=0,zt=zt[0],ae++;(fe1&&(zt=y(zt,pe),Gr=y(Gr,pe),we=zt.length,xe=Gr.length),G=we,F=Gr.slice(0,we),$=F.length;$=d/2&&++Ae;do pe=0,Y=C(zt,F,we,$),Y<0?(q=F[0],we!=$&&(q=q*d+(F[1]||0)),pe=q/Ae|0,pe>1?(pe>=d&&(pe=d-1),ge=y(zt,pe),ze=ge.length,$=F.length,Y=C(ge,F,ze,$),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,H=new pe(.03125);y.abs().gte(.1);)y=y.times(H),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),H=T.plus(x(N,O,Y)),g(H.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=H}}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,H,Y,J,fe,pe,ge=1,ze=10,se=y,Ne=se.d,F=se.constructor,$=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=$):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,$).times(T+""),se=E(new F(D+"."+O.slice(1)),fe-ze).plus(J),F.precision=$,C==null?(n=!0,I(se,$)):se;for(Y=H=se=x(se.minus(c),se.plus(c),fe),pe=I(se.times(se),fe),N=3;;){if(H=I(H.times(pe),fe),J=Y.plus(x(H,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,$).times(T+""))),Y=x(Y,new F(ge),fe),F.precision=$,C==null?(n=!0,I(Y,$)):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,H,Y,J,fe,pe,ge=y.d;for(H=1,T=ge[0];T>=10;T/=10)H++;if(D=C-H,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],H=1;T>=10;T/=10)H++;D%=f,N=D-f+H}if(O!==void 0&&(T=s(10,H-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,H-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,H-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,H,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(),H=fe-D,H){for(pe=H<0,pe?(O=J,H=-H,Y=ge.length):(O=ge,D=fe,Y=J.length),N=Math.max(Math.ceil(se/f),Y)+2,H>N&&(H=N,O.length=1),O.reverse(),N=H;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>H;){if(J[--N]0?T=T.charAt(0)+"."+T.slice(1)+M(D):H>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-H)>0&&(T+=M(D))):N>=H?(T+=M(N+1-H),O&&(D=O-N-1)>0&&(T=T+"."+M(D))):((D=N+1)0&&(N+1===H&&(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 H=this;if(!(H instanceof N))return new N(T);if(H.constructor=N,T instanceof N){H.s=T.s,H.e=T.e,H.d=(T=T.d)?T.slice():T;return}if(typeof T=="number"){if(T*0!==0)throw Error(i+T);if(T>0)H.s=1;else if(T<0)T=-T,H.s=-1;else{H.s=0,H.e=0,H.d=[0];return}if(T===~~T&&T<1e7){H.e=0,H.d=[T];return}return L(H,T.toString())}else if(typeof T!="string")throw Error(i+T);if(T.charCodeAt(0)===45?(T=T.slice(1),H.s=-1):H.s=1,u.test(T))L(H,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 gh<"u"&&gh.exports?gh.exports=r:(e||(e=typeof self<"u"&&self&&self.self==self?self:Function("return this")()),e.Decimal=r)})(y3)});var y8=Or((Mpe,$w)=>{"use strict";var cY=Object.prototype.hasOwnProperty,Kr="~";function yf(){}Object.create&&(yf.prototype=Object.create(null),new yf().__proto__||(Kr=!1));function dY(e,t,r){this.fn=e,this.context=t,this.once=r||!1}function g8(e,t,r,n,a){if(typeof r!="function")throw new TypeError("The listener must be a function");var i=new dY(r,n||e,a),o=Kr?Kr+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 z0(e,t){--e._eventsCount===0?e._events=new yf:delete e._events[t]}function zr(){this._events=new yf,this._eventsCount=0}zr.prototype.eventNames=function(){var t=[],r,n;if(this._eventsCount===0)return t;for(n in r=this._events)cY.call(r,n)&&t.push(Kr?n.slice(1):n);return Object.getOwnPropertySymbols?t.concat(Object.getOwnPropertySymbols(r)):t};zr.prototype.listeners=function(t){var r=Kr?Kr+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 Mt({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=Ro[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(bz,null)),n&&r.length>0&&tt.default.createElement("div",{className:"flex gap-1 mt-1"},r.map(c=>{let d=el[c];return d?tt.default.createElement("span",{key:c,className:"text-sm",title:d.label},d.emoji):null})))}var rn=ee(ne());var la=ee(ne()),rE=(0,la.createContext)();function nE({children:e}){let[t,r]=(0,la.useState)(()=>{let a=localStorage.getItem("dashboard-theme");return a?a==="dark":window.matchMedia("(prefers-color-scheme: dark)").matches});(0,la.useEffect)(()=>{localStorage.setItem("dashboard-theme",t?"dark":"light")},[t]),(0,la.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 la.default.createElement(rE.Provider,{value:{darkMode:t,toggleDarkMode:n}},e)}function mt(){let e=(0,la.useContext)(rE);if(!e)throw new Error("useTheme must be used within a ThemeProvider");return e}function Fs({name:e,subtitle:t,badge:r,accentColor:n,stats:a=[],footer:i,onClick:o,actions:l}){let{darkMode:s}=mt(),[u,c]=rn.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 rn.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"}},rn.default.createElement("div",{style:{display:"flex",alignItems:"flex-start",justifyContent:"space-between",gap:"8px"}},rn.default.createElement("div",{style:{minWidth:0}},rn.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&&rn.default.createElement("div",{style:{fontSize:"13px",color:d.textSecondary,marginTop:"2px",display:"-webkit-box",WebkitLineClamp:2,WebkitBoxOrient:"vertical",overflow:"hidden"}},t)),r&&rn.default.createElement("div",{style:{flexShrink:0}},r)),a.length>0&&rn.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"repeat(3, 1fr)",gap:"6px"}},a.map(p=>rn.default.createElement("div",{key:p.label,title:p.title,style:{background:d.tileBg,borderRadius:"8px",padding:"6px 8px",textAlign:"center"}},rn.default.createElement("div",{style:{fontSize:"14px",fontWeight:600,lineHeight:1.2,color:f(p.tone)}},p.value),rn.default.createElement("div",{style:{fontSize:"10px",textTransform:"uppercase",letterSpacing:"0.04em",color:d.textMuted,whiteSpace:"nowrap"}},p.label)))),i&&rn.default.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",gap:"8px",fontSize:"12px",color:d.textMuted}},i),l&&rn.default.createElement("div",{style:{opacity:u?1:0,transition:"opacity 0.15s ease"}},l))}function wb(e){return e==null?"muted":e>=.85?"good":e>=.7?"warn":"bad"}var wz=[{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 Sb({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"},wz.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(Fs,{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:wb(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:wb(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(Mt,{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=ee(ne());var lr={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 qs={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 Mm(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 qs[e]||[]}var Dm=[{id:"basics",label:"Basics",icon:"1"},{id:"configure",label:"Configure",icon:"2"},{id:"review",label:"Review",icon:"3"}];function Ab({meta:e,onSave:t,onCancel:r,isLoading:n}){let[a,i]=(0,ue.useState)(0),[o,l]=(0,ue.useState)(qs),[s,u]=(0,ue.useState)({name:"",description:"",provider:"openai",model:qs.openai[0],instructions:"",preset_type:"terminal",instruction_sets:["github"],tools:["terminal","code"],model_config:{temperature:.7}});(0,ue.useEffect)(()=>{let h=!1;return Mm(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),Vs[h]){let b=Vs[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"},Dm.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===Dm.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===Dm.length-1?"Create Agent":"Continue")))}function Sz({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 Az({formData:e,meta:t,toggleArrayItem:r,updateField:n,selectPreset:a}){let i=Object.keys(Vs),o=Object.keys(Ro),l=Object.keys(el);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=Ro[s];return u?ue.default.createElement("span",{key:s,className:"text-lg",title:u.label},u.emoji):null})),ue.default.createElement(Mt,{size:120}),ue.default.createElement("div",{className:"flex gap-2 mt-2"},e.tools.map(s=>{let u=el[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=Ro[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=el[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 Nz({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=Ro[t];return r?ue.default.createElement("span",{key:t,className:"text-lg",title:r.label},r.emoji):null})),ue.default.createElement(Mt,{size:100}),ue.default.createElement("div",{className:"flex gap-2 mt-2"},e.tools.map(t=>{let r=el[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=Ro[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=el[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 K=ee(ne());var Ys=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"}),sa="#ef4444";var w=ee(ne());var Pm=ee(ne()),sE=ee(ne());function aE(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=Oz(e,kz),d=i||{width:n,height:a,x:0,y:0},f=Le("recharts-surface",o);return Pm.createElement("svg",Cb({},Kt(c),{className:f,width:n,height:a,style:l,viewBox:"".concat(d.x," ").concat(d.y," ").concat(d.width," ").concat(d.height),ref:t}),Pm.createElement("title",null,s),Pm.createElement("desc",null,u),r)});var jm=ee(ne());var Mz=["children","className"];function kb(){return kb=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var r=e.children,n=e.className,a=Dz(e,Mz),i=Le("recharts-layer",n);return jm.createElement("g",kb({className:i},Kt(a),{ref:t}),r)});function Im(e){return e==="__proto__"}var jz=/\.|(\[(?:[^[\]]*|(["'])(?:(?!\2)[^\\]|\\.)*?\2)\])/;function zm(e){switch(typeof e){case"number":case"symbol":return!1;case"string":return e===""||e.startsWith(".")||e.endsWith(".")?!1:jz.test(e);default:return!1}}function Ws(e){return typeof e=="string"||typeof e=="symbol"?e:Object.is(e?.valueOf?.(),-0)?"-0":String(e)}function Ob(e){if(e==null)return"";if(typeof e=="string")return e;if(Array.isArray(e))return e.map(Ob).join(",");let t=String(e);return t==="0"&&Object.is(Number(e),-0)?"-0":t}function Ks(e){if(Array.isArray(e))return e.map(Ws);if(typeof e=="symbol")return[e];e=Ob(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]:zz,r=10**t,n=Math.round(e*r)/r;return Object.is(n,-0)?0:n}function ur(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+_a(l)+i:a+i},"")}var cr=e=>e===0?0:e>0?1:-1,Mr=e=>typeof e=="number"&&e!=+e,si=e=>typeof e=="string"&&e.length>1&&e.indexOf("%")===e.length-1,ye=e=>(typeof e=="number"||e instanceof Number)&&!Mr(e),qr=e=>ye(e)||typeof e=="string",Lz=0,ui=e=>{var t=++Lz;return"".concat(e||"").concat(t)},Fn=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(si(t)){if(r==null)return n;var o=t.indexOf("%");i=r*parseFloat(t.slice(0,o))/100}else i=+t;return Mr(i)&&(i=n),a&&r!=null&&i>r&&(i=r),i},Tb=e=>{if(!Array.isArray(e))return!1;for(var t=e.length,r={},n=0;nn&&(typeof t=="function"?t(n):wn(n,t))===r)}var Dt=e=>e===null||typeof e>"u",Gs=e=>Dt(e)?e:"".concat(e.charAt(0).toUpperCase()).concat(e.slice(1));function dr(e){return e!=null}function ua(){}function md(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 uE(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 Ca(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=md(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"?Ca({x:h+M,y:u+c/2,horizontalAnchor:L,verticalAnchor:"middle"},Q):r==="insideRight"?Ca({x:h+b-M,y:u+c/2,horizontalAnchor:E,verticalAnchor:"middle"},Q):r==="insideTop"?Ca({x:p+d/2,y:u+x,horizontalAnchor:"middle",verticalAnchor:k},Q):r==="insideBottom"?Ca({x:v+f/2,y:u+c-x,horizontalAnchor:"middle",verticalAnchor:A},Q):r==="insideTopLeft"?Ca({x:p+M,y:u+x,horizontalAnchor:L,verticalAnchor:k},Q):r==="insideTopRight"?Ca({x:p+d-M,y:u+x,horizontalAnchor:E,verticalAnchor:k},Q):r==="insideBottomLeft"?Ca({x:v+M,y:u+c-x,horizontalAnchor:L,verticalAnchor:A},Q):r==="insideBottomRight"?Ca({x:v+f-M,y:u+c-x,horizontalAnchor:E,verticalAnchor:A},Q):r&&typeof r=="object"&&(ye(r.x)||si(r.x))&&(ye(r.y)||si(r.y))?Ca({x:s+Fn(r.x,b),y:u+Fn(r.y,c),horizontalAnchor:"end",verticalAnchor:"end"},Q):Ca({x:m,y:u+c/2,horizontalAnchor:"middle",verticalAnchor:"middle"},Q)},Hz=["top","left","right","bottom"];function dE(e){return e==null?!1:typeof e=="object"?!0:Hz.includes(e)}var Mb=ee(ne()),fE=(0,Mb.createContext)(null);function _t(e){return function(){return e}}var Db=Math.PI,Pb=2*Db,tl=1e-6,Vz=Pb-tl;function pE(e){this._+=e[0];for(let t=1,r=e.length;t=0))throw new Error(`invalid digits: ${e}`);if(t>15)return pE;let r=10**t;return function(n){this._+=n[0];for(let a=1,i=n.length;atl)if(!(Math.abs(d*s-u*c)>tl)||!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((Db-Math.acos((h+f-b)/(2*m*g)))/2),A=x/g,k=x/m;Math.abs(A-1)>tl&&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)>tl||Math.abs(this._y1-c)>tl)&&this._append`L${u},${c}`,n&&(f<0&&(f=f%Pb+Pb),f>Vz?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>tl&&this._append`A${n},${n},0,${+(f>=Db)},${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 mE(){return new rl}mE.prototype=rl.prototype;function Bm(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 rl(t)}var NJ=Array.prototype.slice;function Xs(e){return typeof e=="object"&&"length"in e?e:Array.from(e)}function vE(e){this._context=e}vE.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 co(e){return new vE(e)}function Um(e){return e[0]}function $m(e){return e[1]}function vd(e,t){var r=_t(!0),n=null,a=co,i=null,o=Bm(l);e=typeof e=="function"?e:e===void 0?Um:_t(e),t=typeof t=="function"?t:t===void 0?$m:_t(t);function l(s){var u,c=(s=Xs(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 vd().defined(a).curve(o).context(i)}return u.x=function(d){return arguments.length?(e=typeof d=="function"?d:_t(+d),n=null,u):e},u.x0=function(d){return arguments.length?(e=typeof d=="function"?d:_t(+d),u):e},u.x1=function(d){return arguments.length?(n=d==null?null:typeof d=="function"?d:_t(+d),u):n},u.y=function(d){return arguments.length?(t=typeof d=="function"?d:_t(+d),r=null,u):t},u.y0=function(d){return arguments.length?(t=typeof d=="function"?d:_t(+d),u):t},u.y1=function(d){return arguments.length?(r=d==null?null:typeof d=="function"?d:_t(+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:_t(!!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 Hm=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 jb(e){return new Hm(e,!0)}function Ib(e){return new Hm(e,!1)}function Qs(){}function Js(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 hE(e){this._context=e}hE.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:Js(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:Js(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:Qs,areaEnd:Qs,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:Js(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: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:Js(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function Bb(e){return new yE(e)}function xE(e){this._context=e}xE.prototype={areaStart:Qs,areaEnd:Qs,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 Ub(e){return new xE(e)}function bE(e){return e<0?-1:1}function wE(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(bE(i)+bE(o))*Math.min(Math.abs(i),Math.abs(o),.5*Math.abs(l))||0}function SE(e,t){var r=e._x1-e._x0;return r?(3*(e._y1-e._y0)/r-t)/2:t}function $b(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 Vm(e){this._context=e}Vm.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:$b(this,this._t0,SE(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,$b(this,SE(this,r=wE(this,e,t)),r);break;default:$b(this,this._t0,r=wE(this,e,t));break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t,this._t0=r}}};function AE(e){this._context=new NE(e)}(AE.prototype=Object.create(Vm.prototype)).point=function(e,t){Vm.prototype.point.call(this,t,e)};function NE(e){this._context=e}NE.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 Hb(e){return new Vm(e)}function Vb(e){return new AE(e)}function CE(e){this._context=e}CE.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=_E(e),a=_E(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 qb(e){return new Fm(e,.5)}function Yb(e){return new Fm(e,0)}function Wb(e){return new Fm(e,1)}function Sn(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 qz(e,t){return e[t]}function Yz(e){let t=[];return t.key=e,t}function Kb(){var e=_t([]),t=Rs,r=Sn,n=qz;function a(i){var o=Array.from(e.apply(this,arguments),Yz),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,eu=(e,t)=>{if(!e||typeof e=="function"||typeof e=="boolean")return null;var r=e;if((0,EE.isValidElement)(e)&&(r=e.props),typeof r!="object"&&typeof r!="function")return null;var n={};return Object.keys(r).forEach(a=>{pd(a)&&typeof r[a]=="function"&&(n[a]=t||(i=>r[a](r,i)))}),n},Wz=(e,t,r)=>n=>(e(t,r,n),null),kE=(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];pd(a)&&typeof i=="function"&&(n||(n={}),n[a]=Wz(i,t,r))}),n};function OE(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 Kz(e){for(var t=1;t(o[l]===void 0&&n[l]!==void 0&&(o[l]=n[l]),o),r);return i}function TE(e,t){let r=new Map;for(let n=0;nObject.prototype.propertyIsEnumerable.call(e,t))}function ci(e){return e==null?e===void 0?"[object Undefined]":"[object Null]":Object.prototype.toString.call(e)}var Wm="[object RegExp]",tu="[object String]",ru="[object Number]",nu="[object Boolean]",Km="[object Arguments]",Gm="[object Symbol]",Xm="[object Date]",Zm="[object Map]",Qm="[object Set]",Jm="[object Array]",jE="[object Function]",Rm="[object ArrayBuffer]",au="[object Object]",IE="[object Error]",ev="[object DataView]",tv="[object Uint8Array]",rv="[object Uint8ClampedArray]",nv="[object Uint16Array]",av="[object Uint32Array]",zE="[object BigUint64Array]",iv="[object Int8Array]",ov="[object Int16Array]",lv="[object Int32Array]",LE="[object BigInt64Array]",sv="[object Float32Array]",uv="[object Float64Array]";var Qb=typeof globalThis=="object"&&globalThis||typeof window=="object"&&window||typeof self=="object"&&self||typeof global=="object"&&global||(function(){return this})();function yd(e){return typeof Qb.Buffer<"u"&&Qb.Buffer.isBuffer(e)}function BE(e,t){return fo(e,void 0,e,new Map,t)}function fo(e,t,r,n=new Map,a=void 0){let i=a?.(e,t,r,n);if(i!==void 0)return i;if(hd(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{}):Jb(e,t,function n(a,i,o,l,s,u){let c=r(a,i,o,l,s,u);return c!==void 0?!!c:Jb(a,i,n,u,!1)},new Map,!0)}function Jb(e,t,r,n,a=!1){if(t===e)return!0;switch(typeof t){case"object":return Jz(e,t,r,n);case"function":return Object.keys(t).length>0?Jb(e,{...t},r,n,a):po(e,t);default:return cv(e)?a?typeof t=="string"?t==="":!0:po(e,t):po(e,t)}}function Jz(e,t,r,n){if(t==null)return!0;if(Array.isArray(t))return $E(e,t,r,n);if(t instanceof Map)return Rz(e,t,r,n);if(t instanceof Set)return e9(e,t,r,n);let a=Object.keys(t);if(e==null||hd(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 HE(e){return e=UE(e),t=>dv(t,e)}function VE(e,t){return BE(e,(r,n,a,i)=>{let o=t?.(r,n,a,i);if(o!==void 0)return o;if(typeof e=="object"){if(ci(e)==="[object Object]"&&typeof e.constructor!="function"){let l={};return i.set(e,l),Yn(l,e,a,i),l}switch(Object.prototype.toString.call(e)){case ru:case tu:case nu:{let l=new e.constructor(e?.valueOf());return Yn(l,e),l}case Km:{let l={};return Yn(l,e),l.length=e.length,l[Symbol.iterator]=e[Symbol.iterator],l}default:return}}})}function FE(e){return VE(e)}var t9=/^(?:0|[1-9]\d*)$/;function fv(e,t=Number.MAX_SAFE_INTEGER){switch(typeof e){case"number":return Number.isInteger(e)&&e>=0&&e=0}function pv(e){return e!=null&&typeof e!="function"&&GE(e.length)}function XE(e){return typeof e=="object"&&e!==null}function ZE(e){return XE(e)&&pv(e)}function mv(e,t=Ym){return ZE(e)?TE(Array.from(e),ME(KE(t),1)):[]}function QE(e,t,r){return t===!0?mv(e,r):typeof t=="function"?mv(e,t):e}var lk=ee(ik()),bd=ee(ne());var ok=ee(ne()),xd=(0,ok.createContext)(null);var x9=e=>e,Ve=()=>{var e=(0,bd.useContext)(xd);return e?e.store.dispatch:x9},hv=()=>{},b9=()=>hv,w9=(e,t)=>e===t;function Se(e){var t=(0,bd.useContext)(xd),r=(0,bd.useMemo)(()=>t?n=>{if(n!=null)return e(n)}:hv,[t,e]);return(0,lk.useSyncExternalStoreWithSelector)(t?t.subscription.addNestedSub:b9,t?t.store.getState:hv,t?t.store.getState:hv,r,w9)}function S9(e,t=`expected a function, instead received ${typeof e}`){if(typeof e!="function")throw new TypeError(t)}function A9(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 sk=e=>Array.isArray(e)?e:[e];function N9(e){let t=Array.isArray(e[0])?e[0]:e;return A9(t,"createSelector expects all input-selectors to be functions, but received the following types: "),t}function _9(e,t){let r=[],{length:n}=e;for(let a=0;atypeof WeakRef>"u"?C9:WeakRef,ck=E9(),k9=0,uk=1;function gv(){return{s:k9,v:void 0,o:null,p:null}}function O9(e){return e instanceof ck?e.deref():e}function dk(e,t={}){let r=gv(),{resultEqualityCheck:n}=t,a,i=0;function o(){let l=r,{length:s}=arguments;for(let d=0,f=s;d{r=gv(),o.resetResultsCount()},o.resultsCount=()=>i,o.resetResultsCount=()=>{i=0},o}function T9(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()),S9(u,`createSelector expects an output function after the inputs, but received: [${typeof u}]`);let c={...r,...s},{memoize:d,memoizeOptions:f=[],argsMemoize:p=dk,argsMemoizeOptions:v=[]}=c,h=sk(f),b=sk(v),m=N9(a),g=d(function(){return i++,u.apply(null,arguments)},...h),x=!0,A=p(function(){o++;let _=_9(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=T9(dk);function fk(e,t=1){let r=[],n=Math.floor(t),a=(i,o)=>{for(let l=0;l{if(e!==t){let n=pk(e),a=pk(t);if(n===a&&n===0){if(et)return r==="desc"?-1:1}return r==="desc"?a-n:n-a}return 0};function yv(e){return typeof e=="symbol"||e instanceof Symbol}var M9=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,D9=/^\w*$/;function vk(e,t){return Array.isArray(e)?!1:typeof e=="number"||typeof e=="boolean"||e==null||yv(e)?!0:typeof e=="string"&&(D9.test(e)||!M9.test(e))||t!=null&&Object.hasOwn(t,e)}function hk(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)||vk(l)?l:{key:l,path:Ks(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 di(e,...t){let r=t.length;return r>1&&wd(e,t[0],t[1])?t=[]:r>2&&wd(t[0],t[1],t[2])&&(t=[t[0]]),hk(e,fk(t),["asc"])}var t1=e=>e.legend.settings,gk=e=>e.legend.size,P9=e=>e.legend.payload,jte=V([P9,t1],(e,t)=>{var r=t.itemSorter,n=e.flat(1);return r?di(n,r):n});var fi=ee(ne());function j9(e,t){return B9(e)||L9(e,t)||z9(e,t)||I9()}function I9(){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 z9(e,t){if(e){if(typeof e=="string")return yk(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)?yk(e,t):void 0}}function yk(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);rxv||Math.abs(e.left-t.left)>xv||Math.abs(e.top-t.top)>xv||Math.abs(e.width-t.width)>xv}function bk(e){var t=e.getBoundingClientRect();return{height:t.height,left:t.left,top:t.top,width:t.width}}function wk(){var e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:[],t=(0,fi.useState)({height:0,left:0,top:0,width:0}),r=j9(t,2),n=r[0],a=r[1],i=(0,fi.useRef)(null),o=(0,fi.useRef)(n);o.current=n;var l=(0,fi.useCallback)(s=>{if(i.current!=null&&(i.current.disconnect(),i.current=null),s!=null){var u=bk(s);if(xk(u,o.current)&&a(u),typeof ResizeObserver<"u"){var c=new ResizeObserver(()=>{var d=bk(s);xk(d,o.current)&&a(d)});c.observe(s),i.current=c}}},[...e]);return(0,fi.useEffect)(()=>()=>{var s;(s=i.current)===null||s===void 0||s.disconnect()},[]),[n,l]}var UO=ee(ne());function Cr(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 U9=typeof Symbol=="function"&&Symbol.observable||"@@observable",Sk=U9,r1=()=>Math.random().toString(36).substring(7).split("").join("."),$9={INIT:`@@redux/INIT${r1()}`,REPLACE:`@@redux/REPLACE${r1()}`,PROBE_UNKNOWN_ACTION:()=>`@@redux/PROBE_UNKNOWN_ACTION${r1()}`},bv=$9;function wv(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 n1(e,t,r){if(typeof e!="function")throw new Error(Cr(2));if(typeof t=="function"&&typeof r=="function"||typeof r=="function"&&typeof arguments[3]=="function")throw new Error(Cr(0));if(typeof t=="function"&&typeof r>"u"&&(r=t,t=void 0),typeof r<"u"){if(typeof r!="function")throw new Error(Cr(1));return r(n1)(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(Cr(3));return a}function d(b){if(typeof b!="function")throw new Error(Cr(4));if(s)throw new Error(Cr(5));let m=!0;u();let g=l++;return o.set(g,b),function(){if(m){if(s)throw new Error(Cr(6));m=!1,u(),o.delete(g),i=null}}}function f(b){if(!wv(b))throw new Error(Cr(7));if(typeof b.type>"u")throw new Error(Cr(8));if(typeof b.type!="string")throw new Error(Cr(17));if(s)throw new Error(Cr(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(Cr(10));n=b,f({type:bv.REPLACE})}function v(){let b=d;return{subscribe(m){if(typeof m!="object"||m===null)throw new Error(Cr(11));function g(){let A=m;A.next&&A.next(c())}return g(),{unsubscribe:b(g)}},[Sk](){return this}}}return f({type:bv.INIT}),{dispatch:f,subscribe:d,getState:c,replaceReducer:p,[Sk]:v}}function H9(e){Object.keys(e).forEach(t=>{let r=e[t];if(typeof r(void 0,{type:bv.INIT})>"u")throw new Error(Cr(12));if(typeof r(void 0,{type:bv.PROBE_UNKNOWN_ACTION()})>"u")throw new Error(Cr(13))})}function Sv(e){let t=Object.keys(e),r={};for(let o=0;o"u"){let b=s&&s.type;throw new Error(Cr(14))}c[f]=h,u=u||h!==v}return u=u||n.length!==Object.keys(l).length,u?c:l}}function Sd(...e){return e.length===0?t=>t:e.length===1?e[0]:e.reduce((t,r)=>(...n)=>t(r(...n)))}function Ak(...e){return t=>(r,n)=>{let a=t(r,n),i=()=>{throw new Error(Cr(15))},o={getState:a.getState,dispatch:(s,...u)=>i(s,...u)},l=e.map(s=>s(o));return i=Sd(...l)(a.dispatch),{...a,dispatch:i}}}function a1(e){return wv(e)&&"type"in e&&typeof e.type=="string"}var Dk=Symbol.for("immer-nothing"),Nk=Symbol.for("immer-draftable"),Dr=Symbol.for("immer-state");function ca(e,...t){throw new Error(`[Immer] minified error nr: ${e}. Full error at: https://bit.ly/3cXEKWf`)}var An=Object,lu=An.getPrototypeOf,Cv="constructor",Dv="prototype",s1="configurable",Ev="enumerable",Nv="writable",Ad="value",Ea=e=>!!e&&!!e[Dr];function Nn(e){return e?Pk(e)||jv(e)||!!e[Nk]||!!e[Cv]?.[Nk]||Iv(e)||zv(e):!1}var V9=An[Dv][Cv].toString(),_k=new WeakMap;function Pk(e){if(!e||!h1(e))return!1;let t=lu(e);if(t===null||t===An[Dv])return!0;let r=An.hasOwnProperty.call(t,Cv)&&t[Cv];if(r===Object)return!0;if(!ou(r))return!1;let n=_k.get(r);return n===void 0&&(n=Function.toString.call(r),_k.set(r,n)),n===V9}function Pv(e,t,r=!0){Cd(e)===0?(r?Reflect.ownKeys(e):An.keys(e)).forEach(a=>{t(a,e[a],e)}):e.forEach((n,a)=>t(a,n,e))}function Cd(e){let t=e[Dr];return t?t.type_:jv(e)?1:Iv(e)?2:zv(e)?3:0}var i1=(e,t,r=Cd(e))=>r===2?e.has(t):An[Dv].hasOwnProperty.call(e,t),u1=(e,t,r=Cd(e))=>r===2?e.get(t):e[t],kv=(e,t,r,n=Cd(e))=>{n===2?e.set(t,r):n===3?e.add(r):e[t]=r};function F9(e,t){return e===t?e!==0||1/e===1/t:e!==e&&t!==t}var jv=Array.isArray,Iv=e=>e instanceof Map,zv=e=>e instanceof Set,h1=e=>typeof e=="object",ou=e=>typeof e=="function",o1=e=>typeof e=="boolean";function q9(e){let t=+e;return Number.isInteger(t)&&String(t)===e}var pi=e=>e.copy_||e.base_;var g1=e=>e.modified_?e.copy_:e.base_;function c1(e,t){if(Iv(e))return new Map(e);if(zv(e))return new Set(e);if(jv(e))return Array[Dv].slice.call(e);let r=Pk(e);if(t===!0||t==="class_only"&&!r){let n=An.getOwnPropertyDescriptors(e);delete n[Dr];let a=Reflect.ownKeys(n);for(let i=0;i1&&An.defineProperties(e,{set:Av,add:Av,clear:Av,delete:Av}),An.freeze(e),t&&Pv(e,(r,n)=>{y1(n,!0)},!1)),e}function Y9(){ca(2)}var Av={[Ad]:Y9};function Lv(e){return e===null||!h1(e)?!0:An.isFrozen(e)}var Ov="MapSet",d1="Patches",Ck="ArrayMethods",jk={};function nl(e){let t=jk[e];return t||ca(0,e),t}var Ek=e=>!!jk[e];var Nd,Ik=()=>Nd,W9=(e,t)=>({drafts_:[],parent_:e,immer_:t,canAutoFreeze_:!0,unfinalizedDrafts_:0,handledSet_:new Set,processedForPatches_:new Set,mapSetPlugin_:Ek(Ov)?nl(Ov):void 0,arrayMethodsPlugin_:Ek(Ck)?nl(Ck):void 0});function kk(e,t){t&&(e.patchPlugin_=nl(d1),e.patches_=[],e.inversePatches_=[],e.patchListener_=t)}function f1(e){p1(e),e.drafts_.forEach(K9),e.drafts_=null}function p1(e){e===Nd&&(Nd=e.parent_)}var Ok=e=>Nd=W9(Nd,e);function K9(e){let t=e[Dr];t.type_===0||t.type_===1?t.revoke_():t.revoked_=!0}function Tk(e,t){t.unfinalizedDrafts_=t.drafts_.length;let r=t.drafts_[0];if(e!==void 0&&e!==r){r[Dr].modified_&&(f1(t),ca(4)),Nn(e)&&(e=Mk(t,e));let{patchPlugin_:a}=t;a&&a.generateReplacementPatches_(r[Dr].base_,e,t)}else e=Mk(t,r);return G9(t,e,!0),f1(t),t.patches_&&t.patchListener_(t.patches_,t.inversePatches_),e!==Dk?e:void 0}function Mk(e,t){if(Lv(t))return t;let r=t[Dr];if(!r)return Tv(t,e.handledSet_,e);if(!Bv(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);Bk(r,e)}return r.copy_}function G9(e,t,r=!1){!e.parent_&&e.immer_.autoFreeze_&&e.canAutoFreeze_&&y1(t,r)}function zk(e){e.finalized_=!0,e.scope_.unfinalizedDrafts_--}var Bv=(e,t)=>e.scope_===t,X9=[];function Lk(e,t,r,n){let a=pi(e),i=e.type_;if(n!==void 0&&u1(a,n,i)===t){kv(a,n,r,i);return}if(!e.draftLocations_){let l=e.draftLocations_=new Map;Pv(a,(s,u)=>{if(Ea(u)){let c=l.get(u)||[];c.push(s),l.set(u,c)}})}let o=e.draftLocations_.get(t)??X9;for(let l of o)kv(a,l,r,i)}function Z9(e,t,r){e.callbacks_.push(function(a){let i=t;if(!i||!Bv(i,a))return;a.mapSetPlugin_?.fixSetContents(i);let o=g1(i);Lk(e,i.draft_??i,o,r),Bk(i,a)})}function Bk(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)}zk(e)}}function Q9(e,t,r){let{scope_:n}=e;if(Ea(r)){let a=r[Dr];Bv(a,n)&&a.callbacks_.push(function(){_v(e);let o=g1(a);Lk(e,r,o,t)})}else Nn(r)&&e.callbacks_.push(function(){let i=pi(e);e.type_===3?i.has(r)&&Tv(r,n.handledSet_,n):u1(i,t,e.type_)===r&&n.drafts_.length>1&&(e.assigned_.get(t)??!1)===!0&&e.copy_&&Tv(u1(e.copy_,t,e.type_),n.handledSet_,n)})}function Tv(e,t,r){return!r.immer_.autoFreeze_&&r.unfinalizedDrafts_<1||Ea(e)||t.has(e)||!Nn(e)||Lv(e)||(t.add(e),Pv(e,(n,a)=>{if(Ea(a)){let i=a[Dr];if(Bv(i,r)){let o=g1(i);kv(e,n,o,e.type_),zk(i)}}else Nn(a)&&Tv(a,t,r)})),e}function J9(e,t){let r=jv(e),n={type_:r?1:0,scope_:t?t.scope_:Ik(),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=Mv;r&&(a=[n],i=_d);let{revoke:o,proxy:l}=Proxy.revocable(a,i);return n.draft_=l,n.revoke_=o,[l,n]}var Mv={get(e,t){if(t===Dr)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=pi(e);if(!i1(a,t,e.type_))return eL(e,a,t);let i=a[t];if(e.finalized_||!Nn(i)||n&&e.operationMethod&&r?.isMutatingArrayMethod(e.operationMethod)&&q9(t))return i;if(i===l1(e.base_,t)||R9(e,t,i)){_v(e);let o=e.type_===1?+t:t,l=v1(e.scope_,i,e,o);return e.copy_[o]=l}return i},has(e,t){return t in pi(e)},ownKeys(e){return Reflect.ownKeys(pi(e))},set(e,t,r){let n=Uk(pi(e),t);if(n?.set)return n.set.call(e.draft_,r),!0;if(!e.modified_){let a=l1(pi(e),t),i=a?.[Dr];if(i&&i.base_===r)return e.copy_[t]=r,e.assigned_.set(t,!1),!0;if(F9(r,a)&&(r!==void 0||i1(e.base_,t,e.type_)))return!0;_v(e),m1(e)}return e.copy_[t]===r&&(r!==void 0||i1(e.copy_,t,e.type_))||Number.isNaN(r)&&Number.isNaN(e.copy_[t])||(e.copy_[t]=r,e.assigned_.set(t,!0),Q9(e,t,r)),!0},deleteProperty(e,t){return _v(e),l1(e.base_,t)!==void 0||t in e.base_?(e.assigned_.set(t,!1),m1(e)):e.assigned_.delete(t),e.copy_&&delete e.copy_[t],!0},getOwnPropertyDescriptor(e,t){let r=pi(e),n=Reflect.getOwnPropertyDescriptor(r,t);return n&&{[Nv]:!0,[s1]:e.type_!==1||t!=="length",[Ev]:n[Ev],[Ad]:r[t]}},defineProperty(){ca(11)},getPrototypeOf(e){return lu(e.base_)},setPrototypeOf(){ca(12)}},_d={};for(let e in Mv){let t=Mv[e];_d[e]=function(){let r=arguments;return r[0]=r[0][0],t.apply(this,r)}}_d.deleteProperty=function(e,t){return _d.set.call(this,e,t,void 0)};_d.set=function(e,t,r){return Mv.set.call(this,e[0],t,r,e[0])};function l1(e,t){let r=e[Dr];return(r?pi(r):e)[t]}function R9(e,t,r){return e.type_!==1||!e.allIndicesReassigned_||e.assigned_?.get(t)||!Nn(r)||r[Dr]?!1:e.baseRefs_.has(r)}function eL(e,t,r){let n=Uk(t,r);return n?Ad in n?n[Ad]:n.get?.call(e.draft_):void 0}function Uk(e,t){if(!(t in e))return;let r=lu(e);for(;r;){let n=Object.getOwnPropertyDescriptor(r,t);if(n)return n;r=lu(r)}}function m1(e){e.modified_||(e.modified_=!0,e.parent_&&m1(e.parent_))}function _v(e){e.copy_||(e.assigned_=new Map,e.copy_=c1(e.base_,e.scope_.immer_.useStrictShallowCopy_))}var tL=class{constructor(e){this.autoFreeze_=!0,this.useStrictShallowCopy_=!1,this.useStrictIteration_=!1,this.produce=(t,r,n)=>{if(ou(t)&&!ou(r)){let i=r;r=t;let o=this;return function(s=i,...u){return o.produce(s,c=>r.call(this,c,...u))}}ou(r)||ca(6),n!==void 0&&!ou(n)&&ca(7);let a;if(Nn(t)){let i=Ok(this),o=v1(i,t,void 0),l=!0;try{a=r(o),l=!1}finally{l?f1(i):p1(i)}return kk(i,n),Tk(a,i)}else if(!t||!h1(t)){if(a=r(t),a===void 0&&(a=t),a===Dk&&(a=void 0),this.autoFreeze_&&y1(a,!0),n){let i=[],o=[];nl(d1).generateReplacementPatches_(t,a,{patches_:i,inversePatches_:o}),n(i,o)}return a}else ca(1,t)},this.produceWithPatches=(t,r)=>{if(ou(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]},o1(e?.autoFreeze)&&this.setAutoFreeze(e.autoFreeze),o1(e?.useStrictShallowCopy)&&this.setUseStrictShallowCopy(e.useStrictShallowCopy),o1(e?.useStrictIteration)&&this.setUseStrictIteration(e.useStrictIteration)}createDraft(e){Nn(e)||ca(8),Ea(e)&&(e=Er(e));let t=Ok(this),r=v1(t,e,void 0);return r[Dr].isManual_=!0,p1(t),r}finishDraft(e,t){let r=e&&e[Dr];(!r||!r.isManual_)&&ca(9);let{scope_:n}=r;return kk(n,t),Tk(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=nl(d1).applyPatches_;return Ea(e)?n(e,t):this.produce(e,a=>n(a,t))}};function v1(e,t,r,n){let[a,i]=Iv(t)?nl(Ov).proxyMap_(t,r):zv(t)?nl(Ov).proxySet_(t,r):J9(t,r);return(r?.scope_??Ik()).drafts_.push(a),i.callbacks_=r?.callbacks_??[],i.key_=n,r&&n!==void 0?Z9(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 Er(e){return Ea(e)||ca(10,e),$k(e)}function $k(e){if(!Nn(e)||Lv(e))return e;let t=e[Dr],r,n=!0;if(t){if(!t.modified_)return t.base_;t.finalized_=!0,r=c1(e,t.scope_.immer_.useStrictShallowCopy_),n=t.scope_.immer_.shouldUseStrictIteration()}else r=c1(e,!0);return Pv(r,(a,i)=>{kv(r,a,$k(i))},n),t&&(t.finalized_=!1),r}var rL=new tL,x1=rL.produce;var $e=e=>e;function Hk(e){return({dispatch:r,getState:n})=>a=>i=>typeof i=="function"?i(r,n,e):a(i)}var Vk=Hk(),Fk=Hk;var nL=typeof window<"u"&&window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__:function(){if(arguments.length!==0)return typeof arguments[0]=="object"?Sd:Sd.apply(null,arguments)},qte=typeof window<"u"&&window.__REDUX_DEVTOOLS_EXTENSION__?window.__REDUX_DEVTOOLS_EXTENSION__:function(){return function(e){return e}},aL=e=>e&&typeof e.match=="function";function rr(e,t){function r(...n){if(t){let a=t(...n);if(!a)throw new Error(_n(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=>a1(n)&&n.type===e,r}var Qk=class Ed extends Array{constructor(...t){super(...t),Object.setPrototypeOf(this,Ed.prototype)}static get[Symbol.species](){return Ed}concat(...t){return super.concat.apply(this,t)}prepend(...t){return t.length===1&&Array.isArray(t[0])?new Ed(...t[0].concat(this)):new Ed(...t.concat(this))}};function qk(e){return Nn(e)?x1(e,()=>{}):e}function Uv(e,t,r){return e.has(t)?e.get(t):e.set(t,r(t)).get(t)}function iL(e){return typeof e=="boolean"}var oL=()=>function(t){let{thunk:r=!0,immutableCheck:n=!0,serializableCheck:a=!0,actionCreatorCheck:i=!0}=t??{},o=new Qk;return r&&(iL(r)?o.push(Vk):o.push(Fk(r.extraArgument))),o},Jk="RTK_autoBatch",st=()=>e=>({payload:e,meta:{[Jk]:!0}}),Yk=e=>t=>{setTimeout(t,e)},lL=(e,t)=>r=>{let n=!1,a=()=>{n||(n=!0,cancelAnimationFrame(i),clearTimeout(o),r())},i=e(a),o=setTimeout(a,t)},A1=(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?lL(window.requestAnimationFrame,100):Yk(10):e.type==="callback"?e.queueNotification:Yk(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?.[Jk],i=!a,i&&(o||(o=!0,s(u))),n.dispatch(c)}finally{a=!0}}})},sL=e=>function(r){let{autoBatch:n=!0}=r??{},a=new Qk(e);return n&&a.push(A1(typeof n=="object"?n:void 0)),a};function Rk(e){let t=oL(),{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(wv(r))s=Sv(r);else throw new Error(_n(1));let u;typeof n=="function"?u=n(t):u=t();let c=Sd;a&&(c=nL({trace:!1,...typeof a=="object"&&a}));let d=Ak(...u),f=sL(d),p=typeof l=="function"?l(f):f(),v=c(...p);return n1(s,o,v)}function eO(e){let t={},r=[],n,a={addCase(i,o){let l=typeof i=="string"?i:i.type;if(!l)throw new Error(_n(28));if(l in t)throw new Error(_n(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 uL(e){return typeof e=="function"}function cL(e,t){let[r,n,a]=eO(t),i;if(uL(e))i=()=>qk(e());else{let l=qk(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(Ea(c)){let p=d(c,s);return p===void 0?c:p}else{if(Nn(c))return x1(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 dL=(e,t)=>aL(e)?e.match(t):e(t);function fL(...e){return t=>e.some(r=>dL(r,t))}var pL="ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW",tO=(e=21)=>{let t="",r=e;for(;r--;)t+=pL[Math.random()*64|0];return t},mL=["name","message","stack","code"],b1=class{constructor(e,t){this.payload=e,this.meta=t}payload;meta;_type},Wk=class{constructor(e,t){this.payload=e,this.meta=t}payload;meta;_type},vL=e=>{if(typeof e=="object"&&e!==null){let t={};for(let r of mL)typeof e[r]=="string"&&(t[r]=e[r]);return t}return{message:String(e)}},Kk="External signal was aborted",hL=(()=>{function e(t,r,n){let a=rr(t+"/fulfilled",(s,u,c,d)=>({payload:s,meta:{...d||{},arg:c,requestId:u,requestStatus:"fulfilled"}})),i=rr(t+"/pending",(s,u,c)=>({payload:void 0,meta:{...c||{},arg:u,requestId:s,requestStatus:"pending"}})),o=rr(t+"/rejected",(s,u,c,d,f)=>({payload:d,error:(n&&n.serializeError||vL)(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):tO(),v=new AbortController,h,b;function m(x){b=x,v.abort()}u&&(u.aborted?m(Kk):u.addEventListener("abort",()=>m(Kk),{once:!0}));let g=(async function(){let x;try{let k=n?.condition?.(s,{getState:d,extra:f});if(yL(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 b1(M,E)),fulfillWithValue:((M,E)=>new Wk(M,E))})).then(M=>{if(M instanceof b1)throw M;return M instanceof Wk?a(M.payload,p,s,M.meta):a(M,p,s)})])}catch(k){x=k instanceof b1?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(gL)}})}}return Object.assign(l,{pending:i,rejected:o,fulfilled:a,settled:fL(o,a),typePrefix:t})}return e.withTypes=()=>e,e})();function gL(e){if(e.meta&&e.meta.rejectedWithValue)throw e.payload;if(e.error)throw e.error;return e.payload}function yL(e){return e!==null&&typeof e=="object"&&typeof e.then=="function"}var rO=Symbol.for("rtk-slice-createasyncthunk"),Wte={[rO]:hL};function xL(e,t){return`${e}/${t}`}function bL({creators:e}={}){let t=e?.asyncThunk?.[rO];return function(n){let{name:a,reducerPath:i=a}=n;if(!a)throw new Error(_n(11));typeof process<"u";let o=(typeof n.reducers=="function"?n.reducers(SL()):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(_n(12));if(k in s.sliceCaseReducersByType)throw new Error(_n(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:xL(a,x),createNotation:typeof n.reducers=="function"};NL(A)?CL(k,A,u,t):AL(k,A,u)});function c(){let[x={},A=[],k=void 0]=typeof n.extraReducers=="function"?eO(n.extraReducers):[n.extraReducers],_={...x,...s.sliceCaseReducersByType};return cL(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=Uv(p,k,b)),E}function _(M=d){let E=Uv(f,A,()=>new WeakMap);return Uv(E,M,()=>{let L={};for(let[I,j]of Object.entries(n.selectors??{}))L[I]=wL(j,M,()=>Uv(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 wL(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 nt=bL();function SL(){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 AL({type:e,reducerName:t,createNotation:r},n,a){let i,o;if("reducer"in n){if(r&&!_L(n))throw new Error(_n(17));i=n.reducer,o=n.prepare}else i=n;a.addCase(e,i).exposeCaseReducer(t,i).exposeAction(t,o?rr(e,o):rr(e))}function NL(e){return e._reducerDefinitionType==="asyncThunk"}function _L(e){return e._reducerDefinitionType==="reducerWithPrepare"}function CL({type:e,reducerName:t},r,n,a){if(!a)throw new Error(_n(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||$v,pending:l||$v,rejected:s||$v,settled:u||$v})}function $v(){}var EL="task",nO="listener",aO="completed",N1="cancelled",kL=`task-${N1}`,OL=`task-${aO}`,w1=`${nO}-${N1}`,TL=`${nO}-${aO}`,Fv=class{constructor(e){this.code=e,this.message=`${EL} ${N1} (reason: ${e})`}code;name="TaskAbortError";message},_1=(e,t)=>{if(typeof e!="function")throw new TypeError(_n(32))},Hv=()=>{},iO=(e,t=Hv)=>(e.catch(t),e),oO=(e,t)=>(e.addEventListener("abort",t,{once:!0}),()=>e.removeEventListener("abort",t)),al=e=>{if(e.aborted)throw new Fv(e.reason)};function lO(e,t){let r=Hv;return new Promise((n,a)=>{let i=()=>a(new Fv(e.reason));if(e.aborted){i();return}r=oO(e,i),t.finally(()=>r()).then(n,a)}).finally(()=>{r=Hv})}var ML=async(e,t)=>{try{return await Promise.resolve(),{status:"ok",value:await e()}}catch(r){return{status:r instanceof Fv?"cancelled":"rejected",error:r}}finally{t?.()}},Vv=e=>t=>iO(lO(e,t).then(r=>(al(e),r))),sO=e=>{let t=Vv(e);return r=>t(new Promise(n=>setTimeout(n,r)))},{assign:su}=Object,Gk={},qv="listenerMiddleware",DL=(e,t)=>{let r=n=>oO(e,()=>n.abort(e.reason));return(n,a)=>{_1(n,"taskExecutor");let i=new AbortController;r(i);let o=ML(async()=>{al(e),al(i.signal);let l=await n({pause:Vv(i.signal),delay:sO(i.signal),signal:i.signal});return al(i.signal),l},()=>i.abort(OL));return a?.autoJoin&&t.push(o.catch(Hv)),{result:Vv(e)(o),cancel(){i.abort(kL)}}}},PL=(e,t)=>{let r=async(n,a)=>{al(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 lO(t,Promise.race(l));return al(t),s}finally{i()}};return((n,a)=>iO(r(n,a)))},uO=e=>{let{type:t,actionCreator:r,matcher:n,predicate:a,effect:i}=e;if(t)a=rr(t).match;else if(r)t=r.type,a=r.match;else if(n)a=n;else if(!a)throw new Error(_n(21));return _1(i,"options.listener"),{predicate:a,type:t,effect:i}},cO=su(e=>{let{type:t,predicate:r,effect:n}=uO(e);return{id:tO(),effect:n,type:t,predicate:r,pending:new Set,unsubscribe:()=>{throw new Error(_n(22))}}},{withTypes:()=>cO}),Xk=(e,t)=>{let{type:r,effect:n,predicate:a}=uO(t);return Array.from(e.values()).find(i=>(typeof r=="string"?i.type===r:i.predicate===a)&&i.effect===n)},S1=e=>{e.pending.forEach(t=>{t.abort(w1)})},jL=(e,t)=>()=>{for(let r of t.keys())S1(r);e.clear()},Zk=(e,t,r)=>{try{e(t,r)}catch(n){setTimeout(()=>{throw n},0)}},dO=su(rr(`${qv}/add`),{withTypes:()=>dO}),IL=rr(`${qv}/removeAll`),fO=su(rr(`${qv}/remove`),{withTypes:()=>fO}),zL=(...e)=>{console.error(`${qv}/error`,...e)},mi=(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=zL}=e;_1(o,"onError");let l=p=>(p.unsubscribe=()=>t.delete(p.id),t.set(p.id,p),v=>{p.unsubscribe(),v?.cancelActive&&S1(p)}),s=(p=>{let v=Xk(t,p)??cO(p);return l(v)});su(s,{withTypes:()=>s});let u=p=>{let v=Xk(t,p);return v&&(v.unsubscribe(),p.cancelActive&&S1(v)),!!v};su(u,{withTypes:()=>u});let c=async(p,v,h,b)=>{let m=new AbortController,g=PL(s,m.signal),x=[];try{p.pending.add(m),n(p),await Promise.resolve(p.effect(v,su({},h,{getOriginalState:b,condition:(A,k)=>g(A,k).then(Boolean),take:g,delay:sO(m.signal),pause:Vv(m.signal),extra:i,signal:m.signal,fork:DL(m.signal,x),unsubscribe:p.unsubscribe,subscribe:()=>{t.set(p.id,p)},cancelActiveListeners:()=>{p.pending.forEach((A,k,_)=>{A!==m&&(A.abort(w1),_.delete(A))})},cancel:()=>{m.abort(w1),p.pending.delete(m)},throwIfCancelled:()=>{al(m.signal)}})))}catch(A){A instanceof Fv||Zk(o,A,{raisedBy:"effect"})}finally{await Promise.all(x),m.abort(TL),a(p),p.pending.delete(m)}},d=jL(t,r);return{middleware:p=>v=>h=>{if(!a1(h))return v(h);if(dO.match(h))return s(h.payload);if(IL.match(h)){d();return}if(fO.match(h))return u(h.payload);let b=p.getState(),m=()=>{if(b===Gk)throw new Error(_n(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,Zk(o,M,{raisedBy:"predicate"})}_&&c(k,h,p,m)}}}finally{b=Gk}return g},startListening:s,stopListening:u,clearListeners:d}};function _n(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 LL={layoutType:"horizontal",width:0,height:0,margin:{top:5,right:5,bottom:5,left:5},scale:1},pO=nt({name:"chartLayout",initialState:LL,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}}}),Yv=pO.actions,C1=Yv.setMargin,mO=Yv.setLayout,vO=Yv.setChartSize,hO=Yv.setScale;var gO=pO.reducer;function Wv(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 nn(e){return typeof e=="number"&&e>0&&Number.isFinite(e)}function yO(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 an(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(dE(s)){if(s==="top"&&ye(e.top))return an(an({},e),{},{top:e.top+(a||0)+c});if(s==="bottom"&&ye(e.bottom))return an(an({},e),{},{bottom:e.bottom+(a||0)+c});if(s==="left"&&ye(e.left))return an(an({},e),{},{left:e.left+(n||0)+c});if(s==="right"&&ye(e.right))return an(an({},e),{},{right:e.right+(n||0)+c})}return e}if((l==="vertical"||l==="horizontal"&&o==="middle")&&i!=="center"&&ye(e[i]))return an(an({},e),{},{[i]:e[i]+(n||0)});if((l==="horizontal"||l==="vertical"&&i==="center")&&o!=="middle"&&ye(e[o]))return an(an({},e),{},{[o]:e[o]+(a||0)})}return e},on=(e,t)=>e==="horizontal"&&t==="xAxis"||e==="vertical"&&t==="yAxis"||e==="centric"&&t==="angleAxis"||e==="radial"&&t==="radiusAxis",E1=(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},k1=(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?cr(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(dr);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(dr):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(dr):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(dr)};var 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?(u[0]=i,i+=f,u[1]=i):(u[0]=o,o+=f,u[1]=o)}}}},VL=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)}}}},FL={sign:HL,expand:Gb,none:Sn,silhouette:Xb,wiggle:Zb,positive:VL},bO=(e,t,r)=>{var n,a=(n=FL[r])!==null&&n!==void 0?n:Sn,i=Kb().keys(t).value((l,s)=>Number(Ct(l,s,0))).order(Rs).offset(a),o=i(e);return o.forEach((l,s)=>{l.forEach((u,c)=>{var d=Ct(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 wO(e){return e==null?void 0:String(e)}function O1(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&&!Dt(a[t.dataKey])){var l=Lm(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=Ct(a,Dt(o)?t.dataKey:o),u=t.scale.map(s);return ye(u)?u:null}var qL=e=>{var t=e.flat(2).filter(ye);return[Math.min(...t),Math.max(...t)]},YL=e=>[e[0]===1/0?0:e[0],e[1]===-1/0?0:e[1]],SO=(e,t,r)=>{if(!(e==null||Object.keys(e).length===0))return YL(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=Wv(u,t,r),d=qL(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]))},T1=/^dataMin[\s]*-[\s]*([0-9]+([.]{1}[0-9]+){0,1})$/,M1=/^dataMax[\s]*\+[\s]*([0-9]+([.]{1}[0-9]+){0,1})$/,uu=(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=di(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 D1(e){var t=e.tooltipEntrySettings,r=e.dataKey,n=e.payload,a=e.value,i=e.name;return an(an({},t),{},{dataKey:r,payload:n,value:a,name:i})}function P1(e,t){if(e!=null)return String(e);if(typeof t=="string")return t}var AO=(e,t)=>{if(t==="horizontal")return e.relativeX;if(t==="vertical")return e.relativeY},NO=(e,t)=>t==="centric"?e.angle:e.radius;var Pr=e=>e.layout.width,jr=e=>e.layout.height,_O=e=>e.layout.scale,Kv=e=>e.layout.margin;var cu=V(e=>e.cartesianAxis.xAxis,e=>Object.values(e)),du=V(e=>e.cartesianAxis.yAxis,e=>Object.values(e));var CO="data-recharts-item-index",EO="data-recharts-item-id",il=60,kd=30;function kO(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 Gv(e){for(var t=1;te.brush.height;function ZL(e){var t=du(e);return t.reduce((r,n)=>{if(n.orientation==="left"&&!n.mirror&&!n.hide){var a=typeof n.width=="number"?n.width:il;return r+a}return r},0)}function QL(e){var t=du(e);return t.reduce((r,n)=>{if(n.orientation==="right"&&!n.mirror&&!n.hide){var a=typeof n.width=="number"?n.width:il;return r+a}return r},0)}function JL(e){var t=cu(e);return t.reduce((r,n)=>{if(n.orientation==="top"&&!n.mirror&&!n.hide){var a=typeof n.height=="number"?n.height:kd;return r+a}return r},0)}function RL(e){var t=cu(e);return t.reduce((r,n)=>{if(n.orientation==="bottom"&&!n.mirror&&!n.hide){var a=typeof n.height=="number"?n.height:kd;return r+a}return r},0)}var Et=V([Pr,jr,Kv,XL,ZL,QL,JL,RL,t1,gk],(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=Gv(Gv({},d),c),p=f.bottom;f.bottom+=n,f=xO(f,s,u);var v=e-f.left-f.right,h=t-f.top-f.bottom;return Gv(Gv({brushBottom:p},f),{},{width:Math.max(v,0),height:Math.max(h,0)})}),OO=V(Et,e=>({x:e.left,y:e.top,width:e.width,height:e.height})),Xv=V(Pr,jr,(e,t)=>({x:0,y:0,width:e,height:t}));var eB=ee(ne()),Zv=ee(ne()),tB=(0,Zv.createContext)(null),bt=()=>(0,Zv.useContext)(tB)!=null;var fu=e=>e.brush,ol=V([fu,Et,Kv],(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 ll=ee(ne()),nr=ee(ne());function TO(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 MO(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=TO(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 j1(e,t=0,r={}){let{leading:n=!0,trailing:a=!0}=r;return MO(e,t,{leading:n,maxWait:t,trailing:a})}var rB=!0,pu=function(t,r){for(var n=arguments.length,a=new Array(n>2?n-2:0),i=2;ia[o++]))}};var da={width:"100%",height:"100%",debounce:0,minWidth:0,initialDimension:{width:-1,height:-1}},I1=(e,t,r)=>{var n=r.width,a=n===void 0?da.width:n,i=r.height,o=i===void 0?da.height:i,l=r.aspect,s=r.maxHeight,u=si(a)?e:Number(a),c=si(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}},nB={width:0,height:0,overflow:"visible"},aB={width:0,overflowX:"visible"},iB={height:0,overflowY:"visible"},oB={},DO=e=>{var t=e.width,r=e.height,n=si(t),a=si(r);return n&&a?nB:n?aB:a?iB:oB};function PO(e){var t=e.width,r=e.height,n=e.aspect,a=t,i=r;return a===void 0&&i===void 0?(a=da.width,i=da.height):a===void 0?a=n&&n>0?void 0:da.width:i===void 0&&(i=n&&n>0?void 0:da.height),{width:a,height:i}}var lB=["aspect","initialDimension","width","height","minWidth","minHeight","maxHeight","children","debounce","id","className","onResize","style"];function Qv(){return Qv=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 yB(a)?ll.createElement(LO.Provider,{value:a},t):null}var Od=()=>(0,nr.useContext)(LO),xB=(0,nr.forwardRef)((e,t)=>{var r=e.aspect,n=e.initialDimension,a=n===void 0?da.initialDimension:n,i=e.width,o=e.height,l=e.minWidth,s=l===void 0?da.minWidth:l,u=e.minHeight,c=e.maxHeight,d=e.children,f=e.debounce,p=f===void 0?da.debounce:f,v=e.id,h=e.className,b=e.onResize,m=e.style,g=m===void 0?{}:m,x=hB(e,lB),A=(0,nr.useRef)(null),k=(0,nr.useRef)();k.current=b,(0,nr.useImperativeHandle)(t,()=>A.current);var _=(0,nr.useState)({containerWidth:a.width,containerHeight:a.height}),M=dB(_,2),E=M[0],L=M[1],I=(0,nr.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,nr.useEffect)(()=>{if(A.current==null||typeof ResizeObserver>"u")return ua;var y=T=>{var H,Y=T[0];if(Y!=null){var J=Y.contentRect,fe=J.width,pe=J.height;I(fe,pe),(H=k.current)===null||H===void 0||H.call(k,fe,pe)}};p>0&&(y=j1(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;pu(!r||r>0,"The aspect(%s) must be greater than zero.",r);var X=I1(j,z,{width:i,height:o,aspect:r,maxHeight:c}),R=X.calculatedWidth,Q=X.calculatedHeight;return pu(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),ll.createElement("div",Qv({id:v?"".concat(v):void 0,className:Le("recharts-responsive-container",h),style:IO(IO({},g),{},{width:i,height:o,minWidth:s,minHeight:u,maxHeight:c}),ref:A},x),ll.createElement("div",{style:DO({width:i,height:o})},ll.createElement(BO,{width:R,height:Q},d)))}),mu=(0,nr.forwardRef)((e,t)=>{var r=Od();if(nn(r.width)&&nn(r.height))return e.children;var n=PO({width:e.width,height:e.height,aspect:e.aspect}),a=n.width,i=n.height,o=I1(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)?ll.createElement(BO,{width:l,height:s},e.children):ll.createElement(xB,Qv({},e,{width:a,height:i,ref:t}))});var sl=()=>{var e,t=bt(),r=Se(OO),n=Se(ol),a=(e=Se(fu))===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}},bB={top:0,bottom:0,left:0,right:0,width:0,height:0,brushBottom:0},Jv=()=>{var e;return(e=Se(Et))!==null&&e!==void 0?e:bB},Rv=()=>Se(Pr),eh=()=>Se(jr);var lt=e=>e.layout.layoutType,ka=()=>Se(lt),vu=()=>{var e=ka();if(e==="horizontal"||e==="vertical")return e},z1=e=>{var t=e.layout.layoutType;if(t==="centric"||t==="radial")return t};var $O=()=>{var e=ka();return e!==void 0},ul=e=>{var t=Ve(),r=bt(),n=e.width,a=e.height,i=Od(),o=n,l=a;return i&&(o=i.width>0?i.width:n,l=i.height>0?i.height:a),(0,UO.useEffect)(()=>{!r&&nn(o)&&nn(l)&&t(vO({width:o,height:l}))},[t,r,o,l]),null};var wB={settings:{layout:"horizontal",align:"center",verticalAlign:"bottom",itemSorter:"value",position:void 0,offset:0},size:{width:0,height:0},payload:[]},HO=nt({name:"legend",initialState:wB,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:st()},replaceLegendPayload:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=Er(e).payload.indexOf($e(n));i>-1&&(e.payload[i]=$e(a))},prepare:st()},removeLegendPayload:{reducer(e,t){var r=Er(e).payload.indexOf($e(t.payload));r>-1&&e.payload.splice(r,1)},prepare:st()}}}),Td=HO.actions,tne=Td.setLegendSize,rne=Td.setLegendSettings,VO=Td.addLegendPayload,FO=Td.replaceLegendPayload,qO=Td.removeLegendPayload;var YO=HO.reducer;var Ht=ee(ne(),1),XB=ee(XO(),1);var OB=Symbol.for("react.forward_ref");var TB=Symbol.for("react.memo");var MB=OB,DB=TB;function PB(e){e()}function jB(){let e=null,t=null;return{clear(){e=null,t=null},notify(){PB(()=>{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 ZO={notify(){},get:()=>[]};function IB(e,t){let r,n=ZO,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=jB())}function d(){a--,r&&a===0&&(r(),r=void 0,n.clear(),n=ZO)}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 zB=()=>typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",LB=zB(),BB=()=>typeof navigator<"u"&&navigator.product==="ReactNative",UB=BB(),$B=()=>LB||UB?Ht.useLayoutEffect:Ht.useEffect,HB=$B();function QO(e,t){return e===t?e!==0||t!==0||1/e===1/t:e!==e&&t!==t}function JO(e,t){if(QO(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=IB(a);return{store:a,subscription:s,getServerState:n?()=>n:void 0}},[a,n]),o=Ht.useMemo(()=>a.getState(),[a]);return HB(()=>{let{subscription:s}=i;return s.onStateChange=s.notifyNestedSubs,s.trySubscribe(),o!==a.getState()&&s.notifyNestedSubs(),()=>{s.tryUnsubscribe(),s.onStateChange=void 0}},[i,o]),Ht.createElement((r||KB).Provider,{value:i},t)}var RO=GB;var ZB=new Set(["axisLine","tickLine","activeBar","activeDot","activeLabel","activeShape","allowEscapeViewBox","background","cursor","dot","label","line","margin","padding","position","shape","style","tick","wrapperStyle","radius","throttledEvents"]);function QB(e,t){return e==null&&t==null?!0:typeof e=="number"&&typeof t=="number"?e===t||e!==e&&t!==t:e===t}function mo(e,t){var r=new Set([...Object.keys(e),...Object.keys(t)]);for(var n of r)if(ZB.has(n)){if(e[n]==null&&t[n]==null)continue;if(!JO(e[n],t[n]))return!1}else if(!QB(e[n],t[n]))return!1;return!0}var Qn=ee(ne()),z8=ee(ne()),L8=ee(ip());var fa=ee(ne());function L1(){return L1=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?hu.separator:t,n=e.contentStyle,a=e.itemStyle,i=e.labelStyle,o=i===void 0?hu.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?hu.accessibilityLayer:v,b=()=>{if(l&&l.length){var E={padding:0,margin:0},L=lU(l,u),I=L.map((j,z)=>{if(!j||j.type==="none")return null;var X=j.formatter||s||oU,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=tU(O,2);y=D[0],C=D[1]}else if(O!=null)y=O;else return null}var N=Dd(Dd({},hu.itemStyle),{},{color:j.color||hu.itemStyle.color},a);return fa.createElement("li",{className:"recharts-tooltip-item",key:"tooltip-item-".concat(z),style:N},qr(C)?fa.createElement("span",{className:"recharts-tooltip-item-name"},C):null,qr(C)?fa.createElement("span",{className:"recharts-tooltip-item-separator"},r):null,fa.createElement("span",{className:"recharts-tooltip-item-value"},y),fa.createElement("span",{className:"recharts-tooltip-item-unit"},j.unit||""))});return fa.createElement("ul",{className:"recharts-tooltip-item-list",style:E},I)}return null},m=Dd(Dd({},hu.contentStyle),n),g=Dd({margin:0},o),x=!Dt(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 fa.createElement("div",L1({className:k,style:m},M),fa.createElement("p",{className:_,style:g},fa.isValidElement(A)?A:"".concat(A)),b())};var vo=ee(ne());var Pd="recharts-tooltip-wrapper",sU={visibility:"hidden"};function uU(e){var t=e.coordinate,r=e.translateX,n=e.translateY;return Le(Pd,{["".concat(Pd,"-right")]:ye(r)&&t&&ye(t.x)&&r>=t.x,["".concat(Pd,"-left")]:ye(r)&&t&&ye(t.x)&&r=t.y,["".concat(Pd,"-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 cU(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 aT(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=nT({allowEscapeViewBox:t,coordinate:r,key:"x",offset:a,position:i,reverseDirection:o,tooltipDimension:l.width,viewBox:u,viewBoxDimension:u.width}),f=nT({allowEscapeViewBox:t,coordinate:r,key:"y",offset:n,position:i,reverseDirection:o,tooltipDimension:l.height,viewBox:u,viewBoxDimension:u.height}),c=cU({translateX:d,translateY:f,useTranslate3d:s})):c=sU,{cssProperties:c,cssClasses:uU({translateX:d,translateY:f,coordinate:r})}}var th=ee(ne());var dU=()=>!(typeof window<"u"&&window.document&&window.document.createElement&&window.setTimeout),Wn={devToolsEnabled:!0,isSsr:dU()};function fU(e,t){return hU(e)||vU(e,t)||mU(e,t)||pU()}function pU(){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 mU(e,t){if(e){if(typeof e=="string")return iT(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)?iT(e,t):void 0}}function iT(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);rWn.isSsr||!window.matchMedia?!1:window.matchMedia("(prefers-reduced-motion: reduce)").matches),t=fU(e,2),r=t[0],n=t[1];return(0,th.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 oT(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 gu(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=bU(s,2),c=u[0],d=u[1];vo.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(gu(gu({},c),{},{dismissed:!1}));var f=aT({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?{}:gu(gu({transition:_U({prefersReducedMotion:l,isAnimationActive:e.isAnimationActive,active:e.active,animationDuration:e.animationDuration,animationEasing:e.animationEasing})},v),{},{pointerEvents:"none",position:"absolute",top:0,left:0}),b=gu(gu({},h),{},{visibility:!c.dismissed&&e.active&&e.hasPayload?"visible":"hidden"},e.wrapperStyle);return vo.createElement("div",{xmlns:"http://www.w3.org/1999/xhtml",tabIndex:-1,className:p,style:b,ref:e.innerRef},e.children)}var sT=vo.memo(CU);var nh=()=>{var e;return(e=Se(t=>t.rootProps.accessibilityLayer))!==null&&e!==void 0?e:!0};var j0=ee(ne()),Wu=ee(ne());var mT=ee(ne());function B1(){return B1=Object.assign?Object.assign.bind():function(e){for(var t=1;tIe(e.x)&&Ie(e.y),fT=e=>e.base!=null&&ah(e.base)&&ah(e),jd=e=>e.x,Id=e=>e.y,TU=(e,t)=>{if(typeof e=="function")return e;var r="curve".concat(Gs(e));if((r==="curveMonotone"||r==="curveBump")&&t){var n=dT["".concat(r).concat(t==="vertical"?"Y":"X")];if(n)return n}return dT[r]||co},pT={connectNulls:!1,type:"linear"},MU=e=>{var t=e.type,r=t===void 0?pT.type:t,n=e.points,a=n===void 0?[]:n,i=e.baseLine,o=e.layout,l=e.connectNulls,s=l===void 0?pT.connectNulls:l,u=TU(r,o),c=s?a.filter(ah):a;if(Array.isArray(i)){var d,f=a.map((m,g)=>cT(cT({},m),{},{base:i[g]}));o==="vertical"?d=Zs().y(Id).x1(jd).x0(m=>m.base.x):d=Zs().x(jd).y1(Id).y0(m=>m.base.y);var p=d.defined(fT).curve(u),v=s?f.filter(fT):f;return p(v)}var h;o==="vertical"&&ye(i)?h=Zs().y(Id).x1(jd).x0(i):ye(i)?h=Zs().x(jd).y1(Id).y0(i):h=vd().x(jd).y(Id);var b=h.defined(ah).curve(u);return b(c)},yu=e=>{var t=e.className,r=e.points,n=e.path,a=e.pathRef,i=ka();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?MU(o):n;return mT.createElement("path",B1({},gr(e),eu(e),{className:Le("recharts-curve",t),d:l===null?void 0:l,ref:a}))};var hT=ee(ne());var DU=["x","y","top","left","width","height","className"];function U1(){return U1=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),gT=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=LU(e,DU),h=PU({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:hT.createElement("path",U1({},Kt(h),{className:Le("recharts-cross",p),d:UU(r,a,c,f,o,s)}))};function yT(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 ch=ee(ne()),Kn=ee(ne());var zd=ee(ne());var ih=1e-4,wT=(e,t)=>[0,3*e,3*t-6*e,3*e-3*t+1],ST=(e,t)=>e.map((r,n)=>r*t**n).reduce((r,n)=>r+n),xT=(e,t)=>r=>{var n=wT(e,t);return ST(n,r)},$U=(e,t)=>r=>{var n=wT(e,t),a=[...n.map((i,o)=>i*o).slice(1),0];return ST(a,r)},HU=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]]},VU=function(){for(var t=arguments.length,r=new Array(t),n=0;n{var a=xT(e,r),i=xT(t,n),o=$U(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}},AT=e=>{if(typeof e=="string")switch(e){case"ease":case"ease-in-out":case"ease-out":case"ease-in":case"linear":return bT(e);case"spring":return qU();default:if(e.split("(")[0]==="cubic-bezier")return bT(e)}return typeof e=="function"?e:null};var xu=ee(ne());var NT=(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 _T=(0,xu.createContext)(NT),Vne=_T.Provider;function CT(e){var t=(0,xu.useContext)(_T);return(0,xu.useMemo)(()=>e??t,[e,t])}function YU(e,t,r){return(t=WU(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function WU(e){var t=KU(e,"string");return typeof t=="symbol"?t:t+""}function KU(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 ET="init",kT="pending",OT="active",GU="completed";function $1(e){return Math.max(0,e)}var H1=class{getAnimationStartedTime(){return this.animationStartedTime}getBeginStartedTime(){return this.beginStartedTime}constructor(t){var r;YU(this,"state",ET),this.animationId=t.animationId,this.onAnimationEnd=t.onAnimationEnd,this.animationDuration=$1(t.animationDuration),this.animationBegin=$1(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()===ET)return this.state=kT,this.beginStartedTime=t,this.animationBegin;if(this.getState()===kT){if(this.beginStartedTime==null)throw new Error;var r=t-this.beginStartedTime;return r>=this.animationBegin?(this.state=OT,this.animationStartedTime=t,this.nextAnimationUpdate(0)):$1(this.animationBegin-r)}if(this.getState()===OT){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=GU}getFrom(){return this.from}getTo(){return this.to}getAnimationId(){return this.animationId}getAnimationBegin(){return this.animationBegin}},oh=class extends H1{nextAnimationUpdate(){return 0}getInterpolated(){return this.easing(qn(this.getFrom(),this.getTo(),this.getProgress()))}};var lh=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 XU(e,t){return RU(e)||JU(e,t)||QU(e,t)||ZU()}function ZU(){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 QU(e,t){if(e){if(typeof e=="string")return TT(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)?TT(e,t):void 0}}function TT(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{},onAnimationStart:()=>{}},MT=0,V1=1;function sh(e){var t=Pt(e,e$),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=rh(),f=n==="auto"?!Wn.isSsr&&!d:n,p=CT(t.animationController),v=(0,zd.useState)(f?MT:V1),h=XU(v,2),b=h[0],m=h[1];return(0,zd.useEffect)(()=>{f||m(V1)},[f]),(0,zd.useEffect)(()=>{var g=AT(o);if(!f||!a||g==null)return ua;var x=new lh,A=new oh({animationId:r,easing:g,animationDuration:i,animationBegin:l,onAnimationStart:u,onAnimationEnd:s,from:MT,to:V1});return p(x,A,m)},[p,r,f,a,i,o,l,u,s]),c(Number(b))}var F1=ee(ne());function uh(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:"animation-",r=(0,F1.useRef)(ui(t)),n=(0,F1.useRef)(e);return n.current!==e&&(r.current=ui(t),n.current=e),r.current}var t$=e=>e.replace(/([A-Z])/g,t=>"-".concat(t.toLowerCase())),DT=(e,t,r)=>e.map(n=>"".concat(t$(n)," ").concat(t,"ms ").concat(r)).join(",");var r$=["radius"],n$=["radius"],PT,jT,IT,zT,LT,BT,UT,$T,HT,VT;function FT(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 qT(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r{var i=_a(r),o=_a(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=ur(PT||(PT=Oa(["M",",",""])),e,t+s*f[0]),f[0]>0&&(d+=ur(jT||(jT=Oa(["A ",",",",0,0,",",",",",""])),f[0],f[0],c,e+u*f[0],t)),d+=ur(IT||(IT=Oa(["L ",",",""])),e+r-u*f[1],t),f[1]>0&&(d+=ur(zT||(zT=Oa(["A ",",",",0,0,",`, + `,",",""])),f[1],f[1],c,e+r,t+s*f[1])),d+=ur(LT||(LT=Oa(["L ",",",""])),e+r,t+n-s*f[2]),f[2]>0&&(d+=ur(BT||(BT=Oa(["A ",",",",0,0,",`, + `,",",""])),f[2],f[2],c,e+r-u*f[2],t+n)),d+=ur(UT||(UT=Oa(["L ",",",""])),e+u*f[3],t+n),f[3]>0&&(d+=ur($T||($T=Oa(["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=ur(HT||(HT=Oa(["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=ur(VT||(VT=Oa(["M ",","," h "," v "," h "," Z"])),e,t,r,n,-r);return d},GT={x:0,y:0,width:0,height:0,radius:0,isAnimationActive:!1,isUpdateAnimationActive:!1,animationBegin:0,animationDuration:1500,animationEasing:"ease"},XT=e=>{var t=Pt(e,GT),r=(0,Kn.useRef)(null),n=(0,Kn.useState)(-1),a=s$(n,2),i=a[0],o=a[1];(0,Kn.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,Kn.useRef)(u),x=(0,Kn.useRef)(c),A=(0,Kn.useRef)(l),k=(0,Kn.useRef)(s),_=(0,Kn.useMemo)(()=>({x:l,y:s,width:u,height:c,radius:d}),[l,s,u,c,d]),M=uh(_,"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=Kt(t),I=L.radius,j=YT(L,r$);return ch.createElement("path",dh({},j,{x:_a(l),y:_a(s),width:_a(u),height:_a(c),radius:typeof d=="number"?d:void 0,className:E,d:KT(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=DT(["strokeDasharray"],v,typeof p=="string"?p:GT.animationEasing);return ch.createElement(sh,{animationId:M,key:M,canBegin:i>0,duration:v,easing:p,isActive:m,begin:h},D=>{var N=qn(z,u,D),T=qn(X,c,D),H=qn(R,l,D),Y=qn(Q,s,D);r.current&&(g.current=N,x.current=T,A.current=H,k.current=Y);var J;b?D>0?J={transition:O,strokeDasharray:C}:J={strokeDasharray:y}:J={strokeDasharray:C};var fe=Kt(t),pe=fe.radius,ge=YT(fe,n$);return ch.createElement("path",dh({},ge,{radius:typeof d=="number"?d:void 0,className:E,d:KT(H,Y,N,T,d),ref:r,style:qT(qT({},J),t.style)}))})};function ZT(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 QT(e){for(var t=1;te*180/Math.PI,Vt=(e,t,r,n)=>({x:e+Math.cos(-Ld*n)*r,y:t+Math.sin(-Ld*n)*r}),JT=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},g$=(e,t)=>{var r=e.x,n=e.y,a=t.x,i=t.y;return Math.sqrt((r-a)**2+(n-i)**2)},y$=(e,t)=>{var r=e.x,n=e.y,a=t.cx,i=t.cy,o=g$({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:h$(s),angleInRadian:s}},x$=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}},b$=(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},RT=(e,t)=>{var r=e.relativeX,n=e.relativeY,a=y$({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=x$(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?QT(QT({},t),{},{radius:i,angle:b$(f,t)}):null};function fh(e){var t=e.cx,r=e.cy,n=e.radius,a=e.startAngle,i=e.endAngle,o=Vt(t,r,n,a),l=Vt(t,r,n,i);return{points:[o,l],cx:t,cy:r,radius:n,startAngle:a,endAngle:i}}var l3=ee(ne());var e3,t3,r3,n3,a3,i3,o3;function q1(){return q1=Object.assign?Object.assign.bind():function(e){for(var t=1;t{var r=cr(t-e),n=Math.min(Math.abs(t-e),359.999);return r*n},ph=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)/Ld,d=s?a:a+i*c,f=Vt(t,r,u,d),p=Vt(t,r,n,d),v=s?a-i*c:a,h=Vt(t,r,u*Math.cos(c*Ld),v);return{center:f,circleTangency:p,lineTangency:h,theta:c}},s3=e=>{var t=e.cx,r=e.cy,n=e.innerRadius,a=e.outerRadius,i=e.startAngle,o=e.endAngle,l=w$(i,o),s=i+l,u=Vt(t,r,a,i),c=Vt(t,r,a,s),d=ur(e3||(e3=cl(["M ",",",` + A `,",",`,0, + `,",",`, + `,",",` + `])),u.x,u.y,a,a,+(Math.abs(l)>180),+(i>s),c.x,c.y);if(n>0){var f=Vt(t,r,n,i),p=Vt(t,r,n,s);d+=ur(t3||(t3=cl(["L ",",",` + A `,",",`,0, + `,",",`, + `,","," Z"])),p.x,p.y,n,n,+(Math.abs(l)>180),+(i<=s),f.x,f.y)}else d+=ur(r3||(r3=cl(["L ",","," Z"])),t,r);return d},S$=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=cr(u-s),d=ph({cx:t,cy:r,radius:a,angle:s,sign:c,cornerRadius:i,cornerIsExternal:l}),f=d.circleTangency,p=d.lineTangency,v=d.theta,h=ph({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?ur(n3||(n3=cl(["M ",",",` + a`,",",",0,0,1,",`,0 + a`,",",",0,0,1,",`,0 + `])),p.x,p.y,i,i,i*2,i,i,-i*2):s3({cx:t,cy:r,innerRadius:n,outerRadius:a,startAngle:s,endAngle:u});var A=ur(a3||(a3=cl(["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=ph({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=ph({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+=ur(i3||(i3=cl(["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+=ur(o3||(o3=cl(["L",",","Z"])),t,r);return A},A$={cx:0,cy:0,innerRadius:0,outerRadius:0,startAngle:0,endAngle:0,cornerRadius:0,forceCornerRadius:!1,cornerIsExternal:!1},u3=e=>{var t=Pt(e,A$),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=S$({cx:r,cy:n,innerRadius:a,outerRadius:i,cornerRadius:Math.min(v,p/2),forceCornerRadius:l,cornerIsExternal:s,startAngle:u,endAngle:c}):h=s3({cx:r,cy:n,innerRadius:a,outerRadius:i,startAngle:u,endAngle:c}),l3.createElement("path",q1({},Kt(t),{className:f,d:h}))};function c3(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(qm(t)){if(e==="centric"){var n=t.cx,a=t.cy,i=t.innerRadius,o=t.outerRadius,l=t.angle,s=Vt(n,a,i,l),u=Vt(n,a,o,l);return[{x:s.x,y:s.y},{x:u.x,y:u.y}]}return fh(t)}}function d3(e){return yv(e)?NaN:Number(e)}function mh(e){return e?(e=d3(e),e===1/0||e===-1/0?(e<0?-1:1)*Number.MAX_VALUE:e===e?e:0):e===0?e:0}function vh(e,t,r){r&&typeof r!="number"&&wd(e,t,r)&&(t=r=void 0),e=mh(e),t===void 0?(t=e,e=0):t=mh(t),r=r===void 0?ee.chartData,Y1=V([ln],e=>{var t=e.chartData!=null?e.chartData.length-1:0;return{chartData:e.chartData,computedData:e.computedData,dataEndIndex:t,dataStartIndex:0}}),Bd=(e,t,r,n)=>n?Y1(e):ln(e),f3=(e,t,r)=>r?Y1(e):ln(e),p3=V([Bd],e=>{var t=e.chartData,r=e.dataStartIndex,n=e.dataEndIndex;return t!=null?t.slice(r,n+1):[]}),Bae=V([Y1],e=>{var t=e.chartData,r=e.dataStartIndex,n=e.dataEndIndex;return t!=null?t.slice(r,n+1):[]}),m3=V([ln],e=>{var t=e.chartData,r=e.dataStartIndex,n=e.dataEndIndex;return t!=null?t.slice(r,n+1):[]});function W1(e,t){return E$(e)||C$(e,t)||_$(e,t)||N$()}function N$(){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 _$(e,t){if(e){if(typeof e=="string")return v3(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)?v3(e,t):void 0}}function v3(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=Ud(e,2),r=t[0],n=t[1],a=r,i=n;return r>n&&(a=n,i=r),[a,i]},Q1=(e,t,r)=>{if(e.lte(0))return new Re.default(0);var n=X1(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()))},w3=(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()))},D$=(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(X1(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]:Q1;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?S3(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 yh=function(t){var r=Ud(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=b3([n,a]),c=Ud(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 D$(d,i,o);var v=l==="snap125"?w3:Q1,h=S3(d,f,s,o,0,v),b=h.step,m=h.tickMin,g=h.tickMax,x=Z1(m,g.add(new Re.default(.1).mul(b)),b);return n>a?x.reverse():x},xh=function(t,r){var n=Ud(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=b3([a,i]),u=Ud(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"?w3:Q1,p=Math.max(r,2),v=f(new Re.default(d).sub(c).div(p-1),o,0),h=[...Z1(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 A3=e=>e.rootProps.barCategoryGap;var bu=e=>e.rootProps.stackOffset,bh=e=>e.rootProps.reverseStackOrder,wu=e=>e.options.chartName,wh=e=>e.rootProps.syncId,J1=e=>e.rootProps.syncMethod,Sh=e=>e.options.eventEmitter,N3=e=>e.rootProps.baseValue;var dt={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 ho={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:dt.axis};var pa={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:dt.axis};var dl=(e,t)=>{if(!(!e||!t))return e!=null&&e.reversed?[t[1],t[0]]:t};function fl(e,t,r){if(r!=="auto")return r;if(e!=null)return on(e,t)?"category":"number"}function _3(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 Ah(e){for(var t=1;t{if(t!=null)return e.polarAxis.angleAxis[t]},Nh=V([z$,z1],(e,t)=>{var r;if(e!=null)return e;var n=(r=fl(t,"angleAxis",C3.type))!==null&&r!==void 0?r:"category";return Ah(Ah({},C3),{},{type:n})}),L$=(e,t)=>e.polarAxis.radiusAxis[t],_h=V([L$,z1],(e,t)=>{var r;if(e!=null)return e;var n=(r=fl(t,"radiusAxis",E3.type))!==null&&r!==void 0?r:"category";return Ah(Ah({},E3),{},{type:n})}),Ch=e=>e.polarOptions,R1=V([Pr,jr,Et],JT),k3=V([Ch,R1],(e,t)=>{if(e!=null)return Fn(e.innerRadius,t,0)}),O3=V([Ch,R1],(e,t)=>{if(e!=null)return Fn(e.outerRadius,t,t*.8)}),B$=e=>{if(e==null)return[0,0];var t=e.startAngle,r=e.endAngle;return[t,r]},e2=V([Ch],B$),mie=V([Nh,e2],dl),t2=V([R1,k3,O3],(e,t,r)=>{if(!(e==null||t==null||r==null))return[t,r]}),vie=V([_h,t2],dl),Eh=V([lt,Ch,k3,O3,Pr,jr],(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:Fn(o,a,a/2),cy:Fn(l,i,i/2),innerRadius:r,outerRadius:n,startAngle:s,endAngle:u,clockWise:!1}}});var fr=(e,t)=>t;var $d=(e,t,r)=>r;function Su(e){return e?.id}function kh(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=Su(s);c.forEach((f,p)=>{var v=o==null||i?p:String(Ct(f,o,null)),h=Ct(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 Hd(e){return"stackId"in e&&e.stackId!=null&&e.dataKey!=null}var pl=(e,t)=>e===t?!0:e==null||t==null?!1:e[0]===t[0]&&e[1]===t[1];function Au(e,t){return Array.isArray(e)&&Array.isArray(t)&&e.length===0&&t.length===0?!0:e===t}function T3(e,t){if(e.length===t.length){for(var r=0;r{var t=lt(e);return t==="horizontal"?"xAxis":t==="vertical"?"yAxis":t==="centric"?"angleAxis":"radiusAxis"};var go=e=>e.tooltip.settings.axisId;function Vd(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 M3=(e,t)=>{if(t!=null)switch(e){case"linear":{if(!Cn(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 cf={};Ej(cf,{scaleBand:()=>Wd,scaleDiverging:()=>u0,scaleDivergingLog:()=>j2,scaleDivergingPow:()=>c0,scaleDivergingSqrt:()=>R4,scaleDivergingSymlog:()=>I2,scaleIdentity:()=>Kh,scaleImplicit:()=>Ih,scaleLinear:()=>Wh,scaleLog:()=>Gh,scaleOrdinal:()=>Cu,scalePoint:()=>B3,scalePow:()=>nf,scaleQuantile:()=>Qh,scaleQuantize:()=>Jh,scaleRadial:()=>Zh,scaleSequential:()=>i0,scaleSequentialLog:()=>D2,scaleSequentialPow:()=>o0,scaleSequentialQuantile:()=>l0,scaleSequentialSqrt:()=>J4,scaleSequentialSymlog:()=>P2,scaleSqrt:()=>b4,scaleSymlog:()=>Xh,scaleThreshold:()=>Rh,scaleTime:()=>T2,scaleUtc:()=>M2,tickFormat:()=>Jd});function Yr(e,t){return e==null||t==null?NaN:et?1:e>=t?0:NaN}function r2(e,t){return e==null||t==null?NaN:te?1:t>=e?0:NaN}function ml(e){let t,r,n;e.length!==2?(t=Yr,r=(l,s)=>Yr(e(l),s),n=(l,s)=>e(l)-s):(t=e===Yr||e===r2?e:U$,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 U$(){return 0}function Fd(e){return e===null?NaN:+e}function*D3(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 P3=ml(Yr),j3=P3.right,$$=P3.left,H$=ml(Fd).center,ma=j3;var Nu=class extends Map{constructor(t,r=q$){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(I3(this,t))}has(t){return super.has(I3(this,t))}set(t,r){return super.set(V$(this,t),r)}delete(t){return super.delete(F$(this,t))}};function I3({_intern:e,_key:t},r){let n=t(r);return e.has(n)?e.get(n):r}function V$({_intern:e,_key:t},r){let n=t(r);return e.has(n)?e.get(n):(e.set(n,r),r)}function F$({_intern:e,_key:t},r){let n=t(r);return e.has(n)&&(r=e.get(n),e.delete(n)),r}function q$(e){return e!==null&&typeof e=="object"?e.valueOf():e}function z3(e=Yr){if(e===Yr)return n2;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 n2(e,t){return(e==null||!(e>=e))-(t==null||!(t>=t))||(et?1:0)}var Y$=Math.sqrt(50),W$=Math.sqrt(10),K$=Math.sqrt(2);function Oh(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>=Y$?10:i>=W$?5:i>=K$?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 Mh(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 Dh(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?n2:z3(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));Dh(e,t,p,v,a)}let i=e[t],o=r,l=n;for(Yd(e,r,t),a(e[n],i)>0&&Yd(e,r,n);o0;)--l}a(e[r],i)===0?Yd(e,r,l):(++l,Yd(e,l,n)),l<=t&&(r=l+1),t<=l&&(n=l-1)}return e}function Yd(e,t,r){let n=e[t];e[t]=e[r],e[r]=n}function Ph(e,t,r){if(e=Float64Array.from(D3(e,r)),!(!(n=e.length)||isNaN(t=+t))){if(t<=0||n<2)return Mh(e);if(t>=1)return Th(e);var n,a=(n-1)*t,i=Math.floor(a),o=Th(Dh(e,i).subarray(0,i+1)),l=Mh(e.subarray(i+1));return o+(l-o)*(a-i)}}function a2(e,t,r=Fd){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 jh(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?Lh(t>>24&255,t>>16&255,t>>8&255,(t&255)/255):r===4?Lh(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=X$.exec(e))?new sn(t[1],t[2],t[3],1):(t=Z$.exec(e))?new sn(t[1]*255/100,t[2]*255/100,t[3]*255/100,1):(t=Q$.exec(e))?Lh(t[1],t[2],t[3],t[4]):(t=J$.exec(e))?Lh(t[1]*255/100,t[2]*255/100,t[3]*255/100,t[4]):(t=R$.exec(e))?Y3(t[1],t[2]/100,t[3]/100,1):(t=eH.exec(e))?Y3(t[1],t[2]/100,t[3]/100,t[4]):U3.hasOwnProperty(e)?V3(U3[e]):e==="transparent"?new sn(NaN,NaN,NaN,0):null}function V3(e){return new sn(e>>16&255,e>>8&255,e&255,1)}function Lh(e,t,r,n){return n<=0&&(e=t=r=NaN),new sn(e,t,r,n)}function nH(e){return e instanceof Xd||(e=yo(e)),e?(e=e.rgb(),new sn(e.r,e.g,e.b,e.opacity)):new sn}function ku(e,t,r,n){return arguments.length===1?nH(e):new sn(e,t,r,n??1)}function sn(e,t,r,n){this.r=+e,this.g=+t,this.b=+r,this.opacity=+n}zh(sn,ku,i2(Xd,{brighter(e){return e=e==null?Uh:Math.pow(Uh,e),new sn(this.r*e,this.g*e,this.b*e,this.opacity)},darker(e){return e=e==null?Kd:Math.pow(Kd,e),new sn(this.r*e,this.g*e,this.b*e,this.opacity)},rgb(){return this},clamp(){return new sn(gl(this.r),gl(this.g),gl(this.b),$h(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:F3,formatHex:F3,formatHex8:aH,formatRgb:q3,toString:q3}));function F3(){return`#${hl(this.r)}${hl(this.g)}${hl(this.b)}`}function aH(){return`#${hl(this.r)}${hl(this.g)}${hl(this.b)}${hl((isNaN(this.opacity)?1:this.opacity)*255)}`}function q3(){let e=$h(this.opacity);return`${e===1?"rgb(":"rgba("}${gl(this.r)}, ${gl(this.g)}, ${gl(this.b)}${e===1?")":`, ${e})`}`}function $h(e){return isNaN(e)?1:Math.max(0,Math.min(1,e))}function gl(e){return Math.max(0,Math.min(255,Math.round(e)||0))}function hl(e){return e=gl(e),(e<16?"0":"")+e.toString(16)}function Y3(e,t,r,n){return n<=0?e=t=r=NaN:r<=0||r>=1?e=t=NaN:t<=0&&(e=NaN),new va(e,t,r,n)}function K3(e){if(e instanceof va)return new va(e.h,e.s,e.l,e.opacity);if(e instanceof Xd||(e=yo(e)),!e)return new va;if(e instanceof va)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 va(o,l,s,e.opacity)}function G3(e,t,r,n){return arguments.length===1?K3(e):new va(e,t,r,n??1)}function va(e,t,r,n){this.h=+e,this.s=+t,this.l=+r,this.opacity=+n}zh(va,G3,i2(Xd,{brighter(e){return e=e==null?Uh:Math.pow(Uh,e),new va(this.h,this.s,this.l*e,this.opacity)},darker(e){return e=e==null?Kd:Math.pow(Kd,e),new va(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 sn(o2(e>=240?e-240:e+120,a,n),o2(e,a,n),o2(e<120?e+240:e-120,a,n),this.opacity)},clamp(){return new va(W3(this.h),Bh(this.s),Bh(this.l),$h(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=$h(this.opacity);return`${e===1?"hsl(":"hsla("}${W3(this.h)}, ${Bh(this.s)*100}%, ${Bh(this.l)*100}%${e===1?")":`, ${e})`}`}}));function W3(e){return e=(e||0)%360,e<0?e+360:e}function Bh(e){return Math.max(0,Math.min(1,e||0))}function o2(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 l2(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 X3(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 iH(e,t){return function(r){return e+r*t}}function oH(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 Q3(e){return(e=+e)==1?Hh:function(t,r){return r-t?oH(t,r,e):Zd(isNaN(t)?r:t)}}function Hh(e,t){var r=t-e;return r?iH(e,r):Zd(isNaN(e)?t:e)}var s2=(function e(t){var r=Q3(t);function n(a,i){var o=r((a=ku(a)).r,(i=ku(i)).r),l=r(a.g,i.g),s=r(a.b,i.b),u=Hh(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 J3(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:xo(n,a)})),r=u2.lastIndex;return rt&&(r=e,e=t,t=r),function(n){return Math.max(e,Math.min(t,n))}}function cH(e,t,r){var n=e[0],a=e[1],i=t[0],o=t[1];return a2?dH:cH,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),xo)))(f)))},d.domain=function(f){return arguments.length?(e=Array.from(f,bo),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=yl,c()},d.clamp=function(f){return arguments.length?(o=f?!0:pr,c()):o!==pr},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 bl(){return xl()(pr,pr)}function o4(e){return Math.abs(e=Math.round(e))>=1e21?e.toLocaleString("en").replace(/,/g,""):e.toString(10)}function wl(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 Da(e){return e=wl(Math.abs(e)),e?e[1]:NaN}function l4(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 s4(e){return function(t){return t.replace(/[0-9]/g,function(r){return e[+r]})}}var fH=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Pa(e){if(!(t=fH.exec(e)))throw new Error("invalid format: "+e);var t;return new Fh({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]})}Pa.prototype=Fh.prototype;function Fh(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+""}Fh.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 u4(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 Qd;function c4(e,t){var r=wl(e,t);if(!r)return Qd=void 0,e.toPrecision(t);var n=r[0],a=r[1],i=a-(Qd=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")+wl(e,Math.max(0,t+i-1))[0]}function p2(e,t){var r=wl(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 m2={"%":(e,t)=>(e*100).toFixed(t),b:e=>Math.round(e).toString(2),c:e=>e+"",d:o4,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)=>p2(e*100,t),r:p2,s:c4,X:e=>Math.round(e).toString(16).toUpperCase(),x:e=>Math.round(e).toString(16)};function v2(e){return e}var d4=Array.prototype.map,f4=["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];function p4(e){var t=e.grouping===void 0||e.thousands===void 0?v2:l4(d4.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?v2:s4(d4.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=Pa(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"):m2[_]||(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=m2[_],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=u4(z)),O&&+z==0&&h!=="+"&&(O=!1),X=(O?h==="("?h:l:h==="-"||h==="("?"":h)+X,R=(_==="s"&&!isNaN(z)&&Qd!==void 0?f4[8+Qd/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(Da(f)/3)))*3,v=Math.pow(10,-p),h=u((d=Pa(d),d.type="f",d),{suffix:f4[8+p/3]});return function(b){return h(v*b)}}return{format:u,formatPrefix:c}}var qh,Ou,Yh;h2({thousands:",",grouping:[3],currency:["$",""]});function h2(e){return qh=p4(e),Ou=qh.format,Yh=qh.formatPrefix,qh}function g2(e){return Math.max(0,-Da(Math.abs(e)))}function y2(e,t){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(Da(t)/3)))*3-Da(Math.abs(e)))}function x2(e,t){return e=Math.abs(e),t=Math.abs(t)-e,Math.max(0,Da(t)-Da(e))+1}function Jd(e,t,r,n){var a=_u(e,t,r),i;switch(n=Pa(n??",f"),n.type){case"s":{var o=Math.max(Math.abs(e),Math.abs(t));return n.precision==null&&!isNaN(i=y2(a,o))&&(n.precision=i),Yh(n,o)}case"":case"e":case"g":case"p":case"r":{n.precision==null&&!isNaN(i=x2(a,Math.max(Math.abs(e),Math.abs(t))))&&(n.precision=i-(n.type==="e"));break}case"f":case"%":{n.precision==null&&!isNaN(i=g2(a))&&(n.precision=i-(n.type==="%")*2);break}}return Ou(n)}function Wr(e){var t=e.domain;return e.ticks=function(r){var n=t();return vl(n[0],n[n.length-1],r??10)},e.tickFormat=function(r,n){var a=t();return Jd(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=qd(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 Wh(){var e=bl();return e.copy=function(){return Ma(e,Wh())},jt.apply(e,arguments),Wr(e)}function Kh(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,bo),r):e.slice()},r.unknown=function(n){return arguments.length?(t=n,r):t},r.copy=function(){return Kh(e).unknown(t)},e=arguments.length?Array.from(e,bo):[0,1],Wr(r)}function Rd(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 gH(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 h4(e){return(t,r)=>-e(-t,r)}function ef(e){let t=e(m4,v4),r=t.domain,n=10,a,i;function o(){return a=gH(n),i=hH(n),r()[0]<0?(a=h4(a),i=h4(i),e(pH,mH)):e(m4,v4),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=Pa(s)).precision==null&&(s.trim=!0),s=Ou(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(Rd(r(),{floor:l=>i(Math.floor(a(l))),ceil:l=>i(Math.ceil(a(l)))})),t}function Gh(){let e=ef(xl()).domain([1,10]);return e.copy=()=>Ma(e,Gh()).base(e.base()),jt.apply(e,arguments),e}function g4(e){return function(t){return Math.sign(t)*Math.log1p(Math.abs(t/e))}}function y4(e){return function(t){return Math.sign(t)*Math.expm1(Math.abs(t))*e}}function tf(e){var t=1,r=e(g4(t),y4(t));return r.constant=function(n){return arguments.length?e(g4(t=+n),y4(t)):t},Wr(r)}function Xh(){var e=tf(xl());return e.copy=function(){return Ma(e,Xh()).constant(e.constant())},jt.apply(e,arguments)}function x4(e){return function(t){return t<0?-Math.pow(-t,e):Math.pow(t,e)}}function yH(e){return e<0?-Math.sqrt(-e):Math.sqrt(e)}function xH(e){return e<0?-e*e:e*e}function rf(e){var t=e(pr,pr),r=1;function n(){return r===1?e(pr,pr):r===.5?e(yH,xH):e(x4(r),x4(1/r))}return t.exponent=function(a){return arguments.length?(r=+a,n()):r},Wr(t)}function nf(){var e=rf(xl());return e.copy=function(){return Ma(e,nf()).exponent(e.exponent())},jt.apply(e,arguments),e}function b4(){return nf.apply(null,arguments).exponent(.5)}function w4(e){return Math.sign(e)*e*e}function bH(e){return Math.sign(e)*Math.sqrt(Math.abs(e))}function Zh(){var e=bl(),t=[0,1],r=!1,n;function a(i){var o=bH(e(i));return isNaN(o)?n:r?Math.round(o):o}return a.invert=function(i){return e.invert(w4(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,bo)).map(w4)),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 Zh(e.domain(),t).round(r).clamp(e.clamp()).unknown(n)},jt.apply(a,arguments),Wr(a)}function Qh(){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 Jh().domain([e,t]).range(a).unknown(i)},jt.apply(Wr(o),arguments)}function Rh(){var e=[.5],t=[0,1],r,n=1;function a(i){return i!=null&&i<=i?t[ma(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 Rh().domain(e).range(t).unknown(r)},jt.apply(a,arguments)}var b2=new Date,w2=new Date;function gt(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(ugt(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)=>(b2.setTime(+i),w2.setTime(+o),e(b2),e(w2),Math.floor(r(b2,w2))),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 af=gt(()=>{},(e,t)=>{e.setTime(+e+t)},(e,t)=>t-e);af.every=e=>(e=Math.floor(e),!isFinite(e)||!(e>0)?null:e>1?gt(t=>{t.setTime(Math.floor(t/e)*e)},(t,r)=>{t.setTime(+t+r*e)},(t,r)=>(r-t)/e):af);var Kse=af.range;var Xn=gt(e=>{e.setTime(e-e.getMilliseconds())},(e,t)=>{e.setTime(+e+t*1e3)},(e,t)=>(t-e)/1e3,e=>e.getUTCSeconds()),S4=Xn.range;var Tu=gt(e=>{e.setTime(e-e.getMilliseconds()-e.getSeconds()*1e3)},(e,t)=>{e.setTime(+e+t*6e4)},(e,t)=>(t-e)/6e4,e=>e.getMinutes()),wH=Tu.range,Mu=gt(e=>{e.setUTCSeconds(0,0)},(e,t)=>{e.setTime(+e+t*6e4)},(e,t)=>(t-e)/6e4,e=>e.getUTCMinutes()),SH=Mu.range;var Du=gt(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()),AH=Du.range,Pu=gt(e=>{e.setUTCMinutes(0,0,0)},(e,t)=>{e.setTime(+e+t*36e5)},(e,t)=>(t-e)/36e5,e=>e.getUTCHours()),NH=Pu.range;var vi=gt(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),_H=vi.range,Nl=gt(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/864e5,e=>e.getUTCDate()-1),CH=Nl.range,e0=gt(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/864e5,e=>Math.floor(e/864e5)),EH=e0.range;function _l(e){return gt(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 hi=_l(0),ju=_l(1),N4=_l(2),_4=_l(3),wo=_l(4),C4=_l(5),E4=_l(6),k4=hi.range,kH=ju.range,OH=N4.range,TH=_4.range,MH=wo.range,DH=C4.range,PH=E4.range;function Cl(e){return gt(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 gi=Cl(0),Iu=Cl(1),O4=Cl(2),T4=Cl(3),So=Cl(4),M4=Cl(5),D4=Cl(6),P4=gi.range,jH=Iu.range,IH=O4.range,zH=T4.range,LH=So.range,BH=M4.range,UH=D4.range;var zu=gt(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()),$H=zu.range,Lu=gt(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()),HH=Lu.range;var kn=gt(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());kn.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:gt(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 VH=kn.range,On=gt(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());On.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:gt(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 FH=On.range;function I4(e,t,r,n,a,i){let o=[[Xn,1,1e3],[Xn,5,5*1e3],[Xn,15,15*1e3],[Xn,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(_u(u/31536e6,c/31536e6,d));if(p===0)return af.every(Math.max(_u(u,c,d),1));let[v,h]=o[f/o[p-1][2]53)return null;"w"in $||($.w=1),"Z"in $?(ae=E2(lf($.y,0,1)),ie=ae.getUTCDay(),ae=ie>4||ie===0?Iu.ceil(ae):Iu(ae),ae=Nl.offset(ae,($.V-1)*7),$.y=ae.getUTCFullYear(),$.m=ae.getUTCMonth(),$.d=ae.getUTCDate()+($.w+6)%7):(ae=C2(lf($.y,0,1)),ie=ae.getDay(),ae=ie>4||ie===0?ju.ceil(ae):ju(ae),ae=vi.offset(ae,($.V-1)*7),$.y=ae.getFullYear(),$.m=ae.getMonth(),$.d=ae.getDate()+($.w+6)%7)}else("W"in $||"U"in $)&&("w"in $||($.w="u"in $?$.u%7:"W"in $?1:0),ie="Z"in $?E2(lf($.y,0,1)).getUTCDay():C2(lf($.y,0,1)).getDay(),$.m=0,$.d="W"in $?($.w+6)%7+$.W*7-(ie+5)%7:$.w+$.U*7-(ie+6)%7);return"Z"in $?($.H+=$.Z/100|0,$.M+=$.Z%100,E2($)):C2($)}}function E(se,Ne,F,$){for(var q=0,ae=Ne.length,ie=F.length,G,xe;q=ie)return-1;if(G=Ne.charCodeAt(q++),G===37){if(G=Ne.charAt(q++),xe=k[G in z4?Ne.charAt(q++):G],!xe||($=xe(se,F,$))<0)return-1}else if(G!=F.charCodeAt($++))return-1}return $}function L(se,Ne,F){var $=u.exec(Ne.slice(F));return $?(se.p=c.get($[0].toLowerCase()),F+$[0].length):-1}function I(se,Ne,F){var $=p.exec(Ne.slice(F));return $?(se.w=v.get($[0].toLowerCase()),F+$[0].length):-1}function j(se,Ne,F){var $=d.exec(Ne.slice(F));return $?(se.w=f.get($[0].toLowerCase()),F+$[0].length):-1}function z(se,Ne,F){var $=m.exec(Ne.slice(F));return $?(se.m=g.get($[0].toLowerCase()),F+$[0].length):-1}function X(se,Ne,F){var $=h.exec(Ne.slice(F));return $?(se.m=b.get($[0].toLowerCase()),F+$[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 H(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 z4={"-":"",_:" ",0:"0"},yr=/^\s*\d+/,YH=/^%/,WH=/[\\^$*+?|[\]().{}]/g;function at(e,t,r){var n=e<0?"-":"",a=(n?-e:e)+"",i=a.length;return n+(i[t.toLowerCase(),r]))}function GH(e,t,r){var n=yr.exec(t.slice(r,r+1));return n?(e.w=+n[0],r+n[0].length):-1}function XH(e,t,r){var n=yr.exec(t.slice(r,r+1));return n?(e.u=+n[0],r+n[0].length):-1}function ZH(e,t,r){var n=yr.exec(t.slice(r,r+2));return n?(e.U=+n[0],r+n[0].length):-1}function QH(e,t,r){var n=yr.exec(t.slice(r,r+2));return n?(e.V=+n[0],r+n[0].length):-1}function JH(e,t,r){var n=yr.exec(t.slice(r,r+2));return n?(e.W=+n[0],r+n[0].length):-1}function L4(e,t,r){var n=yr.exec(t.slice(r,r+4));return n?(e.y=+n[0],r+n[0].length):-1}function B4(e,t,r){var n=yr.exec(t.slice(r,r+2));return n?(e.y=+n[0]+(+n[0]>68?1900:2e3),r+n[0].length):-1}function RH(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 eV(e,t,r){var n=yr.exec(t.slice(r,r+1));return n?(e.q=n[0]*3-3,r+n[0].length):-1}function tV(e,t,r){var n=yr.exec(t.slice(r,r+2));return n?(e.m=n[0]-1,r+n[0].length):-1}function U4(e,t,r){var n=yr.exec(t.slice(r,r+2));return n?(e.d=+n[0],r+n[0].length):-1}function rV(e,t,r){var n=yr.exec(t.slice(r,r+3));return n?(e.m=0,e.d=+n[0],r+n[0].length):-1}function $4(e,t,r){var n=yr.exec(t.slice(r,r+2));return n?(e.H=+n[0],r+n[0].length):-1}function nV(e,t,r){var n=yr.exec(t.slice(r,r+2));return n?(e.M=+n[0],r+n[0].length):-1}function aV(e,t,r){var n=yr.exec(t.slice(r,r+2));return n?(e.S=+n[0],r+n[0].length):-1}function iV(e,t,r){var n=yr.exec(t.slice(r,r+3));return n?(e.L=+n[0],r+n[0].length):-1}function oV(e,t,r){var n=yr.exec(t.slice(r,r+6));return n?(e.L=Math.floor(n[0]/1e3),r+n[0].length):-1}function lV(e,t,r){var n=YH.exec(t.slice(r,r+1));return n?r+n[0].length:-1}function sV(e,t,r){var n=yr.exec(t.slice(r));return n?(e.Q=+n[0],r+n[0].length):-1}function uV(e,t,r){var n=yr.exec(t.slice(r));return n?(e.s=+n[0],r+n[0].length):-1}function H4(e,t){return at(e.getDate(),t,2)}function cV(e,t){return at(e.getHours(),t,2)}function dV(e,t){return at(e.getHours()%12||12,t,2)}function fV(e,t){return at(1+vi.count(kn(e),e),t,3)}function W4(e,t){return at(e.getMilliseconds(),t,3)}function pV(e,t){return W4(e,t)+"000"}function mV(e,t){return at(e.getMonth()+1,t,2)}function vV(e,t){return at(e.getMinutes(),t,2)}function hV(e,t){return at(e.getSeconds(),t,2)}function gV(e){var t=e.getDay();return t===0?7:t}function yV(e,t){return at(hi.count(kn(e)-1,e),t,2)}function K4(e){var t=e.getDay();return t>=4||t===0?wo(e):wo.ceil(e)}function xV(e,t){return e=K4(e),at(wo.count(kn(e),e)+(kn(e).getDay()===4),t,2)}function bV(e){return e.getDay()}function wV(e,t){return at(ju.count(kn(e)-1,e),t,2)}function SV(e,t){return at(e.getFullYear()%100,t,2)}function AV(e,t){return e=K4(e),at(e.getFullYear()%100,t,2)}function NV(e,t){return at(e.getFullYear()%1e4,t,4)}function _V(e,t){var r=e.getDay();return e=r>=4||r===0?wo(e):wo.ceil(e),at(e.getFullYear()%1e4,t,4)}function CV(e){var t=e.getTimezoneOffset();return(t>0?"-":(t*=-1,"+"))+at(t/60|0,"0",2)+at(t%60,"0",2)}function V4(e,t){return at(e.getUTCDate(),t,2)}function EV(e,t){return at(e.getUTCHours(),t,2)}function kV(e,t){return at(e.getUTCHours()%12||12,t,2)}function OV(e,t){return at(1+Nl.count(On(e),e),t,3)}function G4(e,t){return at(e.getUTCMilliseconds(),t,3)}function TV(e,t){return G4(e,t)+"000"}function MV(e,t){return at(e.getUTCMonth()+1,t,2)}function DV(e,t){return at(e.getUTCMinutes(),t,2)}function PV(e,t){return at(e.getUTCSeconds(),t,2)}function jV(e){var t=e.getUTCDay();return t===0?7:t}function IV(e,t){return at(gi.count(On(e)-1,e),t,2)}function X4(e){var t=e.getUTCDay();return t>=4||t===0?So(e):So.ceil(e)}function zV(e,t){return e=X4(e),at(So.count(On(e),e)+(On(e).getUTCDay()===4),t,2)}function LV(e){return e.getUTCDay()}function BV(e,t){return at(Iu.count(On(e)-1,e),t,2)}function UV(e,t){return at(e.getUTCFullYear()%100,t,2)}function $V(e,t){return e=X4(e),at(e.getUTCFullYear()%100,t,2)}function HV(e,t){return at(e.getUTCFullYear()%1e4,t,4)}function VV(e,t){var r=e.getUTCDay();return e=r>=4||r===0?So(e):So.ceil(e),at(e.getUTCFullYear()%1e4,t,4)}function FV(){return"+0000"}function F4(){return"%"}function q4(e){return+e}function Y4(e){return Math.floor(+e/1e3)}var Bu,t0,Z4,r0,Q4;O2({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 O2(e){return Bu=k2(e),t0=Bu.format,Z4=Bu.parse,r0=Bu.utcFormat,Q4=Bu.utcParse,Bu}function qV(e){return new Date(e)}function YV(e){return e instanceof Date?+e:+new Date(+e)}function n0(e,t,r,n,a,i,o,l,s,u){var c=bl(),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(_,YV)):f().map(qV)},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(Rd(M,_)):c},c.copy=function(){return Ma(c,n0(e,t,r,n,a,i,o,l,s,u))},c}function T2(){return jt.apply(n0(N2,_2,kn,zu,hi,vi,Du,Tu,Xn,t0).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)}function M2(){return jt.apply(n0(S2,A2,On,Lu,gi,Nl,Pu,Mu,Xn,r0).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)}function a0(){var e=0,t=1,r,n,a,i,o=pr,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(En),u.rangeRound=c(yl),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 yi(e,t){return t.domain(e.domain()).interpolator(e.interpolator()).clamp(e.clamp()).unknown(e.unknown())}function i0(){var e=Wr(a0()(pr));return e.copy=function(){return yi(e,i0())},Gn.apply(e,arguments)}function D2(){var e=ef(a0()).domain([1,10]);return e.copy=function(){return yi(e,D2()).base(e.base())},Gn.apply(e,arguments)}function P2(){var e=tf(a0());return e.copy=function(){return yi(e,P2()).constant(e.constant())},Gn.apply(e,arguments)}function o0(){var e=rf(a0());return e.copy=function(){return yi(e,o0()).exponent(e.exponent())},Gn.apply(e,arguments)}function J4(){return o0.apply(null,arguments).exponent(.5)}function l0(){var e=[],t=pr;function r(n){if(n!=null&&!isNaN(n=+n))return t((ma(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(Yr),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)=>Ph(e,i/n))},r.copy=function(){return l0(t).domain(e)},Gn.apply(r,arguments)}function s0(){var e=0,t=.5,r=1,n=1,a,i,o,l,s,u=pr,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 GV(n)?n:"point"}};function XV(e,t){for(var r=0,n=e.length,a=e[0]t)?r=i+1:n=i}return r}function f0(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=XV(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 tM(e){if(e!=null)return"invert"in e&&typeof e.invert=="function"?e.invert.bind(e):f0(e,void 0)}function rM(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 p0(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);re.cartesianAxis.xAxis[t],Ia=(e,t)=>{var r=L2(e,t);return r??Xt},Zt={allowDataOverflow:!1,allowDecimals:!0,allowDuplicatedCategory:!0,angle:0,dataKey:void 0,domain:z2,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:il},B2=(e,t)=>e.cartesianAxis.yAxis[t],za=(e,t)=>{var r=B2(e,t);return r??Zt},nF={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:""},U2=(e,t)=>{var r=e.cartesianAxis.zAxis[t];return r??nF},Ir=(e,t,r)=>{switch(t){case"xAxis":return Ia(e,r);case"yAxis":return za(e,r);case"zAxis":return U2(e,r);case"angleAxis":return Nh(e,r);case"radiusAxis":return _h(e,r);default:throw new Error("Unexpected axis type: ".concat(t))}},aF=(e,t,r)=>{switch(t){case"xAxis":return Ia(e,r);case"yAxis":return za(e,r);default:throw new Error("Unexpected axis type: ".concat(t))}},pf=(e,t,r)=>{switch(t){case"xAxis":return Ia(e,r);case"yAxis":return za(e,r);case"angleAxis":return Nh(e,r);case"radiusAxis":return _h(e,r);default:throw new Error("Unexpected axis type: ".concat(t))}},$2=e=>e.graphicalItems.cartesianItems.some(t=>t.type==="bar")||e.graphicalItems.polarItems.some(t=>t.type==="radialBar");function H2(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 V2=e=>e.graphicalItems.cartesianItems,iF=V([fr,$d],H2),F2=(e,t,r)=>e.filter(r).filter(n=>t?.includeHidden===!0?!0:!n.hide),$u=V([V2,Ir,iF],F2,{memoizeOptions:{resultEqualityCheck:Au}}),oM=V([$u],e=>e.filter(t=>t.type==="area"||t.type==="bar").filter(Hd)),q2=e=>e.filter(t=>!("stackId"in t)||t.stackId===void 0),oF=V([$u],q2),Y2=e=>e.map(t=>t.data).filter(Boolean).flat(1),lF=V([$u],e=>e.some(t=>!t.data)),lM=V([$u],Y2,{memoizeOptions:{resultEqualityCheck:Au}}),W2=(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)},K2=V([lM,Bd],W2),sF=(e,t,r)=>t?.dataKey!=null?e.map(n=>({value:Ct(n,t.dataKey)})):r.length>0?r.map(n=>n.dataKey).flatMap(n=>e.map(a=>({value:Ct(a,n)}))):e.map(n=>({value:n})),G2=(e,t,r,n,a,i)=>{var o=n.chartData,l=o===void 0?[]:o,s=n.dataStartIndex,u=n.dataEndIndex,c=sF(e,t,r);if(a&&t?.dataKey!=null&&i.length>0){var d=l.slice(s,u+1),f=d.map(p=>({value:Ct(p,t.dataKey)})).filter(p=>p.value!=null);return[...f,...c]}return c},mf=V([K2,Ir,$u,Bd,lF,lM],G2);function Uu(e){if(qr(e)||e instanceof Date){var t=Number(e);if(Ie(t))return t}}function aM(e){if(Array.isArray(e)){var t=[Uu(e[0]),Uu(e[1])];return Cn(t)?t:void 0}var r=Uu(e);if(r!=null)return[r,r]}function ha(e){return e.map(Uu).filter(dr)}function uF(e,t){var r=Uu(e),n=Uu(t);return r==null&&n==null?0:r==null?-1:n==null?1:r-n}var cF=V([mf],e=>e?.map(t=>t.value).sort(uF));function sM(e,t){switch(e){case"xAxis":return t.direction==="x";case"yAxis":return t.direction==="y";default:return!1}}function dF(e,t,r){if(!r)return[];if(!r.length)return[];var n;if(typeof t=="number"&&!Mr(t))n=t;else if(Array.isArray(t)){var a=ha(t);a.length>0&&(n=Math.max(...a))}return n==null?[]:ha(r.flatMap(i=>{var o=Ct(e,i.dataKey),l,s;if(Array.isArray(o)){var u=iM(o,2);l=u[0],s=u[1]}else l=s=o;if(!(!Ie(l)||!Ie(s)))return[n-l,n+s]}))}var Qt=e=>{var t=Gt(e),r=go(e);return pf(e,t,r)},xi=V([Qt],e=>e?.dataKey),fF=V([oM,Bd,Qt],kh),X2=(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=iM(o,2),s=l[0],u=l[1],c=n?[...u].reverse():u,d=c.map(Su);return[s,{stackedData:bO(e,d,r),graphicalItems:c}]}))},Z2=V([fF,oM,bu,bh],X2),Q2=(e,t,r,n)=>{var a=t.dataStartIndex,i=t.dataEndIndex;if(n==null&&r!=="zAxis")return SO(e,a,i)},pF=V([Ir],e=>e.allowDataOverflow),m0=e=>{var t;if(e==null||!("domain"in e))return z2;if(e.domain!=null)return e.domain;if("ticks"in e&&e.ticks!=null){if(e.type==="number"){var r=ha(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:z2},uM=V([Ir],m0),cM=V([uM,pF],hh),mF=V([Z2,ln,fr,cM],Q2,{memoizeOptions:{resultEqualityCheck:pl}}),v0=e=>e.errorBars,vF=(e,t,r)=>e.flatMap(n=>t[n.id]).filter(Boolean).filter(n=>sM(r,n)),ff=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=>sM(i,p));d.forEach(p=>{var v,h=Ct(p,(v=r.dataKey)!==null&&v!==void 0?v:u.dataKey),b=dF(p,h,f);if(b.length>=2){var m=Math.min(...b),g=Math.max(...b);(l==null||ms)&&(s=g)}var x=aM(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=aM(Ct(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]},hF=V([K2,Ir,oF,v0,fr,p3],J2,{memoizeOptions:{resultEqualityCheck:pl}});function gF(e){var t=e.value;if(qr(t)||t instanceof Date)return t}var yF=(e,t,r)=>{var n=e.map(gF).filter(a=>a!=null);return r&&(t.dataKey==null||t.allowDuplicatedCategory&&Tb(n))?vh(0,e.length):t.allowDuplicatedCategory?n:Array.from(new Set(n))},R2=e=>e.referenceElements.dots,El=(e,t,r)=>e.filter(n=>n.ifOverflow==="extendDomain").filter(n=>t==="xAxis"?n.xAxisId===r:n.yAxisId===r),xF=V([R2,fr,$d],El),ew=e=>e.referenceElements.areas,bF=V([ew,fr,$d],El),tw=e=>e.referenceElements.lines,wF=V([tw,fr,$d],El),rw=(e,t)=>{if(e!=null){var r=ha(e.map(n=>t==="xAxis"?n.x:n.y));if(r.length!==0)return[Math.min(...r),Math.max(...r)]}},SF=V(xF,fr,rw),nw=(e,t)=>{if(e!=null){var r=ha(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)]}},AF=V([bF,fr],nw);function NF(e){var t;if(e.x!=null)return ha([e.x]);var r=(t=e.segment)===null||t===void 0?void 0:t.map(n=>n.x);return r==null||r.length===0?[]:ha(r)}function _F(e){var t;if(e.y!=null)return ha([e.y]);var r=(t=e.segment)===null||t===void 0?void 0:t.map(n=>n.y);return r==null||r.length===0?[]:ha(r)}var aw=(e,t)=>{if(e!=null){var r=e.flatMap(n=>t==="xAxis"?NF(n):_F(n));if(r.length!==0)return[Math.min(...r),Math.max(...r)]}},CF=V([wF,fr],aw),EF=V(SF,CF,AF,(e,t,r)=>ff(e,r,t)),iw=(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?ff(n,i,a):ff(i,a),d=g3(t,c,e.allowDataOverflow);return d??(e.allowDataOverflow&&c==null&&s!=null?s:d)},kF=e=>{if(!(e==null||e.type!=="number"||!("ticks"in e)||e.ticks==null)){var t=ha(e.ticks);if(t.length!==0)return[Math.min(...t),Math.max(...t)]}},OF=V([Ir],kF,{memoizeOptions:{resultEqualityCheck:pl}}),TF=V([Ir,uM,cM,mF,hF,EF,lt,fr,OF],iw,{memoizeOptions:{resultEqualityCheck:pl}}),MF=[0,1],ow=(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=on(t,i);if(u&&l==null){var c;return vh(0,(c=r?.length)!==null&&c!==void 0?c:0)}return s==="category"?yF(n,e,u):a==="expand"&&!u?MF:o}},lw=V([Ir,lt,K2,mf,bu,fr,TF],ow),Hu=V([Ir,$2,wu],d0),sw=(e,t,r)=>{var n=t.niceTicks;if(n!=="none"){var a=m0(t),i=Array.isArray(a)&&(a[0]==="auto"||a[1]==="auto");if((n==="snap125"||n==="adaptive")&&t!=null&&t.tickCount&&Cn(e)){if(i)return yh(e,t.tickCount,t.allowDecimals,n);if(t.type==="number")return xh(e,t.tickCount,t.allowDecimals,n)}if(n==="auto"&&r==="linear"&&t!=null&&t.tickCount){if(i&&Cn(e))return yh(e,t.tickCount,t.allowDecimals,"adaptive");if(t.type==="number"&&Cn(e))return xh(e,t.tickCount,t.allowDecimals,"adaptive")}}},uw=V([lw,pf,Hu],sw),cw=(e,t,r,n)=>{if(n!=="angleAxis"&&e?.type==="number"&&Cn(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},DF=V([Ir,lw,uw,fr],cw),PF=V(mf,Ir,(e,t)=>{if(!(!t||t.type!=="number")){var r=1/0,n=Array.from(ha(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=Fn(r,e*i),l=e*i/2;return l-o-(l-o)/i*o}return 0}),jF=(e,t,r)=>{var n=Ia(e,t);return n==null||typeof n.padding!="string"?0:dM(e,"xAxis",t,r,n.padding)},IF=(e,t,r)=>{var n=za(e,t);return n==null||typeof n.padding!="string"?0:dM(e,"yAxis",t,r,n.padding)},zF=V(Ia,jF,(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}}),LF=V(za,IF,(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}}),dw=V([Et,zF,ol,fu,(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]}),fw=V([Et,lt,LF,ol,fu,(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]}),Vu=(e,t,r,n)=>{var a;switch(t){case"xAxis":return dw(e,r,n);case"yAxis":return fw(e,r,n);case"zAxis":return(a=U2(e,r))===null||a===void 0?void 0:a.range;case"angleAxis":return e2(e);case"radiusAxis":return t2(e,r);default:return}},fM=V([Ir,Vu],dl),BF=V([Hu,DF],M3),pw=V([Ir,Hu,BF,fM],df),mw=(e,t,r,n)=>{if(!(r==null||r.dataKey==null)){var a=r.type,i=r.scale,o=on(e,n);if(o&&(a==="number"||i!=="auto"))return t.map(l=>l.value)}},vw=V([lt,mf,pf,fr],mw),h0=V([pw],Vd),hde=V([pw],tM),gde=V([pw,cF],f0),yde=V([$u,v0,fr],vF);function pM(e,t){return e.idt.id?1:0}var g0=(e,t)=>t,y0=(e,t,r)=>r,UF=V(cu,g0,y0,(e,t,r)=>e.filter(n=>n.orientation===t).filter(n=>n.mirror===r).sort(pM)),$F=V(du,g0,y0,(e,t,r)=>e.filter(n=>n.orientation===t).filter(n=>n.mirror===r).sort(pM)),mM=(e,t)=>{var r=typeof t.height=="number"?t.height:kd;return{width:e.width,height:r}},HF=(e,t)=>{var r=typeof t.width=="number"?t.width:il;return{width:r,height:e.height}},vM=V(Et,Ia,mM),VF=(e,t,r)=>{switch(t){case"top":return e.top;case"bottom":return r-e.bottom;default:return 0}},FF=(e,t,r)=>{switch(t){case"left":return e.left;case"right":return r-e.right;default:return 0}},qF=V(jr,Et,UF,g0,y0,(e,t,r,n,a)=>{var i={},o;return r.forEach(l=>{var s=mM(t,l);o==null&&(o=VF(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}),YF=V(Pr,Et,$F,g0,y0,(e,t,r,n,a)=>{var i={},o;return r.forEach(l=>{var s=HF(t,l);o==null&&(o=FF(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}),WF=(e,t)=>{var r=Ia(e,t);if(r!=null)return qF(e,r.orientation,r.mirror)},hM=V([Et,Ia,WF,(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}}}),KF=(e,t)=>{var r=za(e,t);if(r!=null)return YF(e,r.orientation,r.mirror)},gM=V([Et,za,KF,(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}}}),yM=V(Et,za,(e,t)=>{var r=typeof t.width=="number"?t.width:il;return{width:r,height:e.height}});var hw=(e,t,r,n)=>{if(r!=null){var a=r.allowDuplicatedCategory,i=r.type,o=r.dataKey,l=on(e,n),s=t.map(c=>c.value),u=s.filter(c=>c!=null);if(o&&l&&i==="category"&&a&&Tb(u))return s}},gw=V([lt,mf,Ir,fr],hw),yw=V([lt,aF,Hu,h0,gw,vw,Vu,uw,fr],(e,t,r,n,a,i,o,l,s)=>{if(t!=null){var u=on(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}}}),GF=(e,t,r,n,a,i,o,l,s)=>{if(!(t==null||n==null)){var u=on(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?cr(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(dr):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(dr):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(dr):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(dr)}},x0=V([lt,pf,Hu,h0,uw,Vu,gw,vw,fr],GF),XF=(e,t,r,n,a,i,o)=>{if(!(t==null||r==null||n==null||n[0]===n[1])){var l=on(e,o),s=t.tickCount,u=0;return u=o==="angleAxis"&&n?.length>=2?cr(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(dr):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(dr):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(dr)}},xw=V([lt,pf,h0,Vu,gw,vw,fr],XF),bw=V(Ir,h0,(e,t)=>{if(!(e==null||t==null))return p0(p0({},e),{},{scale:t})}),ZF=V([Ir,Hu,lw,fM],df),QF=V([ZF],Vd),xde=V((e,t,r)=>U2(e,r),QF,(e,t)=>{if(!(e==null||t==null))return p0(p0({},e),{},{scale:t})}),xM=V([lt,cu,du],(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}}),JF=(e,t,r)=>{var n;return(n=e.renderedTicks[t])===null||n===void 0?void 0:n[r]},bde=V([JF],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,Sw=e=>e.options.validateTooltipEventTypes;function Aw(e,t,r){if(e==null)return t;var n=e?"axis":"item";return r==null?t:r.includes(n)?n:t}function Ao(e,t){var r=ww(e),n=Sw(e);return Aw(t,r,n)}function bM(e){return Se(t=>Ao(t,e))}var b0=(e,t)=>{var r,n=Number(t);if(!(Mr(n)||t==null))return n>=0?e==null||(r=e[n])===null||r===void 0?void 0:r.value:void 0};var wM=e=>e.tooltip.settings;var bi={active:!1,index:null,dataKey:void 0,graphicalItemId:void 0,coordinate:void 0},RF={itemInteraction:{click:bi,hover:bi},axisInteraction:{click:bi,hover:bi},keyboardInteraction:bi,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}},SM=nt({name:"tooltip",initialState:RF,reducers:{addTooltipEntrySettings:{reducer(e,t){e.tooltipItemPayloads.push($e(t.payload))},prepare:st()},replaceTooltipEntrySettings:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=Er(e).tooltipItemPayloads.indexOf($e(n));i>-1&&(e.tooltipItemPayloads[i]=$e(a))},prepare:st()},removeTooltipEntrySettings:{reducer(e,t){var r=Er(e).tooltipItemPayloads.indexOf($e(t.payload));r>-1&&e.tooltipItemPayloads.splice(r,1)},prepare:st()},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}}}),Zn=SM.actions,AM=Zn.addTooltipEntrySettings,NM=Zn.replaceTooltipEntrySettings,_M=Zn.removeTooltipEntrySettings,CM=Zn.setTooltipSettingsState,EM=Zn.setActiveMouseOverItemIndex,Ode=Zn.mouseLeaveItem,w0=Zn.mouseLeaveChart,Tde=Zn.setActiveClickItemIndex,S0=Zn.setMouseOverAxisIndex,kM=Zn.setMouseClickAxisIndex,Fu=Zn.setSyncInteraction,vf=Zn.setKeyboardInteraction;var OM=SM.reducer;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 A0(e){for(var t=1;t{if(t==null)return bi;var a=nq(e,t,r);if(a==null)return bi;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(aq(a)){if(i)return A0(A0({},a),{},{active:!0})}else if(n!=null)return{active:!0,coordinate:void 0,dataKey:void 0,index:n,graphicalItemId:void 0};return A0(A0({},bi),{},{coordinate:a.coordinate})};function iq(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 oq(e,t){var r=iq(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 lq(e,t,r){if(r==null||t==null)return!0;var n=Ct(e,t);return n==null||!Cn(r)?!0:oq(n,r)}var No=(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||lq(u,r,n)?String(s):null};var _0=(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 C0=(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 E0=e=>e.options.tooltipPayloadSearcher;var wi=e=>e.tooltip;function MM(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 DM(e){for(var t=1;te(t)}function PM(e){if(typeof e=="string")return e}function mq(e){if(!(e==null||typeof e!="object")){var t="name"in e?dq(e.name):void 0,r="unit"in e?fq(e.unit):void 0,n="dataKey"in e?pq(e.dataKey):void 0,a="payload"in e?e.payload:void 0,i="color"in e?PM(e.color):void 0,o="fill"in e?PM(e.fill):void 0;return{name:t,unit:r,dataKey:n,payload:a,color:i,fill:o}}}function vq(e,t){return e??t}var k0=(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=vq(h,l),g=Array.isArray(m)?Wv(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=Lm(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=mq(M),j=I?.name,z=I?.dataKey,X=I?.payload,R=DM(DM({},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(D1({tooltipEntrySettings:R,dataKey:z,payload:X,value:Ct(X,z),name:j==null?void 0:String(j)}))});else{var _;f.push(D1({tooltipEntrySettings:b,dataKey:x,payload:k,value:Ct(k,x),name:(_=Ct(k,A))!==null&&_!==void 0?_:b?.name}))}return f},d)}};var Nw=V([Qt,$2,wu],d0),hq=V([e=>e.graphicalItems.cartesianItems,e=>e.graphicalItems.polarItems],(e,t)=>[...e,...t]),gq=V([Gt,go],H2),_o=V([hq,Qt,gq],F2,{memoizeOptions:{resultEqualityCheck:Au}}),yq=V([_o],e=>e.filter(Hd)),jM=V([_o],Y2,{memoizeOptions:{resultEqualityCheck:Au}}),xq=V([_o],e=>e.some(t=>!t.data)),Si=V([jM,ln],W2),bq=V([yq,ln,Qt],kh),_w=V([Si,Qt,_o,ln,xq,jM],G2),IM=V([Qt],m0),wq=V([Qt],e=>e.allowDataOverflow),zM=V([IM,wq],hh),Sq=V([_o],e=>e.filter(Hd)),Aq=V([bq,Sq,bu,bh],X2),Nq=V([Aq,ln,Gt,zM],Q2),_q=V([_o],q2),Cq=V([Si,Qt,_q,v0,Gt,m3],J2,{memoizeOptions:{resultEqualityCheck:pl}}),Eq=V([R2,Gt,go],El),kq=V([Eq,Gt],rw),Oq=V([ew,Gt,go],El),Tq=V([Oq,Gt],nw),Mq=V([tw,Gt,go],El),Dq=V([Mq,Gt],aw),Pq=V([kq,Dq,Tq],ff),jq=V([Qt,IM,zM,Nq,Cq,Pq,lt,Gt],iw),Co=V([Qt,lt,Si,_w,bu,Gt,jq],ow),Iq=V([Co,Qt,Nw],sw),zq=V([Qt,Co,Iq,Gt],cw),LM=e=>{var t=Gt(e),r=go(e),n=!1;return Vu(e,t,r,n)},Cw=V([Qt,LM],dl),Lq=V([Qt,Nw,zq,Cw],df),Ew=V([Lq],Vd),Bq=V([lt,_w,Qt,Gt],hw),Uq=V([lt,_w,Qt,Gt],mw),$q=(e,t,r,n,a,i,o,l)=>{if(t){var s=t.type,u=on(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?cr(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(dr):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(dr)}}},un=V([lt,Qt,Nw,Ew,LM,Bq,Uq,Gt],$q),kw=V([ww,Sw,wM],(e,t,r)=>Aw(r.shared,e,t)),BM=e=>e.tooltip.settings.trigger,Ow=e=>e.tooltip.settings.defaultIndex,hf=V([wi,kw,BM,Ow],N0),kl=V([hf,Si,xi,Co],No),O0=V([un,kl],b0),UM=V([hf],e=>{if(e)return e.dataKey}),$M=V([hf],e=>{if(e)return e.graphicalItemId}),HM=V([wi,kw,BM,Ow],C0),Hq=V([Pr,jr,lt,Et,un,Ow,HM],_0),Tw=V([hf,Hq],(e,t)=>e!=null&&e.coordinate?e.coordinate:t),Mw=V([hf],e=>{var t;return(t=e?.active)!==null&&t!==void 0?t:!1}),Vq=V([HM,kl,ln,xi,O0,E0,kw],k0),VM=V([Vq],e=>{if(e!=null){var t=e.map(r=>r.payload).filter(r=>r!=null);return Array.from(new Set(t))}});function FM(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 qM(e){for(var t=1;tSe(Qt),YM=()=>{var e=Wq(),t=Se(un),r=Se(Ew);return!e||!r?uu(void 0,t):uu(qM(qM({},e),{},{scale:r}),t)};function WM(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 qu(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}},GM=(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 qu(qu(qu({},n),Vt(n.cx,n.cy,o,i)),{},{angle:i,radius:o})}var l=a.coordinate,s=n.angle;return qu(qu(qu({},n),Vt(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 XM(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 Dw=(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(cr(b-h)!==cr(m-b)){var x=[];if(cr(m-b)===cr(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 T0=()=>Se(wu),Pw=(e,t)=>t,ZM=(e,t,r)=>r,jw=(e,t,r,n)=>n,QM=V(un,e=>di(e,t=>t.coordinate)),Iw=V([wi,Pw,ZM,jw],N0),zw=V([Iw,Si,xi,Co],No),JM=(e,t,r)=>{if(t!=null){var n=wi(e);return t==="axis"?r==="hover"?n.axisInteraction.hover.dataKey:n.axisInteraction.click.dataKey:r==="hover"?n.itemInteraction.hover.dataKey:n.itemInteraction.click.dataKey}},RM=V([wi,Pw,ZM,jw],C0),gf=V([Pr,jr,lt,Et,un,jw,RM],_0),e8=V([Iw,gf],(e,t)=>{var r;return(r=e.coordinate)!==null&&r!==void 0?r:t}),Lw=V([un,zw],b0),t8=V([RM,zw,ln,xi,Lw,E0,Pw],k0),r8=V([Iw,zw],(e,t)=>({isActive:e.active&&t!=null,activeIndex:t})),Zq=(e,t,r,n,a,i,o)=>{if(!(!e||!r||!n||!a)&&XM(e,o)){var l=AO(e,t),s=Dw(l,i,a,r,n),u=KM(t,a,s,e);return{activeIndex:String(s),activeCoordinate:u}}},Qq=(e,t,r,n,a,i,o)=>{if(!(!e||!n||!a||!i||!r)){var l=RT(e,r);if(l){var s=NO(l,t),u=Dw(s,o,i,n,a),c=GM(t,i,u,l);return{activeIndex:String(u),activeCoordinate:c}}}},n8=(e,t,r,n,a,i,o,l)=>{if(!(!e||!t||!n||!a||!i))return t==="horizontal"||t==="vertical"?Zq(e,t,n,a,i,o,l):Qq(e,t,r,n,a,i,o)};var Yu=ee(ne()),p8=ee(ip());var a8=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}}),i8=V(e=>e.zIndex.zIndexMap,e=>{var t=Object.keys(e).map(n=>parseInt(n,10)).concat(Object.values(dt)),r=Array.from(new Set(t));return r.sort((n,a)=>n-a)},{memoizeOptions:{resultEqualityCheck:T3}});function o8(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 l8(e){for(var t=1;tl8(l8({},e),{},{[t]:{element:void 0,panoramaElement:void 0,consumers:0}}),tY)},nY=new Set(Object.values(dt));function aY(e){return nY.has(e)}var s8=nt({name:"zIndex",initialState:rY,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:st()},unregisterZIndexPortal:{reducer:(e,t)=>{var r=t.payload.zIndex;e.zIndexMap[r]&&(e.zIndexMap[r].consumers-=1,e.zIndexMap[r].consumers<=0&&!aY(r)&&delete e.zIndexMap[r])},prepare:st()},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:st()},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:st()}}}),M0=s8.actions,u8=M0.registerZIndexPortal,D0=M0.unregisterZIndexPortal,c8=M0.registerZIndexPortalElement,d8=M0.unregisterZIndexPortalElement;var f8=s8.reducer;function kr(e){var t=e.zIndex,r=e.children,n=$O(),a=n&&t!==void 0&&t!==0,i=bt(),o=(0,Yu.useRef)(void 0),l=(0,Yu.useRef)(new Set),s=Ve(),u=Se(d=>a8(d,t,i));if((0,Yu.useLayoutEffect)(()=>{if(!a){var d=l.current;d.forEach(p=>{s(D0({zIndex:p}))}),d.clear(),o.current=void 0;return}if(l.current.has(t)||(s(u8({zIndex:t})),l.current.add(t)),u){o.current=u;var f=l.current;f.forEach(p=>{p!==t&&(s(D0({zIndex:p})),f.delete(p))})}},[s,t,a,u]),(0,Yu.useLayoutEffect)(()=>{var d=l.current;return()=>{d.forEach(f=>{s(D0({zIndex:f}))}),d.clear()}},[s]),!a)return r;var c=u??o.current;return c?(0,p8.createPortal)(r,c):null}function Bw(){return Bw=Object.assign?Object.assign.bind():function(e){for(var t=1;t(0,I0.useContext)(Uw);var xf=ee(ne());var x8=ee(y8(),1);var b8=x8.default;var Ku=new b8;var L0="recharts.syncEvent.tooltip",Hw="recharts.syncEvent.brush";var w8=(e,t)=>{if(t&&Array.isArray(e)){var r=Number.parseInt(t,10);if(!Mr(r))return e[r]}},fY={chartName:"",tooltipPayloadSearcher:()=>{},eventEmitter:void 0,defaultTooltipEventType:"axis"},S8=nt({name:"options",initialState:fY,reducers:{createEventEmitter:e=>{e.eventEmitter==null&&(e.eventEmitter=Symbol("rechartsEventEmitter"))}}}),A8=S8.reducer,N8=S8.actions.createEventEmitter;function _8(e){return e.tooltip.syncInteraction}var pY={chartData:void 0,computedData:void 0,dataStartIndex:0,dataEndIndex:0},C8=nt({name:"chartData",initialState:pY,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)}}}),Vw=C8.actions,Fw=Vw.setChartData,E8=Vw.setDataStartEndIndexes,mY=Vw.setComputedData;var k8=C8.reducer;var vY=["x","y"];function O8(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 Gu(e){for(var t=1;ts.rootProps.className);(0,xf.useEffect)(()=>{if(e==null)return ua;var s=(u,c,d)=>{if(t!==d&&e===u){if(c.payload.active===!1){r(Fu({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=xY(p,vY),m=c.payload.sourceViewBox,g=m.x,x=m.y,A=m.width,k=m.height,_=Gu(Gu({},b),{},{x:o.x+(A?(v-g)/A:0)*o.width,y:o.y+(k?(h-x)/k:0)*o.height});r(Gu(Gu({},c),{},{payload:Gu(Gu({},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(Fu({active:!1,coordinate:void 0,dataKey:void 0,index:null,label:void 0,sourceViewBox:void 0,graphicalItemId:void 0}));return}if(M==null){r(Fu({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=Fu({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 Ku.on(L0,s),()=>{Ku.off(L0,s)}},[l,r,t,e,n,a,i,o])}function SY(){var e=Se(wh),t=Se(Sh),r=Ve();(0,xf.useEffect)(()=>{if(e==null)return ua;var n=(a,i,o)=>{t!==o&&e===a&&r(E8(i))};return Ku.on(Hw,n),()=>{Ku.off(Hw,n)}},[r,t,e])}function T8(){var e=Ve();(0,xf.useEffect)(()=>{e(N8())},[e]),wY(),SY()}function M8(e,t,r,n,a,i){var o=Se(v=>JM(v,e,t)),l=Se($M),s=Se(Sh),u=Se(wh),c=Se(J1),d=Se(_8),f=d?.sourceViewBox!=null,p=sl();(0,xf.useEffect)(()=>{if(!f&&u!=null&&s!=null){var v=Fu({active:i,coordinate:r,dataKey:o,index:a,label:typeof n=="number"?String(n):n,sourceViewBox:p,graphicalItemId:l});Ku.emit(L0,u,v,s)}},[f,r,o,l,a,n,s,u,c,i,p])}function D8(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 P8(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r{M(CM({shared:g,trigger:x,axisId:_,active:a,defaultIndex:E}))},[M,g,x,_,a,E]);var L=sl(),I=nh(),j=bM(g),z=(t=Se(F=>r8(F,j,x,E)))!==null&&t!==void 0?t:{},X=z.activeIndex,R=z.isActive,Q=Se(F=>t8(F,j,x,E)),y=Se(F=>Lw(F,j,x,E)),C=Se(F=>e8(F,j,x,E)),O=Q,D=h8(),N=(r=a??R)!==null&&r!==void 0?r:!1,T=wk([O,N]),H=CY(T,2),Y=H[0],J=H[1],fe=j==="axis"?y:void 0;M8(j,x,C,fe,X,N);var pe=k??D;if(pe==null||L==null||j==null)return null;var ge=O??I8;N||(ge=I8),u&&ge.length&&(ge=QE(ge.filter(F=>F.value!=null&&(F.hide!==!0||n.includeHidden)),f,MY));var ze=ge.length>0,se=P8(P8({},n),{},{payload:ge,label:fe,active:N,activeIndex:X,coordinate:C,accessibilityLayer:I}),Ne=Qn.createElement(sT,{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},DY(s,se));return Qn.createElement(Qn.Fragment,null,(0,L8.createPortal)(Ne,pe),N&&Qn.createElement(v8,{cursor:m,tooltipEventType:j,coordinate:C,payload:ge,index:X}))}var Yw=ee(ne()),H0=ee(ne());function jY(e,t,r){return(t=IY(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function IY(e){var t=zY(e,"string");return typeof t=="symbol"?t:t+""}function zY(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 B0=class{constructor(t){jY(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 B8(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 LY(e){for(var t=1;t{try{var r=document.getElementById($8);r||(r=document.createElement("span"),r.setAttribute("id",$8),r.setAttribute("aria-hidden","true"),document.body.appendChild(r)),Object.assign(r.style,VY,t),r.textContent="".concat(e);var n=r.getBoundingClientRect();return{width:n.width,height:n.height}}catch{return{width:0,height:0}}},Ol=function(t){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};if(t==null||Wn.isSsr)return{width:0,height:0};if(!V8.enableCache)return H8(t,r);var n=FY(t,r),a=U8.get(n);if(a)return a;var i=H8(t,r);return U8.set(n,i),i};var K8;function U0(e,t){return KY(e)||WY(e,t)||YY(e,t)||qY()}function qY(){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 YY(e,t){if(e){if(typeof e=="string")return F8(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)?F8(e,t):void 0}}function F8(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=[];Dt(t)||(r?a=t.toString().split(""):a=t.toString().split(eD));var i=a.map(l=>({word:l,width:Ol(l,n).width})),o=r?0:Ol("\xA0",n).width;return{wordsWithComputedWidth:i,spaceWidth:o}}catch{return null}};function V0(e){return e==="start"||e==="middle"||e==="end"||e==="inherit"}function rD(e){return Dt(e)||typeof e=="string"||typeof e=="number"||typeof e=="boolean"}var nD=(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),pW="\u2026",J8=(e,t,r,n,a,i,o,l)=>{var s=e.slice(0,t),u=tD({breakAll:r,style:n,children:s+pW});if(!u)return[!1,[]];var c=nD(u.wordsWithComputedWidth,i,o,l),d=c.length>a||aD(c).width>Number(i);return[d,c]},mW=(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=nD(t,n,r,a);if(!u||a)return d;var f=d.length>i||aD(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=J8(c,g,s,l,i,n,r,a),A=Z8(x,2),k=A[0],_=A[1],M=J8(c,m,s,l,i,n,r,a),E=Z8(M,1),L=E[0];if(!k&&!L&&(p=m+1),k&&L&&(v=m-1),!k&&L){b=_;break}h++}return b||d},R8=e=>{var t=Dt(e)?[]:e.toString().split(eD);return[{words:t,width:void 0}]},vW=e=>{var t=e.width,r=e.scaleToFit,n=e.children,a=e.style,i=e.breakAll,o=e.maxLines;if((t||r)&&!Wn.isSsr){var l,s,u=tD({breakAll:i,children:n,style:a});if(u){var c=u.wordsWithComputedWidth,d=u.spaceWidth;l=c,s=d}else return R8(n);return mW({breakAll:i,children:n,maxLines:o,style:a},l,s,t,!!r)}return R8(n)},iD="#808080",hW={angle:0,breakAll:!1,capHeight:"0.71em",fill:iD,lineHeight:"1em",scaleToFit:!1,textAnchor:"start",verticalAnchor:"end",x:0,y:0},bf=(0,H0.forwardRef)((e,t)=>{var r=Pt(e,hW),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=X8(r,oW),f=(0,H0.useMemo)(()=>vW({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=X8(d,lW);if(!qr(n)||!qr(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=$0("calc(".concat(o,")"));break;case"middle":k=$0("calc(".concat((f.length-1)/2," * -").concat(i," + (").concat(o," / 2))"));break;default:k=$0("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(" ")),Yw.createElement("text",qw({},Kt(g),{ref:t,x,y:A,className:Le("recharts-text",b),textAnchor:u,fill:l.includes("url")?iD:l}),f.map((I,j)=>{var z=I.words.join(m?"":" ");return Yw.createElement("tspan",{x,dy:j===0?k:i,key:"".concat(z,"-").concat(j)},z)}))});bf.displayName="Text";var Tn=ee(ne()),xr=ee(ne());var gW=["labelRef"],yW=["content"];function oD(e,t){if(e==null)return{};var r,n,a=xW(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,xr.useMemo)(()=>({x:t,y:r,upperWidth:n,lowerWidth:a,width:i,height:o}),[t,r,n,a,i,o]);return Tn.createElement(sD.Provider,{value:s},l)},cD=()=>{var e=(0,xr.useContext)(sD),t=sl();return e||(t?md(t):void 0)},AW=(0,xr.createContext)(null);var NW=()=>{var e=(0,xr.useContext)(AW),t=Se(Eh);return e||t},_W=e=>{var t=e.value,r=e.formatter,n=Dt(e.children)?t:e.children;return typeof r=="function"?r(n):n},Ml=e=>e!=null&&typeof e=="function",CW=(e,t)=>{var r=cr(t-e),n=Math.min(Math.abs(t-e),360);return r*n},EW=(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=CW(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=Vt(l,s,v,m),A=Vt(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),_=Dt(e.id)?ui("recharts-radial-line-"):e.id;return Tn.createElement("text",Ni({},n,{dominantBaseline:"central",className:Le("recharts-radial-bar-label",o)}),Tn.createElement("defs",null,Tn.createElement("path",{id:_,d:k})),Tn.createElement("textPath",{xlinkHref:"#".concat(_)},r))},kW=(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=Vt(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=Vt(n,a,p,u),h=v.x,b=v.y;return{x:h,y:b,textAnchor:"middle",verticalAnchor:"middle"}},Qu=e=>e!=null&&"cx"in e&&ye(e.cx),OW={angle:0,offset:5,zIndex:dt.label,position:"middle",textBreakAll:!1};function TW(e){if(!Qu(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 Ai(e){var t,r,n=Pt(e,OW),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=NW(),h=cD(),b=o==="center"?h:v??h,m,g,x;a==null?m=b:Qu(a)?m=a:m=md(a);var A=TW(m);if(!m||Dt(l)&&Dt(s)&&!(0,xr.isValidElement)(u)&&typeof u!="function")return null;var k=Qu(m)&&(o==="insideStart"||o==="insideEnd"||o==="end");if(Qu(m))k||(x=kW(m,n.offset,n.position));else if(A){var _=cE({viewBox:A,position:o,offset:n.offset,parentViewBox:Qu(i)?void 0:i,clamp:!0});x=Tl(Tl({x:_.x,y:_.y,textAnchor:_.horizontalAnchor,verticalAnchor:_.verticalAnchor},_.width!==void 0?{width:_.width}:{}),_.height!==void 0?{height:_.height}:{})}var M=Tl(Tl(Tl(Tl({},((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,xr.isValidElement)(u)){var E=M.labelRef,L=oD(M,gW);return(0,xr.cloneElement)(u,L)}if(typeof u=="function"){var I=M.content,j=oD(M,yW);if(g=(0,xr.createElement)(u,j),(0,xr.isValidElement)(g))return g}else g=_W(n);var z=Kt(n);return k&&Qu(m)?EW(n,o,g,z,m):x==null?null:Tn.createElement(kr,{zIndex:n.zIndex},Tn.createElement(bf,Ni({ref:p,className:Le("recharts-label",d)},z,x,{textAnchor:V0(z.textAnchor)?z.textAnchor:x.textAnchor,breakAll:f}),g))}Ai.displayName="Label";var MW=(e,t,r)=>{if(!e)return null;var n={viewBox:t,labelRef:r};return e===!0?Tn.createElement(Ai,Ni({key:"label-implicit"},n)):qr(e)?Tn.createElement(Ai,Ni({key:"label-implicit",value:e},n)):(0,xr.isValidElement)(e)?e.type===Ai?(0,xr.cloneElement)(e,Tl({key:"label-implicit"},n)):Tn.createElement(Ai,Ni({key:"label-implicit",content:e},n)):Ml(e)?Tn.createElement(Ai,Ni({key:"label-implicit",content:e},n)):e&&typeof e=="object"?Tn.createElement(Ai,Ni({},e,{key:"label-implicit"},n)):null};function dD(e){var t=e.label,r=e.labelRef,n=cD();return MW(t,n,r)||null}var _i=ee(ne()),Ju=ee(ne());var DW=["valueAccessor"],PW=["dataKey","clockWise","id","textBreakAll","zIndex"];function q0(){return q0=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(rD(t))return t},pD=(0,Ju.createContext)(void 0),mD=pD.Provider,vD=(0,Ju.createContext)(void 0),Jme=vD.Provider;function zW(){return(0,Ju.useContext)(pD)}function LW(){return(0,Ju.useContext)(vD)}function F0(e){var t=e.valueAccessor,r=t===void 0?IW:t,n=fD(e,DW),a=n.dataKey,i=n.clockWise,o=n.id,l=n.textBreakAll,s=n.zIndex,u=fD(n,PW),c=zW(),d=LW(),f=c||d;return!f||!f.length?null:_i.createElement(kr,{zIndex:s??dt.label},_i.createElement(sr,{className:"recharts-label-list"},f.map((p,v)=>{var h,b=Dt(a)?r(p,v):Ct(p.payload,a),m=Dt(o)?{}:{id:"".concat(o,"-").concat(v)};return _i.createElement(Ai,q0({key:"label-".concat(v)},Kt(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}))})))}F0.displayName="LabelList";function hD(e){var t=e.label;return t?t===!0?_i.createElement(F0,{key:"labelList-implicit"}):_i.isValidElement(t)||Ml(t)?_i.createElement(F0,{key:"labelList-implicit",content:t}):typeof t=="object"?_i.createElement(F0,q0({key:"labelList-implicit"},t,{type:String(t.type)})):null:null}var gD=ee(ne());function Ww(){return Ww=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)?gD.createElement("circle",Ww({},gr(e),eu(e),{className:i,cx:t,cy:r,r:n})):null};var BW={radiusAxis:{},angleAxis:{}},yD=nt({name:"polarAxis",initialState:BW,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]}}}),W0=yD.actions,lve=W0.addRadiusAxis,sve=W0.removeRadiusAxis,uve=W0.addAngleAxis,cve=W0.removeAngleAxis;var xD=yD.reducer;function bD(e){return e&&typeof e=="object"&&"className"in e&&typeof e.className=="string"?e.className:""}var UW=ee(ne());var K0=e=>e&&typeof e=="object"&&"clipDot"in e?!!e.clipDot:!0;var Ru=ee(ne()),wf=ee(ne());function wD(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 SD(e){for(var t=1;t{n||(a.current===null?r(AM(t)):a.current!==t&&r(NM({prev:a.current,next:t})),a.current=t)},[t,r,n]),(0,Sf.useLayoutEffect)(()=>()=>{a.current&&(r(_M(a.current)),a.current=null)},[r]),null}var Af=ee(ne());function CD(e){var t=e.legendPayload,r=Ve(),n=bt(),a=(0,Af.useRef)(null);return(0,Af.useLayoutEffect)(()=>{n||(a.current===null?r(VO(t)):a.current!==t&&r(FO({prev:a.current,next:t})),a.current=t)},[r,n,t]),(0,Af.useLayoutEffect)(()=>()=>{a.current&&(r(qO(a.current)),a.current=null)},[r]),null}var OD=ee(ne()),Cf=ee(ne());function KW(e,t){return QW(e)||ZW(e,t)||XW(e,t)||GW()}function GW(){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 XW(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);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 Kw(n,t)}function eK(e,t){var r=t.map((n,a)=>e[a]);return Kw(r,t)}function tK(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=KW(l,2),u=s[0],c=s[1];a.has(u)||o.push(c)}return Kw(i,t,o)}function _f(e,t,r){return t==null?null:e==null?t.map(n=>({status:"added",next:n})):r===Nf?RW(e,t):r===JW?eK(e,t):rK(e,t,r)}var ec=ee(ne());function G0(e,t){var r=(0,ec.useRef)(e),n=(0,ec.useRef)(t.current),a=(0,ec.useRef)(!0);r.current!==e&&(r.current=e,n.current=t.current,a.current=!1);var i=(0,ec.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 nK(e,t){return lK(e)||oK(e,t)||iK(e,t)||aK()}function aK(){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 iK(e,t){if(e){if(typeof e=="string")return kD(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)?kD(e,t):void 0}}function kD(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,Cf.useCallback)(()=>{typeof t=="function"&&t(),i(!1)},[t]);return{isAnimating:a,handleAnimationStart:o,handleAnimationEnd:l}}function MD(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=uh(r,n),g=G0(m,i),x=(t=g.startValue)!==null&&t!==void 0?t:null,A=_f(x,a,p??Nf);return OD.createElement(sh,{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 jD=ee(ne()),Xw=ee(ne());var X0=ee(ne());var Gw;function sK(e,t){return fK(e)||dK(e,t)||cK(e,t)||uK()}function uK(){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 cK(e,t){if(e){if(typeof e=="string")return DD(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)?DD(e,t):void 0}}function DD(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{var e=X0.useState(()=>ui("uid-")),t=sK(e,1),r=t[0];return r},Z0=(Gw=X0.useId)!==null&&Gw!==void 0?Gw:pK;function PD(e,t){var r=Z0();return t||(e?"".concat(e,"-").concat(r):r)}var mK=(0,Xw.createContext)(void 0),ID=e=>{var t=e.id,r=e.type,n=e.children,a=PD("recharts-".concat(r),t);return jD.createElement(mK.Provider,{value:a},n(a))};var Dl=ee(ne());var vK={cartesianItems:[],polarItems:[]},zD=nt({name:"graphicalItems",initialState:vK,reducers:{addCartesianGraphicalItem:{reducer(e,t){e.cartesianItems.push($e(t.payload))},prepare:st()},replaceCartesianGraphicalItem:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=Er(e).cartesianItems.indexOf($e(n));i>-1&&(e.cartesianItems[i]=$e(a))},prepare:st()},removeCartesianGraphicalItem:{reducer(e,t){var r=Er(e).cartesianItems.indexOf($e(t.payload));r>-1&&e.cartesianItems.splice(r,1)},prepare:st()},addPolarGraphicalItem:{reducer(e,t){e.polarItems.push($e(t.payload))},prepare:st()},removePolarGraphicalItem:{reducer(e,t){var r=Er(e).polarItems.indexOf($e(t.payload));r>-1&&e.polarItems.splice(r,1)},prepare:st()},replacePolarGraphicalItem:{reducer(e,t){var r=t.payload,n=r.prev,a=r.next,i=Er(e).polarItems.indexOf($e(n));i>-1&&(e.polarItems[i]=$e(a))},prepare:st()}}}),tc=zD.actions,LD=tc.addCartesianGraphicalItem,BD=tc.replaceCartesianGraphicalItem,UD=tc.removeCartesianGraphicalItem,hK=tc.addPolarGraphicalItem,gK=tc.removePolarGraphicalItem,yK=tc.replacePolarGraphicalItem;var $D=zD.reducer;var xK=e=>{var t=Ve(),r=(0,Dl.useRef)(null);return(0,Dl.useLayoutEffect)(()=>{r.current===null?t(LD(e)):r.current!==e&&t(BD({prev:r.current,next:e})),r.current=e},[t,e]),(0,Dl.useLayoutEffect)(()=>()=>{r.current&&(t(UD(r.current)),r.current=null)},[t]),null},HD=(0,Dl.memo)(xK);var Ef=ee(ne()),J0=ee(ne());var bK=["points"];function VD(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 Zw(e){for(var t=1;t{var m,g,x=Zw(Zw(Zw({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 Ef.createElement(CK,{key:"dot-".concat(b),option:r,dotProps:x,className:a})}),v={};return l&&s!=null&&(v.clipPath="url(#clipPath-".concat(d?"":"dots-").concat(s,")")),Ef.createElement(kr,{zIndex:c},Ef.createElement(sr,Q0({className:n},v),p))}var kf=ee(ne()),eg=ee(ne());function qD(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 R0(e){for(var t=1;t({top:e.top,bottom:e.bottom,left:e.left,right:e.right}));var r6=V([t6,Pr,jr],(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 rc=()=>Se(r6),n6=()=>Se(VM);function a6(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 Jw(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=Jw(Jw(Jw({},l),uo(a)),eu(a)),u;return(0,eg.isValidElement)(a)?u=(0,eg.cloneElement)(a,s):typeof a=="function"?u=a(s):u=kf.createElement(Y0,s),kf.createElement(sr,{className:"recharts-active-dot",clipPath:o},u)};function Rw(e){var t=e.points,r=e.mainColor,n=e.activeDot,a=e.itemDataKey,i=e.clipPath,o=e.zIndex,l=o===void 0?dt.activeDot:o,s=Se(kl),u=n6();if(t==null||u==null)return null;var c=t.find(d=>u.includes(d.payload));return Dt(c)?null:kf.createElement(kr,{zIndex:l},kf.createElement(IK,{point:c,childIndex:Number(s),mainColor:r,dataKey:a,activeDot:n,clipPath:i}))}var i6=ee(ne());var o6=e=>{var t=e.chartData,r=Ve(),n=bt();return(0,i6.useEffect)(()=>n?()=>{}:(r(Fw(t)),()=>{r(Fw(void 0))}),[t,r,n]),null};var l6={x:0,y:0,width:0,height:0,padding:{top:0,right:0,bottom:0,left:0}},s6=nt({name:"brush",initialState:l6,reducers:{setBrushSettings(e,t){return t.payload==null?l6:t.payload}}}),The=s6.actions.setBrushSettings;var u6=s6.reducer;function zK(e){return(e%180+180)%180}var c6=function(t){var r=t.width,n=t.height,a=arguments.length>1&&arguments[1]!==void 0?arguments[1]:0,i=zK(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=Er(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=Er(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=Er(e).lines.findIndex(n=>n===t.payload);r!==-1&&e.lines.splice(r,1)}}}),nc=d6.actions,Ihe=nc.addDot,zhe=nc.removeDot,Lhe=nc.addArea,Bhe=nc.removeArea,Uhe=nc.addLine,$he=nc.removeLine;var f6=d6.reducer;var Of=ee(ne()),Tf=ee(ne());function BK(e,t){return VK(e)||HK(e,t)||$K(e,t)||UK()}function UK(){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 $K(e,t){if(e){if(typeof e=="string")return p6(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)?p6(e,t):void 0}}function p6(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{var t=e.children,r=(0,Tf.useState)("".concat(ui("recharts"),"-clip")),n=BK(r,1),a=n[0],i=rc();if(i==null)return null;var o=i.x,l=i.y,s=i.width,u=i.height;return Of.createElement(FK.Provider,{value:a},Of.createElement("defs",null,Of.createElement("clipPath",{id:a},Of.createElement("rect",{x:o,y:l,height:u,width:s}))),t)};var kt=ee(ne()),Br=ee(ne());function v6(){}function eS(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 h6(e,t,r){return Mf(e,t,void 0,void 0,void 0,void 0,r)}function Mf(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 Df(e,t,i,o)}return Df(e,t,i,o)}function Df(e,t,r,n){if(Object.is(e,t))return!0;let a=ci(e),i=ci(t);if(a==="[object Arguments]"&&(a=au),i==="[object Arguments]"&&(i=au),a!==i)return!1;switch(a){case tu:return e.toString()===t.toString();case ru:return po(e.valueOf(),t.valueOf());case nu:case Xm:case Gm:return Object.is(e.valueOf(),t.valueOf());case Wm:return e.source===t.source&&e.flags===t.flags;case jE: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 Zm:if(e.size!==t.size)return!1;for(let[s,u]of e.entries())if(!t.has(s)||!Mf(u,t.get(s),s,e,t,r,n))return!1;return!0;case Qm:{if(e.size!==t.size)return!1;let s=Array.from(e.values()),u=Array.from(t.values());for(let c=0;cMf(d,p,void 0,e,t,r,n));if(f===-1)return!1;u.splice(f,1)}return!0}case Jm:case tv:case rv:case nv:case av:case zE:case iv:case ov:case lv:case LE:case sv:case uv:if(yd(e)!==yd(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 x6(e,t){return tg(e,t+1)}function b6(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:tg(n,u)};var h=s,b,m=()=>(b===void 0&&(b=r(v,h)),b),g=v.coordinate,x=s===0||Pl(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 w6(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||Pl(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=Lr(Lr({},v),{},{tickCoord:m>0?v.coordinate-m*e:v.coordinate})}else i[f]=v=Lr(Lr({},v),{},{tickCoord:v.coordinate});if(v.tickCoord!=null){var g=Pl(e,v.tickCoord,b,l,s);g&&(s=v.tickCoord-e*(b()/2+a),i[f]=Lr(Lr({},v),{},{isShow:!0}))}},c=o-1;c>=0;c--)u(c);return i}function GK(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=Lr(Lr({},c),{},{tickCoord:f>0?c.coordinate-f*e:c.coordinate}),c.tickCoord!=null){var p=Pl(e,c.tickCoord,()=>d,s,u);p&&(u=c.tickCoord-e*(d/2+a),o[l-1]=Lr(Lr({},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=Lr(Lr({},A),{},{tickCoord:M<0?A.coordinate-M*e:A.coordinate})}else o[g]=A=Lr(Lr({},A),{},{tickCoord:A.coordinate});if(A.tickCoord!=null){var E=Pl(e,A.tickCoord,_,s,u);E&&(s=A.tickCoord+e*(_()/2+a),o[g]=Lr(Lr({},A),{},{isShow:!0}))}},b=0;b{var M=typeof u=="function"?u(k.value,_):k.value;return v==="width"?g6(Ol(M,{fontSize:t,letterSpacing:r}),h,d):Ol(M,{fontSize:t,letterSpacing:r})[v]},m=a[0],g=a[1],x=a.length>=2&&m!=null&&g!=null?cr(g.coordinate-m.coordinate):1,A=y6(i,x,v);return s==="equidistantPreserveStart"?b6(x,A,b,a,o):s==="equidistantPreserveEnd"?w6(x,A,b,a,o):(s==="preserveStart"||s==="preserveStartEnd"?p=GK(x,A,b,a,o,s==="preserveStartEnd"):p=KK(x,A,b,a,o),p.filter(k=>k.isShow))}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.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 N6=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 XK={xAxis:{},yAxis:{}},_6=nt({name:"renderedTicks",initialState:XK,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]}}}),C6=_6.actions,E6=C6.setRenderedTicks,k6=C6.removeRenderedTicks;var O6=_6.reducer;var ZK=["axisLine","width","height","className","hide","ticks","axisType","axisId"];function T6(e,t){return eG(e)||RK(e,t)||JK(e,t)||QK()}function QK(){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 JK(e,t){if(e){if(typeof e=="string")return M6(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)?M6(e,t):void 0}}function M6(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&&tS(l.ticks,o)||(i.current={ticks:o,axisId:n,axisType:r},a(E6({ticks:o,axisId:n,axisType:r})))}},[a,t,n,r]),(0,Br.useEffect)(()=>n==null||r==null?ua:()=>{a(k6({axisId:n,axisType:r}))},[a,n,r]),null}var fG=(0,Br.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=Pf(Ft(Ft({},k),{},{ticks:n}),x,A),I=gr(k),j=uo(a),z=V0(I.textAnchor)?I.textAnchor:sG(d,f),X=uG(d,f),R={};typeof i=="object"&&(R=i);var Q=Ft(Ft({},I),{},{fill:"none"},R),y=L.map(D=>Ft({entry:D},lG(D,p,v,h,b,d,m,f,g))),C=y.map(D=>{var N=D.entry,T=D.line;return kt.createElement(sr,{className:"recharts-cartesian-axis-tick",key:"tick-".concat(N.value,"-").concat(N.coordinate,"-").concat(N.tickCoord)},i&&kt.createElement("line",jl({},Q,T,{className:Le("recharts-cartesian-axis-tick-line",wn(i,"className"))})))}),O=y.map((D,N)=>{var T,H,Y=D.entry,J=D.tick,fe=Ft(Ft(Ft(Ft({verticalAnchor:X},I),{},{textAnchor:z,stroke:"none",fill:o},J),{},{index:N,payload:Y,visibleTicksCount:L.length,tickFormatter:l,padding:u},c),{},{angle:(T=(H=c?.angle)!==null&&H!==void 0?H:I.angle)!==null&&T!==void 0?T:0}),pe=Ft(Ft({},fe),j);return kt.createElement(sr,jl({className:"recharts-cartesian-axis-tick-label",key:"tick-label-".concat(Y.value,"-").concat(Y.coordinate,"-").concat(Y.tickCoord)},kE(_,Y,N)),a&&kt.createElement(cG,{option:a,tickProps:pe,value:"".concat(typeof l=="function"?l(Y.value,N):Y.value).concat(s||"")}))});return kt.createElement("g",{className:"recharts-cartesian-axis-ticks recharts-".concat(M,"-ticks")},kt.createElement(dG,{ticks:L,axisId:E,axisType:M}),O.length>0&&kt.createElement(kr,{zIndex:dt.label},kt.createElement("g",{className:"recharts-cartesian-axis-tick-labels recharts-".concat(M,"-tick-labels"),ref:t},O)),C.length>0&&kt.createElement("g",{className:"recharts-cartesian-axis-tick-lines recharts-".concat(M,"-tick-lines")},C))}),pG=(0,Br.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=tG(e,ZK),d=(0,Br.useState)(""),f=T6(d,2),p=f[0],v=f[1],h=(0,Br.useState)(""),b=T6(h,2),m=b[0],g=b[1],x=(0,Br.useRef)(null);(0,Br.useImperativeHandle)(t,()=>({getCalculatedWidth:()=>{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})},getCalculatedHeight:()=>{var k;return N6({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,Br.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:kt.createElement(kr,{zIndex:e.zIndex},kt.createElement(sr,{className:Le("recharts-cartesian-axis",i)},kt.createElement(oG,{x:e.x,y:e.y,width:n,height:a,orientation:e.orientation,mirror:e.mirror,axisLine:r,otherSvgProps:gr(e)}),kt.createElement(fG,{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}),kt.createElement(uD,{x:e.x,y:e.y,width:e.width,height:e.height,lowerWidth:e.width,upperWidth:e.width},kt.createElement(dD,{label:e.label,labelRef:e.labelRef}),e.children)))}),jf=kt.forwardRef((e,t)=>{var r=Pt(e,Jn);return kt.createElement(pG,jl({},r,{ref:t}))});jf.displayName="CartesianAxis";var qt=ee(ne());var rg=ee(ne());var P6={grid:{stroke:"#ccc",fill:"none"}};var j6=(0,rg.createContext)(P6),q0e=j6.Provider,I6=()=>(0,rg.useContext)(j6);var mG=["x1","y1","x2","y2","key"],vG=["offset"],hG=["xAxisId","yAxisId"],gG=["xAxisId","yAxisId"];function z6(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 Ur(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 qt.createElement("rect",{x:n,y:a,ry:l,width:i,height:o,stroke:"none",fill:t,fillOpacity:r,className:"recharts-cartesian-grid-bg"})};function L6(e){var t=e.option,r=e.lineItemProps,n;if(qt.isValidElement(t))n=qt.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=ng(r,mG),d=(a=gr(c))!==null&&a!==void 0?a:{},f=d.offset,p=ng(d,vG),v=Array.isArray(p.strokeDasharray)?p.strokeDasharray.join(","):p.strokeDasharray;n=qt.createElement("line",Il({},p,{strokeDasharray:v,x1:i,y1:o,x2:l,y2:s,fill:"none",key:u}))}return n}function AG(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=ng(e,hG),u=i.map((c,d)=>{var f=Ur(Ur({},s),{},{x1:t,y1:c,x2:t+r,y2:c,key:"line-".concat(d),index:d});return qt.createElement(L6,{key:"line-".concat(d),option:a,lineItemProps:f})});return qt.createElement("g",{className:"recharts-cartesian-grid-horizontal"},u)}function NG(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=ng(e,gG),u=i.map((c,d)=>{var f=Ur(Ur({},s),{},{x1:c,y1:t,x2:c,y2:t+r,key:"line-".concat(d),index:d});return qt.createElement(L6,{option:a,lineItemProps:f,key:"line-".concat(d)})});return qt.createElement("g",{className:"recharts-cartesian-grid-vertical"},u)}function _G(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 qt.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 qt.createElement("g",{className:"recharts-cartesian-gridstripes-horizontal"},d)}function CG(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 qt.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 qt.createElement("g",{className:"recharts-cartesian-gridstripes-vertical"},d)}var EG=(e,t)=>{var r=e.xAxis,n=e.width,a=e.height,i=e.offset;return E1(Pf(Ur(Ur(Ur({},Jn),r),{},{ticks:k1(r,!0),viewBox:{x:0,y:0,width:n,height:a}})),i.left,i.left+i.width,t)},kG=(e,t)=>{var r=e.yAxis,n=e.width,a=e.height,i=e.offset;return E1(Pf(Ur(Ur(Ur({},Jn),r),{},{ticks:k1(r,!0),viewBox:{x:0,y:0,width:n,height:a}})),i.top,i.top+i.height,t)},OG={horizontal:!0,vertical:!0,horizontalPoints:[],verticalPoints:[],verticalFill:[],horizontalFill:[],xAxisId:0,yAxisId:0,syncWithTicks:!1,zIndex:dt.grid};function zl(e){var t,r,n,a,i,o,l=Rv(),s=eh(),u=Jv(),c=Ur(Ur({},Pt(e,OG)),{},{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=bt(),k=Se(C=>yw(C,"xAxis",d,A)),_=Se(C=>yw(C,"yAxis",f,A)),M=I6(),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(!nn(h)||!nn(b)||!ye(p)||!ye(v))return null;var L=c.verticalCoordinatesGenerator||EG,I=c.horizontalCoordinatesGenerator||kG,j=c.horizontalPoints,z=c.verticalPoints;if((!j||!j.length)&&typeof I=="function"){var X=g&&g.length,R=I({yAxis:_?Ur(Ur({},_),{},{ticks:X?g:_.ticks}):void 0,width:l??h,height:s??b,offset:u},X?!0:m);pu(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?Ur(Ur({},k),{},{ticks:Q?x:k.ticks}):void 0,width:l??h,height:s??b,offset:u},Q?!0:m);pu(Array.isArray(y),"verticalCoordinatesGenerator should return Array but instead it returned [".concat(typeof y,"]")),Array.isArray(y)&&(z=y)}return qt.createElement(kr,{zIndex:c.zIndex},qt.createElement("g",{className:"recharts-cartesian-grid"},qt.createElement(SG,{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}),qt.createElement(_G,Il({},c,{horizontalPoints:j})),qt.createElement(CG,Il({},c,{verticalPoints:z})),qt.createElement(AG,Il({},c,E,{offset:u,horizontalPoints:j,xAxis:k,yAxis:_})),qt.createElement(NG,Il({},c,E,{offset:u,verticalPoints:z,xAxis:k,yAxis:_}))))}zl.displayName="CartesianGrid";var TG={},B6=nt({name:"errorBars",initialState:TG,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))}}}),rS=B6.actions,uge=rS.addErrorBar,cge=rS.replaceErrorBar,dge=rS.removeErrorBar;var U6=B6.reducer;var nS=ee(ne());function aS(e,t){var r,n,a=Se(u=>Ia(u,e)),i=Se(u=>za(u,t)),o=(r=a?.allowDataOverflow)!==null&&r!==void 0?r:Xt.allowDataOverflow,l=(n=i?.allowDataOverflow)!==null&&n!==void 0?n:Zt.allowDataOverflow,s=o||l;return{needClip:s,needClipX:o,needClipY:l}}function $6(e){var t=e.xAxisId,r=e.yAxisId,n=e.clipPathId,a=rc(),i=aS(t,r),o=i.needClipX,l=i.needClipY,s=i.needClip,u=Se(x=>dw(x,t,!1)),c=Se(x=>fw(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 nS.createElement("clipPath",{id:"clipPath-".concat(n)},nS.createElement("rect",{x:h,y:b,width:m,height:g}))}function H6(e){var t=uo(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=ee(ne()),Ll=ee(ne());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.xAxisId)!==null&&r!==void 0?r:Qw}function ig(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:Qw}var V6=(e,t,r)=>bw(e,"xAxis",ag(e,t),r),F6=(e,t,r)=>xw(e,"xAxis",ag(e,t),r),q6=(e,t,r)=>bw(e,"yAxis",ig(e,t),r),Y6=(e,t,r)=>xw(e,"yAxis",ig(e,t),r),MG=V([lt,V6,q6,F6,Y6],(e,t,r,n,a)=>on(e,"xAxis")?uu(t,n,!1):uu(r,a,!1)),DG=(e,t)=>t,iS=V([V2,DG],(e,t)=>e.filter(r=>r.type==="area").find(r=>r.id===t)),W6=e=>{var t=lt(e),r=on(t,"xAxis");return r?"yAxis":"xAxis"},PG=(e,t)=>{var r=W6(e);return r==="yAxis"?ig(e,t):ag(e,t)},K6=(e,t,r)=>Z2(e,W6(e),PG(e,t),r),jG=V([iS,K6],(e,t)=>{var r;if(!(e==null||t==null)){var n=e.stackId,a=Su(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]])}}}),IG=V([iS,K6],(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(dr)}}),G6=V([lt,V6,q6,F6,Y6,jG,f3,MG,iS,N3,IG],(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 X6({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 $r=ee(ne());var zG=["animationElapsedTime","isAnimating","isEntrance","layout","isRange","stroke","connectNulls"],LG=["id","baseLine"];function If(){return If=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)?$r.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)?$r.createElement("rect",{x:0,y:le==null?[]:t===1?e.flatMap(r=>r.status==="removed"?[]:[r.next]):e.flatMap(r=>r.status==="matched"?[ac(ac({},r.next),{},{x:qn(r.prev.x,r.next.x,t),y:qn(r.prev.y,r.next.y,t)})]:r.status==="added"?[r.next]:[]),eP={activeDot:!0,animationBegin:0,animationDuration:1500,animationEasing:"ease",animationMatchBy:Nf,animationInterpolateFn:GG,connectNulls:!1,dot:!1,fill:"#3182bd",fillOpacity:.6,hide:!1,isAnimationActive:"auto",legendType:"line",stroke:"#3182bd",strokeWidth:1,type:"linear",label:!1,shape:Q6,xAxisId:0,yAxisId:0,zIndex:dt.area};function lg(e,t){return e&&e!=="none"?e:t}var XG=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:lg(n,a),value:P1(r,t),payload:e}]},ZG=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:ua,settings:{stroke:n,strokeWidth:a,fill:i,dataKey:t,nameKey:void 0,name:P1(o,t),hide:l,type:c,color:lg(n,i),unit:s,formatter:u,graphicalItemId:d}};return Ze.createElement(_D,{tooltipEntrySettings:f})});function QG(e){var t=e.clipPathId,r=e.points,n=e.props,a=n.needClip,i=n.dot,o=n.dataKey,l=gr(n);return Ze.createElement(FD,{points:r,dot:i,className:"recharts-area-dots",dotClassName:"recharts-area-dot",dataKey:o,baseProps:l,needClip:a,clipPathId:t})}function JG(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 ac(ac({},s),{},{value:i.value,payload:i.payload,parentViewBox:void 0,viewBox:s,fill:void 0})});return Ze.createElement(mD,{value:t?a:void 0},r)}function RG(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=R6(i,VG),m=Kt(b),g=ac(ac({},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(sr,{clipPath:n?"url(#clipPath-".concat(a,")"):void 0},Ze.createElement(ND,{option:v,DefaultShape:eP.shape,shapeProps:g})),Ze.createElement(QG,{points:t,props:b,clipPathId:a}))}function eX(e,t,r){if(ye(e)){var n=ye(t)?t:void 0;return qn(n,e,r)}if(Dt(e)||Mr(e)){var a=ye(t)?t:void 0;return qn(a,0,r)}return e}function tX(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,Ll.useMemo)(()=>({points:o,baseLine:l}),[o,l]),h=G0(v,i),b=vu(),m=TD(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)?_=_f(k,l,f):Array.isArray(l)?_=_f(null,l,f):_=null,Ze.createElement(MD,{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:eX(l,k,E),h.syncStepValue(I,E),Ze.createElement(JG,{showLabels:!g,points:o},n.children,Ze.createElement(RG,{points:M,baseLine:I,needClip:t,clipPathId:r,props:n,animationElapsedTime:E,isAnimating:g||E<1,isEntrance:L}),Ze.createElement(hD,{label:n.label}))})}function rX(e){var t=e.needClip,r=e.clipPathId,n=e.props,a=(0,Ll.useRef)(null),i=(0,Ll.useRef)();return Ze.createElement(tX,{needClip:t,clipPathId:r,props:n,previousPointsRef:a,previousBaselineRef:i})}var oS=class extends Ll.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=H6(n),x=g.r,A=g.strokeWidth,k=K0(n),_=x*2+A,M=s?"url(#clipPath-".concat(k?"":"dots-").concat(m,")"):void 0;return Ze.createElement(kr,{zIndex:h},Ze.createElement(sr,{className:b},s&&Ze.createElement("defs",null,Ze.createElement($6,{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(rX,{needClip:s,clipPathId:m,props:this.props})),Ze.createElement(Rw,{points:a,mainColor:lg(this.props.stroke,this.props.fill),itemDataKey:this.props.dataKey,activeDot:this.props.activeDot,clipPath:M}),this.props.isRange&&Array.isArray(v)&&Ze.createElement(Rw,{points:v,mainColor:lg(this.props.stroke,this.props.fill),itemDataKey:this.props.dataKey,activeDot:this.props.activeDot,clipPath:M}))}};function nX(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=R6(e,FG),m=ka(),g=T0(),x=aS(v,h),A=x.needClip,k=bt(),_=(t=Se(Q=>G6(Q,e.id,k)))!==null&&t!==void 0?t:{},M=_.points,E=_.isRange,L=_.baseLine,I=rc();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(oS,og({},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 aX=(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 X6(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=aX(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=Ct(_,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=Ct(_,a),Q=b&&R==null&&h!=null&&h.length>0&&h.every(O=>Ct(_,O)==null),y=X==null||b&&!r&&R==null||Q;if(g){var C;return{x:O1({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:O1({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 iX(e){var t=Pt(e,eP),r=bt();return Ze.createElement(ID,{id:t.id,type:"area"},n=>Ze.createElement(Ze.Fragment,null,Ze.createElement(CD,{legendPayload:XG(t)}),Ze.createElement(ZG,{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(HD,{type:"area",id:n,data:t.data,dataKey:t.dataKey,xAxisId:t.xAxisId,yAxisId:t.yAxisId,zAxisId:0,stackId:wO(t.stackId),hide:t.hide,barSize:void 0,baseValue:t.baseValue,isPanorama:r,connectNulls:t.connectNulls}),Ze.createElement(nX,og({},t,{id:n}))))}var Bl=Ze.memo(iX,mo);Bl.displayName="Area";var Ci=ee(ne()),Rn=ee(ne());var oX=["domain","range"],lX=["domain","range"];function tP(e,t){if(e==null)return{};var r,n,a=sX(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n{if(o!=null)return aP(aP({},i),{},{type:o})},[i,o]);return(0,Rn.useLayoutEffect)(()=>{l!=null&&(r.current===null?t(WD(l)):r.current!==l&&t(KD({prev:r.current,next:l})),r.current=l)},[l,t]),(0,Rn.useLayoutEffect)(()=>()=>{r.current&&(t(GD(r.current)),r.current=null)},[t]),null}var gX=e=>{var t=e.xAxisId,r=e.className,n=e.height,a=e.label,i=(0,Rn.useRef)(null),o=(0,Rn.useRef)(null),l=Se(Xv),s=bt(),u=Ve(),c="xAxis",d=Se(_=>x0(_,c,t,s)),f=Se(_=>vM(_,t)),p=Se(_=>hM(_,t)),v=Se(_=>L2(_,t));if((0,Rn.useLayoutEffect)(()=>{if(!(n!=="auto"||!f||Ml(a)||(0,Rn.isValidElement)(a)||v==null)){var _=i.current;if(_){var M=_.getCalculatedHeight();Math.round(f.height)!==Math.round(M)&&u(RD({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=sS(e,cX),x=v.id,A=v.scale,k=sS(v,dX);return Ci.createElement(jf,lS({},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}))},yX={allowDataOverflow:Xt.allowDataOverflow,allowDecimals:Xt.allowDecimals,allowDuplicatedCategory:Xt.allowDuplicatedCategory,angle:Xt.angle,axisLine:Jn.axisLine,height:Xt.height,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:Jn.tickLine,tickSize:Jn.tickSize,type:Xt.type,niceTicks:Xt.niceTicks,xAxisId:0},xX=e=>{var t=Pt(e,yX);return Ci.createElement(Ci.Fragment,null,Ci.createElement(hX,{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}),Ci.createElement(gX,t))},Ul=Ci.memo(xX,sg);Ul.displayName="XAxis";var Ei=ee(ne()),ea=ee(ne());var bX=["type"],wX=["dangerouslySetInnerHTML","ticks","scale"],SX=["id","scale"];function uS(){return uS=Object.assign?Object.assign.bind():function(e){for(var t=1;t{if(o!=null)return oP(oP({},i),{},{type:o})},[o,i]);return(0,ea.useLayoutEffect)(()=>{l!=null&&(r.current===null?t(XD(l)):r.current!==l&&t(ZD({prev:r.current,next:l})),r.current=l)},[l,t]),(0,ea.useLayoutEffect)(()=>()=>{r.current&&(t(QD(r.current)),r.current=null)},[t]),null}function kX(e){var t=e.yAxisId,r=e.className,n=e.width,a=e.label,i=(0,ea.useRef)(null),o=(0,ea.useRef)(null),l=Se(Xv),s=bt(),u=Ve(),c="yAxis",d=Se(_=>yM(_,t)),f=Se(_=>gM(_,t)),p=Se(_=>x0(_,c,t,s)),v=Se(_=>B2(_,t));if((0,ea.useLayoutEffect)(()=>{if(!(n!=="auto"||!d||Ml(a)||(0,ea.isValidElement)(a)||v==null)){var _=i.current;if(_){var M=_.getCalculatedWidth();Math.round(d.width)!==Math.round(M)&&u(JD({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=cS(e,wX),x=v.id,A=v.scale,k=cS(v,SX);return Ei.createElement(jf,uS({},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 OX={allowDataOverflow:Zt.allowDataOverflow,allowDecimals:Zt.allowDecimals,allowDuplicatedCategory:Zt.allowDuplicatedCategory,angle:Zt.angle,axisLine:Jn.axisLine,hide:!1,includeHidden:Zt.includeHidden,interval:Zt.interval,label:!1,minTickGap:Zt.minTickGap,mirror:Zt.mirror,orientation:Zt.orientation,padding:Zt.padding,reversed:Zt.reversed,scale:Zt.scale,tick:Zt.tick,tickCount:Zt.tickCount,tickLine:Jn.tickLine,tickSize:Jn.tickSize,type:Zt.type,niceTicks:Zt.niceTicks,width:Zt.width,yAxisId:0},TX=e=>{var t=Pt(e,OX);return Ei.createElement(Ei.Fragment,null,Ei.createElement(EX,{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}),Ei.createElement(kX,t))},$l=Ei.memo(TX,sg);$l.displayName="YAxis";var Wl=ee(ne()),zP=ee(ne());var bP=ee(ne()),wP=ee(ne());var MX=(e,t)=>t,zf=V([MX,lt,Eh,Gt,Cw,un,QM,Et],n8);function DX(e){return"getBBox"in e.currentTarget&&typeof e.currentTarget.getBBox=="function"}function Lf(e){var t=e.currentTarget.getBoundingClientRect(),r,n;if(DX(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 fS=rr("mouseClick"),pS=mi();pS.startListening({actionCreator:fS,effect:(e,t)=>{var r=e.payload,n=zf(t.getState(),Lf(r));n?.activeIndex!=null&&t.dispatch(kM({activeIndex:n.activeIndex,activeDataKey:void 0,activeCoordinate:n.activeCoordinate}))}});var ug=rr("mouseMove"),mS=mi(),ic=null,Hl=null,dS=null;mS.startListening({actionCreator:ug,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");ic!==null&&(cancelAnimationFrame(ic),ic=null),Hl!==null&&(typeof i!="number"||!l)&&(clearTimeout(Hl),Hl=null),dS=Lf(r);var s=()=>{var u=t.getState(),c=Ao(u,u.tooltip.settings.shared);if(!dS){ic=null,Hl=null;return}if(c==="axis"){var d=zf(u,dS);d?.activeIndex!=null?t.dispatch(S0({activeIndex:d.activeIndex,activeDataKey:void 0,activeCoordinate:d.activeCoordinate})):t.dispatch(w0())}ic=null,Hl=null};if(!l){s();return}i==="raf"?ic=requestAnimationFrame(s):typeof i=="number"&&Hl===null&&(Hl=setTimeout(s,i))}});function lP(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 sP={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},uP=nt({name:"rootProps",initialState:sP,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:sP.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}}}),cP=uP.reducer,dP=uP.actions.updateOptions;var PX=null,jX={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)},fP=nt({name:"polarOptions",initialState:PX,reducers:jX}),cxe=fP.actions.updatePolarOptions;var pP=fP.reducer;var vS=rr("keyDown"),hS=rr("focus"),gS=rr("blur"),Bf=mi(),oc=null,Vl=null,cg=null;Bf.startListening({actionCreator:vS,effect:(e,t)=>{cg=e.payload,oc!==null&&(cancelAnimationFrame(oc),oc=null);var r=t.getState(),n=r.eventSettings,a=n.throttleDelay,i=n.throttledEvents,o=i==="all"||i.includes("keydown");Vl!==null&&(typeof a!="number"||!o)&&(clearTimeout(Vl),Vl=null);var l=()=>{try{var s=t.getState(),u=s.rootProps.accessibilityLayer!==!1;if(!u)return;var c=s.tooltip.keyboardInteraction,d=cg;if(d!=="ArrowRight"&&d!=="ArrowLeft"&&d!=="Enter")return;var f=No(c,Si(s),xi(s),Co(s)),p=f==null?-1:Number(f),v=!Number.isFinite(p)||p<0,h=un(s),b=Si(s),m=Ao(s,s.tooltip.settings.shared);if(d==="Enter"){if(v)return;var g=gf(s,m,"hover",String(c.index));t.dispatch(vf({active:!c.active,activeIndex:c.index,activeCoordinate:g}));return}var x=xM(s),A=x==="left-to-right"?1:-1,k=d==="ArrowRight"?1:-1,_;if(v){var M=xi(s),E=Co(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(No(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=gf(s,m,"hover",String(_));t.dispatch(vf({active:!0,activeIndex:_.toString(),activeCoordinate:R}))}finally{oc=null,Vl=null}};if(!o){l();return}a==="raf"?oc=requestAnimationFrame(l):typeof a=="number"&&Vl===null&&(l(),cg=null,Vl=setTimeout(()=>{cg?l():(Vl=null,oc=null)},a))}});Bf.startListening({actionCreator:hS,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=Ao(r,r.tooltip.settings.shared),l=gf(r,o,"hover",String(i));t.dispatch(vf({active:!0,activeIndex:i,activeCoordinate:l}))}}}});Bf.startListening({actionCreator:gS,effect:(e,t)=>{var r=t.getState(),n=r.rootProps.accessibilityLayer!==!1;if(n){var a=r.tooltip.keyboardInteraction;a.active&&t.dispatch(vf({active:!1,activeIndex:a.index,activeCoordinate:a.coordinate}))}}});function dg(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 Mn=rr("externalEvent"),xS=mi(),fg=new Map,Uf=new Map,yS=new Map;xS.startListening({actionCreator:Mn,effect:(e,t)=>{var r=e.payload,n=r.handler,a=r.reactEvent;if(n!=null){var i=a.type,o=dg(a);yS.set(i,{handler:n,reactEvent:o});var l=fg.get(i);l!==void 0&&(cancelAnimationFrame(l),fg.delete(i));var s=t.getState(),u=s.eventSettings,c=u.throttleDelay,d=u.throttledEvents,f=d,p=f==="all"||f?.includes(i),v=Uf.get(i);v!==void 0&&(typeof c!="number"||!p)&&(clearTimeout(v),Uf.delete(i));var h=()=>{var g=yS.get(i);try{if(!g)return;var x=g.handler,A=g.reactEvent,k=t.getState(),_={activeCoordinate:Tw(k),activeDataKey:UM(k),activeIndex:kl(k),activeLabel:O0(k),activeTooltipIndex:kl(k),isTooltipActive:Mw(k)};x&&x(_,A)}finally{fg.delete(i),Uf.delete(i),yS.delete(i)}};if(!p){h();return}if(c==="raf"){var b=requestAnimationFrame(h);fg.set(i,b)}else if(typeof c=="number"){if(!Uf.has(i)){h();var m=setTimeout(h,c);Uf.set(i,m)}}else h()}}});var IX=V([wi],e=>e.tooltipItemPayloads),mP=V([IX,(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 bS=rr("touchMove"),wS=mi(),Fl=null,ko=null,vP=null,$f=null;wS.startListening({actionCreator:bS,effect:(e,t)=>{var r=e.payload;if(!(r.touches==null||r.touches.length===0)){$f=dg(r);var n=t.getState(),a=n.eventSettings,i=a.throttleDelay,o=a.throttledEvents,l=o==="all"||o.includes("touchmove");Fl!==null&&(cancelAnimationFrame(Fl),Fl=null),ko!==null&&(typeof i!="number"||!l)&&(clearTimeout(ko),ko=null),vP=Array.from(r.touches).map(u=>Lf({clientX:u.clientX,clientY:u.clientY,currentTarget:r.currentTarget}));var s=()=>{if($f!=null){var u=t.getState(),c=Ao(u,u.tooltip.settings.shared);if(c==="axis"){var d,f=(d=vP)===null||d===void 0?void 0:d[0];if(f==null){Fl=null,ko=null;return}var p=zf(u,f);p?.activeIndex!=null&&t.dispatch(S0({activeIndex:p.activeIndex,activeDataKey:void 0,activeCoordinate:p.activeCoordinate}))}else if(c==="item"){var v,h=$f.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(CO),g=(v=b.getAttribute(EO))!==null&&v!==void 0?v:void 0,x=_o(u).find(_=>_.id===g);if(m==null||x==null||g==null)return;var A=x.dataKey,k=mP(u,m,g);t.dispatch(EM({activeDataKey:A,activeIndex:m,activeCoordinate:k,activeGraphicalItemId:g}))}Fl=null,ko=null}};if(!l){s();return}i==="raf"?Fl=requestAnimationFrame(s):typeof i=="number"&&ko===null&&(s(),$f=null,ko=setTimeout(()=>{$f?s():(ko=null,Fl=null)},i))}}});var SS={throttleDelay:"raf",throttledEvents:["mousemove","touchmove","pointermove","scroll","wheel"]},hP=nt({name:"eventSettings",initialState:SS,reducers:{setEventSettings:(e,t)=>{t.payload.throttleDelay!=null&&(e.throttleDelay=t.payload.throttleDelay),t.payload.throttledEvents!=null&&(e.throttledEvents=$e(t.payload.throttledEvents))}}}),gP=hP.actions.setEventSettings;var yP=hP.reducer;var zX=Sv({brush:u6,cartesianAxis:e6,chartData:k8,errorBars:U6,eventSettings:yP,graphicalItems:$D,layout:gO,legend:YO,options:A8,polarAxis:xD,polarOptions:pP,referenceElements:f6,renderedTicks:O6,rootProps:cP,tooltip:OM,zIndex:f8}),xP=function(t){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:"Chart";return Rk({reducer:zX,preloadedState:t,middleware:n=>{var a;return n({serializableCheck:!1,immutableCheck:!["commonjs","es6","production"].includes((a="es6")!==null&&a!==void 0?a:"")}).concat([pS.middleware,mS.middleware,Bf.middleware,xS.middleware,wS.middleware])},enhancers:n=>{var a=n;return typeof n=="function"&&(a=n()),a.concat(A1({type:"raf"}))},devTools:Wn.devToolsEnabled&&{serialize:{replacer:lP},name:"recharts-".concat(r)}})};function SP(e){var t=e.preloadedState,r=e.children,n=e.reduxStoreName,a=bt(),i=(0,wP.useRef)(null);if(a)return r;i.current==null&&(i.current=xP(t,n));var o=xd;return bP.createElement(RO,{context:o,store:i.current},r)}var pg=ee(ne());function LX(e){var t=e.layout,r=e.margin,n=Ve(),a=bt();return(0,pg.useEffect)(()=>{a||(n(mO(t)),n(C1(r)))},[n,a,t,r]),null}var AP=(0,pg.memo)(LX,mo);var NP=ee(ne());function _P(e){var t=Ve();return(0,NP.useEffect)(()=>{t(dP(e))},[t,e]),null}var mg=ee(ne());var BX=e=>{var t=Ve();return(0,mg.useEffect)(()=>{t(gP(e))},[t,e]),null},CP=(0,mg.memo)(BX,mo);var ki=ee(ne()),PP=ee(ne());var Yl=ee(ne()),NS=ee(ne());var ql=ee(ne()),vg=ee(ne());function EP(e){var t=e.zIndex,r=e.isPanorama,n=(0,vg.useRef)(null),a=Ve();return(0,vg.useLayoutEffect)(()=>(n.current&&a(c8({zIndex:t,element:n.current,isPanorama:r})),()=>{a(d8({zIndex:t,isPanorama:r}))}),[a,t,r]),ql.createElement("g",{tabIndex:-1,ref:n,className:"recharts-zIndex-layer_".concat(t)})}function AS(e){var t=e.children,r=e.isPanorama,n=Se(i8);if(!n||n.length===0)return t;var a=n.filter(o=>o<0),i=n.filter(o=>o>0);return ql.createElement(ql.Fragment,null,a.map(o=>ql.createElement(EP,{key:o,zIndex:o,isPanorama:r})),t,i.map(o=>ql.createElement(EP,{key:o,zIndex:o,isPanorama:r})))}var UX=["children"];function $X(e,t){if(e==null)return{};var r,n,a=HX(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n{var r=Rv(),n=eh(),a=nh();if(!nn(r)||!nn(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),Yl.createElement(Eb,hg({},o,{title:l,desc:s,role:c,tabIndex:u,width:r,height:n,style:VX,ref:t}),i)}),qX=e=>{var t=e.children,r=Se(ol);if(!r)return null;var n=r.width,a=r.height,i=r.y,o=r.x;return Yl.createElement(Eb,{width:n,height:a,x:o,y:i},t)},_S=(0,NS.forwardRef)((e,t)=>{var r=e.children,n=$X(e,UX),a=bt();return a?Yl.createElement(qX,null,Yl.createElement(AS,{isPanorama:!0},r)):Yl.createElement(FX,hg({ref:t},n),Yl.createElement(AS,{isPanorama:!1},r))});var It=ee(ne()),We=ee(ne());var gg=ee(ne());function YX(e,t){return XX(e)||GX(e,t)||KX(e,t)||WX()}function WX(){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 KX(e,t){if(e){if(typeof e=="string")return kP(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)?kP(e,t):void 0}}function kP(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(hO(l))}},[n,e,i]),a}function TP(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 ZX(e){for(var t=1;te.length)&&(t=e.length);for(var r=0,n=Array(t);r(T8(),null);function xg(e){if(typeof e=="number")return e;if(typeof e=="string"){var t=parseFloat(e);if(!Number.isNaN(t))return t}return 0}var iZ=(0,We.forwardRef)((e,t)=>{var r,n,a=(0,We.useRef)(null),i=(0,We.useState)({containerWidth:xg((r=e.style)===null||r===void 0?void 0:r.width),containerHeight:xg((n=e.style)===null||n===void 0?void 0:n.height)}),o=yg(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]),It.createElement(It.Fragment,null,It.createElement(ul,{width:l.containerWidth,height:l.containerHeight}),It.createElement("div",Oo({ref:c},e)))}),oZ=(0,We.forwardRef)((e,t)=>{var r=e.width,n=e.height,a=(0,We.useState)({containerWidth:xg(r),containerHeight:xg(n)}),i=yg(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 It.createElement(It.Fragment,null,It.createElement(ul,{width:o.containerWidth,height:o.containerHeight}),It.createElement("div",Oo({ref:u},e)))}),lZ=(0,We.forwardRef)((e,t)=>{var r=e.width,n=e.height;return It.createElement(It.Fragment,null,It.createElement(ul,{width:r,height:n}),It.createElement("div",Oo({ref:t},e)))}),sZ=(0,We.forwardRef)((e,t)=>{var r=e.width,n=e.height;return typeof r=="string"||typeof n=="string"?It.createElement(oZ,Oo({},e,{ref:t})):typeof r=="number"&&typeof n=="number"?It.createElement(lZ,Oo({},e,{width:r,height:n,ref:t})):It.createElement(It.Fragment,null,It.createElement(ul,{width:r,height:n}),It.createElement("div",Oo({ref:t},e)))});function uZ(e){return e?iZ:sZ}var DP=(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=yg(M,2),L=E[0],I=E[1],j=(0,We.useState)(null),z=yg(j,2),X=z[0],R=z[1],Q=OP(),y=Od(),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=>{_(fS(ie)),_(Mn({handler:i,reactEvent:ie}))},[_,i]),T=(0,We.useCallback)(ie=>{_(ug(ie)),_(Mn({handler:u,reactEvent:ie}))},[_,u]),H=(0,We.useCallback)(ie=>{_(w0()),_(Mn({handler:c,reactEvent:ie}))},[_,c]),Y=(0,We.useCallback)(ie=>{_(ug(ie)),_(Mn({handler:d,reactEvent:ie}))},[_,d]),J=(0,We.useCallback)(()=>{_(hS())},[_]),fe=(0,We.useCallback)(()=>{_(gS())},[_]),pe=(0,We.useCallback)(ie=>{_(vS(ie.key))},[_]),ge=(0,We.useCallback)(ie=>{_(Mn({handler:o,reactEvent:ie}))},[_,o]),ze=(0,We.useCallback)(ie=>{_(Mn({handler:l,reactEvent:ie}))},[_,l]),se=(0,We.useCallback)(ie=>{_(Mn({handler:s,reactEvent:ie}))},[_,s]),Ne=(0,We.useCallback)(ie=>{_(Mn({handler:f,reactEvent:ie}))},[_,f]),F=(0,We.useCallback)(ie=>{_(Mn({handler:h,reactEvent:ie}))},[_,h]),$=(0,We.useCallback)(ie=>{A&&_(bS(ie)),_(Mn({handler:v,reactEvent:ie}))},[_,A,v]),q=(0,We.useCallback)(ie=>{_(Mn({handler:p,reactEvent:ie}))},[_,p]),ae=uZ(g);return It.createElement(Uw.Provider,{value:L},It.createElement(fE.Provider,{value:X},It.createElement(ae,{width:C??b?.width,height:O??b?.height,className:Le("recharts-wrapper",n),style:ZX({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:H,onMouseMove:Y,onMouseUp:Ne,onTouchEnd:q,onTouchMove:$,onTouchStart:F,ref:D},It.createElement(aZ,null),r)))});var cZ=["width","height","responsive","children","className","style","compact","title","desc"];function dZ(e,t){if(e==null)return{};var r,n,a=fZ(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=dZ(e,cZ),f=gr(d);return s?ki.createElement(ki.Fragment,null,ki.createElement(ul,{width:r,height:n}),ki.createElement(_S,{otherAttributes:f,title:u,desc:c},i)):ki.createElement(DP,{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},ki.createElement(_S,{otherAttributes:f,title:u,desc:c,ref:t},ki.createElement(m6,null,i)))});function CS(){return CS=Object.assign?Object.assign.bind():function(e){for(var t=1;tBP.createElement(LP,{chartName:"AreaChart",defaultTooltipEventType:"axis",validateTooltipEventTypes:xZ,tooltipPayloadSearcher:w8,categoricalChartProps:e,ref:t}));var _e=ee(ne());var ta=ee(ne()),bZ=/(\*\*[^*]+\*\*|\*[^*\n]+\*|`[^`]+`|\[[^\]]+\]\((?:https?:\/\/|\/)[^)\s]+\))/g,ES=(e,t)=>e.split(bZ).filter(Boolean).map((r,n)=>{let a=`${t}-${n}`;if(r.startsWith("**")&&r.endsWith("**"))return ta.default.createElement("strong",{key:a},r.slice(2,-2));if(r.startsWith("*")&&r.endsWith("*")&&r.length>2)return ta.default.createElement("em",{key:a},r.slice(1,-1));if(r.startsWith("`")&&r.endsWith("`"))return ta.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?ta.default.createElement("a",{key:a,href:i[2],target:"_blank",rel:"noopener noreferrer",className:"underline text-blue-500"},i[1]):r});function kS({text:e}){if(!e)return null;let t=[],r=String(e).split(` +`),n=0,a=0;for(;nta.default.createElement("li",{key:u},ES(s,`li${a}-${u}`)))));continue}if(i.trim()===""){t.push(ta.default.createElement("div",{key:a++,className:"h-2"})),n+=1;continue}t.push(ta.default.createElement("div",{key:a++},ES(i,`p${a}`))),n+=1}return ta.default.createElement("div",{className:"break-words"},t)}var cn=ee(ne()),wZ=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"},SZ=e=>Array.isArray(e.parameters)?e.parameters:e.parameters&&typeof e.parameters=="object"?Object.keys(e.parameters.properties||e.parameters):[];function OS({tool:e,darkMode:t}){let r=SZ(e);return cn.default.createElement("div",{style:{display:"grid",gap:"4px"}},e.description&&cn.default.createElement("div",{className:"text-sm",style:{color:t?"rgba(255,255,255,0.75)":"#4b5563"}},e.description),cn.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"}},cn.default.createElement("span",null,"params: ",r.length>0?r.join(", "):"none"),cn.default.createElement("span",null,"source: ",wZ(e))))}function TS({tools:e,darkMode:t}){let[r,n]=(0,cn.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 cn.default.createElement("div",{style:{marginTop:"8px"}},cn.default.createElement("div",{className:"flex items-center gap-1.5 flex-wrap font-mono"},cn.default.createElement("span",{className:"text-xs uppercase",style:{letterSpacing:"0.05em",color:t?"rgba(255,255,255,0.45)":"#9ca3af"}},"tools"),a.map(l=>cn.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&&cn.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"}`}},cn.default.createElement(OS,{tool:i,darkMode:t})))}var $P=e=>e==null?null:e<1e3?`${Math.round(e)}ms`:`${(e/1e3).toFixed(2)}s`,Hf=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}},AZ=["text","output","body","content"],HP=e=>{if(e==null)return null;if(typeof e=="object")return e;try{return JSON.parse(e)}catch{return null}},NZ=e=>{let t=HP(e.tool_result)??HP(e.content);if(!t||typeof t!="object"||Array.isArray(t))return null;let r=AZ.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}},_Z=e=>!!(e.tool_name||e.tool_call_id||e.tool_arguments||e.tool_result||(e.tool_calls||[]).length>0||e.duration_ms!=null||Hf(e.content)),FP=280,CZ=["tool","system","developer"],Vf=(e,t=FP)=>e.replace(/\s+/g," ").trim().slice(0,t),MS=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)}},qP=e=>{let t=e.tool_calls;return t?Array.isArray(t)?t.map(r=>({name:r.function?.name||r.name||e.tool_name,args:MS(r.function?.arguments??r.arguments??r.input)})):[{name:e.tool_name,args:MS(t)}]:[]},YP=e=>!!(e&&String(e).trim()&&String(e).trim()!=="\u2014"),EZ=e=>{let t=(e||[]).filter(Boolean),r=new Map,n=new Set;return t.forEach((a,i)=>{if(a.role!=="assistant"||YP(a.content))return;let o=qP(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))},VP=e=>CZ.includes(e.role)&&typeof e.content=="string"&&Vf(e.content).length>=FP,kZ=({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}}))},DS=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}),ra=(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 Oi({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=EZ(e);return _e.default.createElement("div",{className:"space-y-3"},l.map(s=>{let u=ra(s.role,t),c=!!n[s.id],d=VP(s)&&!c,f=_Z(s)||VP(s),p=Hf(s.tool_arguments),v=Hf(s.tool_result)||Hf(s.content),h=s.tool_calls&&(!Array.isArray(s.tool_calls)||s.tool_calls.length>0)?Hf(s.tool_calls):null,b=s.role==="tool"?NZ(s):null,m=s.tool_name?(r||[]).find(L=>L&&L.name===s.tool_name):null,g=s.role==="tool"?MS(s.tool_arguments):null,x=s.role==="tool"&&!!g,A=ra("assistant",t),k=ra("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"&&!YP(s.content)?qP(s):[],E=DS(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:")," ",Vf(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",Vf(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}},Vf(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,Vf(L.args,180),L.args.length>180?"\u2026":""):"\u2014"))):_e.default.createElement(kS,{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,$P(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(kZ,{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 ",$P(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(OS,{tool:m,darkMode:t}))))}))}var yt=ee(ne()),OZ={messages:"#2563eb",tool_results:"#5b84ec",instructions:"#85a3f0",tool_schemas:"#a6bcf4",mcp_schemas:"#c3d2f8",memory:"#dce4fb",output:"#7c3aed"},Ff=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},La=e=>{if(e==null)return 0;let t=typeof e=="string"?e:JSON.stringify(e);return Math.round(t.length/4)},sc=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 Ti({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,yt.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=`${sc(p)} / ${sc(t)} (${Math.round(v*100)}%)`,k=M=>M.key==="messages"&&h?h:M.color||OZ[M.key]||"#2563eb",_=M=>yt.default.createElement("div",{style:{display:"flex",height:M,borderRadius:"999px",overflow:"hidden",background:x,flex:1}},f.map(E=>yt.default.createElement("div",{key:E.key,title:`${E.label} \xB7 ${sc(E.tokens)}`,style:{width:`${Math.min(E.tokens/t*100,100)}%`,background:k(E)}})));return i?yt.default.createElement("span",{title:`${l}: ${A}`,style:{display:"inline-flex",alignItems:"center",gap:"6px",width:"132px",verticalAlign:"middle"}},_("6px"),yt.default.createElement("span",{style:{fontSize:"11px",fontFamily:"'SF Mono', 'Fira Code', monospace",color:h||b,whiteSpace:"nowrap"}},Math.round(v*100),"%")):yt.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"}},yt.default.createElement("div",{onClick:M=>{M.stopPropagation(),d(!c)},style:{display:"flex",alignItems:"center",justifyContent:"space-between",cursor:"pointer"}},yt.default.createElement("span",{style:{fontFamily:"'SF Mono', 'Fira Code', monospace",fontSize:"11px",fontWeight:600,letterSpacing:"0.06em",textTransform:"uppercase",color:m}},l,s&&yt.default.createElement("span",{style:{color:b,fontWeight:400}}," \xB7 estimated")),yt.default.createElement("span",{style:{display:"inline-flex",alignItems:"center",gap:"8px"}},yt.default.createElement("span",{style:{fontFamily:"'SF Mono', 'Fira Code', monospace",fontSize:"12px",fontWeight:600,color:h||g}},A),yt.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"}},">"))),yt.default.createElement("div",{style:{display:"flex",marginTop:"8px"}},_("9px")),v>=.9&&yt.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&&yt.default.createElement("div",{style:{marginTop:"10px",display:"grid",gap:"5px"}},[...f,{key:"__free",label:"Free space",tokens:Math.max(t-p,0)}].map(M=>yt.default.createElement("div",{key:M.key,style:{display:"flex",alignItems:"center",gap:"8px"}},yt.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"}}),yt.default.createElement("span",{style:{fontSize:"13px",color:g,flex:1}},M.label),yt.default.createElement("span",{style:{fontFamily:"'SF Mono', 'Fira Code', monospace",fontSize:"12px",color:m,width:"56px",textAlign:"right"}},sc(M.tokens)),yt.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)&&yt.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&&yt.default.createElement("span",null,yt.default.createElement("span",{style:{color:"#2563eb"}},"[=]")," cached prefix ",sc(n)),a>0&&yt.default.createElement("span",null,yt.default.createElement("span",{style:{color:"#7c3aed"}},"[~]")," thinking ",sc(a)))))}var na=ee(ne()),uc=[{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}],WP="30m",KP="activeagents.timeWindow",GP=(0,na.createContext)(null),PS=e=>uc.find(t=>t.id===e);function XP({children:e}){let[t,r]=(0,na.useState)(()=>{try{let i=localStorage.getItem(KP);if(i&&PS(i))return i}catch{}return WP}),n=(0,na.useCallback)(i=>{if(PS(i)){r(i);try{localStorage.setItem(KP,i)}catch{}}},[]),a=(0,na.useMemo)(()=>{let i=uc.findIndex(l=>l.id===t),o=uc[i]||PS(WP);return{timeWindow:o,windowIndex:i,windows:uc,setWindow:n,zoomTo:l=>{let s=uc[Math.min(Math.max(l,0),uc.length-1)];s&&n(s.id)},days:Math.max(1,Math.ceil(o.minutes/1440))}},[t,n]);return na.default.createElement(GP.Provider,{value:a},e)}function Ba(){let e=(0,na.useContext)(GP);if(!e)throw new Error("useTimeWindow must be used within a TimeWindowProvider");return e}var cc=ee(ne());function Ua({compact:e=!1}){let{windows:t,windowIndex:r,timeWindow:n,setWindow:a,zoomTo:i}=Ba(),{darkMode:o}=mt(),l=Ys(o),s={color:l.textSecondary};return cc.default.createElement("div",{className:"flex items-center rounded-lg p-1",style:{background:l.mutedBg}},cc.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"),cc.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=>cc.default.createElement("option",{key:u.id,value:u.id},u.label))),cc.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},"+"))}function rt(e=""){return`${(window.ACTIVE_AGENT_DASHBOARD?.mountPath||"").replace(/\/$/,"")}${e}`||"/"}var ZP=["#6366f1","#10b981","#f59e0b","#ec4899","#3b82f6","#8b5cf6","#14b8a6","#f97316"],TZ=3e4,MZ=(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))},DZ=e=>{let t={};return e.forEach((r,n)=>{t[r]=ZP[n%ZP.length]}),t},PZ=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)},jZ=(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 Kl({agentClass:e=null,embedded:t=!1}){let{darkMode:r}=mt(),[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}=Ba(),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,TZ);return()=>clearInterval(S)},[C]);let O=(0,w.useMemo)(()=>DZ(i),[i]);(0,w.useEffect)(()=>{j(null)},[y.id]);let D=(0,w.useMemo)(()=>jZ(n,i,y.minutes,y.bucketSeconds),[n,i,y]),N=Math.max(0,Math.ceil(D.length/6)-1),[T,H]=(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(Oi,{messages:Z,darkMode:P,tools:Mg(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]),$=(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]),ae=(0,w.useMemo)(()=>_.agent==="all"?$.map(S=>S.key):$.filter(S=>S.agent===_.agent).map(S=>S.key),[_.agent,$]),ie=(0,w.useMemo)(()=>PZ(Ne),[Ne]),G=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:br(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()}]},br=S=>S?S<.01?"<$0.01":`$${S.toFixed(2)}`:"\u2014",Mo=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"))),Gr=S=>M(P=>({...P,agent:P.agent===S?"all":S})),zt=S=>{let P=l[S];if(!P)return Gr(S);let W=rt(`/agents/${P}`);window.history.pushState(window.history.state,"",W),window.dispatchEvent(new CustomEvent("dashboard:navigate",{detail:{path:W}}))},dQ=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},iA=S=>S==null?null:`$${S.toFixed(4)}`,Og=S=>{switch(S){case"root":return lr.spans.root;case"prompt":return lr.spans.prompt;case"generate":return lr.spans.generate;case"llm":return lr.spans.llm;case"thinking":return lr.spans.thinking;case"tool":return lr.spans.tool;case"response":return lr.spans.response;default:return"-"}},Xl=S=>S.status!=="ERROR",oA=(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)}%`},gj=S=>{let P=S.tokens||{};return(P.input||0)+(P.output||0)+(P.thinking||0)},yj=S=>{if(typeof S!="string")return null;try{return JSON.stringify(JSON.parse(S),null,2)}catch{return null}},Tg=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(Lt=>Lt&&Lt.role==="user"&&Lt.content)?.content}catch{}}let je=W("llm.completion")||W("llm.output.message");return{input:Z,output:je}},Ha=(S,P=200)=>{let W=String(S).replace(/\s+/g," ").trim();return W.length>P?`${W.slice(0,P)}\u2026`:W},xj=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(vt=>vt&&vt.role==="user"&&vt.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 Lt=P["tool.input.args"]||P["tool.arguments"];W=typeof Lt=="string"?Lt:JSON.stringify(Lt),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}},lA=(S,P)=>{let W=xj(S),Z=Tg(P);return W.input&&W.input===Z.input&&(W.input=null),W.output&&W.output===Z.output&&(W.output=null),W},Zf=S=>{let P=S.spans||[],W=null;for(let Ge of P){let vt=Ge.tokens||{},Pe=(vt.input||0)+(vt.output||0);Pe>0&&(!W||Pe>W.total)&&(W={input:vt.input||0,output:vt.output||0,thinking:vt.thinking||0,cached:vt.cached||0,total:Pe})}if(!W)return null;let Z=Ge=>{for(let vt of P){let Pe=(vt.attributes||{})[Ge];if(Pe)return Pe}return null},je=La(Z("prompt.input.instructions")||Z("llm.instructions")),Te=La(Z("prompt.input.tools")||Z("llm.tools")),Je=La(Z("prompt.input.mcp_tools")),Me=0;for(let Ge of P){let vt=Ge.attributes||{},Pe=vt["tool.output.result"]||vt["tool.result"],et=vt["tool.input.args"]||vt["tool.arguments"];Pe&&(Me+=La(Pe)),et&&(Me+=La(et))}Me=Math.min(Me,W.input);let Lt=Math.max(W.input-je-Te-Je-Me,0);return{used:W.total,limit:Ff(S.model),cached:W.cached,thinking:W.thinking,segments:[{key:"messages",label:"Messages",tokens:Lt},{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}]}},sA=S=>S.endsWith(".instructions")?0:S.endsWith(".tools")?1:S.endsWith(".messages")?3:2,bj=S=>{let P={...S.attributes||{}},W=et=>{let Do=P[et];return delete P[et],Do},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 Lt=W("tool.input.args")||W("tool.arguments"),Ge=W("tool.output.result")||W("tool.result");if(Lt||Ge){let et=W("tool.name");je({role:"tool",tool_name:et||(S.name||"").replace(/^tool\./,""),tool_arguments:Lt,content:Ge?String(Ge):null})}let vt=W("llm.completion"),Pe=W("llm.output.message");return(vt||Pe)&&je({role:"assistant",content:String(vt||Pe)}),{messages:Z,rest:P}},Mg=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[]},uA=(S,P,W=[])=>{let{messages:Z,rest:je}=bj(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)=>sA(Pe[0])-sA(et[0])),Lt=P?"rgba(255,255,255,0.75)":"#4b5563",Ge=P?"rgba(255,255,255,0.45)":"#9ca3af",vt={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:Lt}},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"),gj(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=ra("tool",P).color,Do=Pe.tool_arguments==null?null:typeof Pe.tool_arguments=="string"?Pe.tool_arguments:JSON.stringify(Pe.tool_arguments);return w.default.createElement("div",{onClick:Jf=>Jf.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),Do&&w.default.createElement(w.default.Fragment,null," ",w.default.createElement("span",{style:{color:ra("assistant",P).color}},"in:")," ",w.default.createElement("span",{style:{color:Lt}},Do))),Pe.content&&w.default.createElement("div",{style:{fontSize:"12px"}},w.default.createElement("span",{style:{color:et}},"out:")," ",w.default.createElement("span",{style:{color:Lt,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(Oi,{messages:Z,darkMode:P,tools:Te||W})),Te&&w.default.createElement(TS,{tools:Te,darkMode:P}),Me.length>0&&w.default.createElement("div",{style:{marginTop:"6px",display:"grid",gap:"2px"}},Me.map(([Pe,et])=>{let Do=Pe.match(/args|result|input|output/)?yj(et):null,Jf=!Do&&(Pe.endsWith(".instructions")||Pe.match(/input|output|result/))&&typeof et=="string"?et:null,Dg=Do||Jf;if(!Dg)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 Pg=`${S.span_id}:${Pe}`,Rf=!!A[Pg];return w.default.createElement("div",{key:Pe},w.default.createElement("div",{onClick:jg=>{jg.stopPropagation(),k(fA=>({...fA,[Pg]:!fA[Pg]}))},title:Rf?"Collapse":"Expand",style:{cursor:"pointer",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},w.default.createElement("span",{style:{color:Ge}},Rf?"\u25BE":"\u25B8"," ",Pe,":")," ",!Rf&&w.default.createElement("span",{style:{color:Ge}},Ha(Dg,140))),Rf&&w.default.createElement("pre",{onClick:jg=>jg.stopPropagation(),style:Jf?{...vt,whiteSpace:"pre-wrap"}:vt},Dg))})))},wj=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)}},cA=(S,P)=>{let{generation:W,tools:Z,toolTotal:je,total:Te,overhead:Je}=wj(S);if(W===0&&je===0||W/Te>=.99&&je/Te<.01)return null;let Me=P?"rgba(255,255,255,0.5)":"#6b7280",Lt=P?"rgba(255,255,255,0.85)":"#374151",Ge=Pe=>`${(Pe/Te*100).toFixed(Pe/Te<.01?2:1)}%`,vt=(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"}},vt(W,"#ef4444"),vt(je,"#10b981"),Je>0&&vt(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:Lt}},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),")"))))},dA=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}},Og(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 ji={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)},Qf=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"),ae.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(mu,{width:"100%",height:"100%"},w.default.createElement(lc,{data:D,onClick:G,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(zl,{strokeDasharray:"3 3",stroke:"rgba(255,255,255,0.1)"}),w.default.createElement(Ul,{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($l,{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(Xu,{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(Bl,{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"}},ji.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"}},ji.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"}},ji.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"}},ji.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"}},Mo,w.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit, minmax(280px, 1fr))",gap:"16px"}},F.map(S=>w.default.createElement(Fs,{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:()=>zt(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(),Gr(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"}},$.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"&&dA(!0),z==="timeline"&&!Qf&&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=Zf(S);return P?w.default.createElement(Ti,{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"})," ",iA(S.estimated_cost)),w.default.createElement("span",{className:`meta-item ${Xl(S)?"success":"error"}`},w.default.createElement("i",{className:`fa-solid ${Xl(S)?"fa-check":"fa-xmark"}`})," ",Xl(S)?"OK":"ERROR"))),(()=>{let P=Tg(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:")," ",Ha(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:")," ",Ha(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}`},Og(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"}},oA(P,S))),(()=>{if(Je)return null;let Me=lA(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:ra(Me.inputTone,!0).color}},Me.inputLabel)," ",Ha(Me.input,140)),Me.output&&w.default.createElement("div",{style:{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},w.default.createElement("span",{style:{color:ra(Me.outputTone,!0).color}},Me.outputLabel)," ",Ha(Me.output,140)))})(),Je&&uA(P,!0,Mg(S)))}),(()=>{let P=Zf(S);return P?w.default.createElement("div",{style:{margin:"10px 0"},onClick:W=>W.stopPropagation()},w.default.createElement(Ti,{...P,label:"Context pressure",estimated:!0,darkMode:!0})):null})(),cA(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)))))),Qf&&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(Ua,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"),ae.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(mu,{width:"100%",height:"100%"},w.default.createElement(lc,{data:D,onClick:G,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(zl,{strokeDasharray:"3 3",stroke:"#e5e7eb"}),w.default.createElement(Ul,{dataKey:"time",stroke:"#9ca3af",tick:{fontSize:10,fill:"#6b7280"},tickLine:!1,interval:N}),w.default.createElement($l,{stroke:"#9ca3af",tick:{fontSize:10,fill:"#6b7280"},tickLine:!1,axisLine:!1,allowDecimals:!1}),w.default.createElement(Xu,{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(Bl,{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"},ji.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"},ji.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"},ji.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"},ji.throughput,"/min"),w.default.createElement("div",{className:"text-xs text-gray-500"},"Throughput")))),z==="agents"&&w.default.createElement("div",null,Mo,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(Fs,{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:()=>zt(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(),Gr(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"},$.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"&&dA(!1),z==="timeline"&&!Qf&&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=Zf(S);return P?w.default.createElement(Ti,{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"}),iA(S.estimated_cost)),w.default.createElement("span",{className:`text-sm ${Xl(S)?"text-green-600":"text-red-600"}`},w.default.createElement("i",{className:`fa-solid ${Xl(S)?"fa-check":"fa-xmark"} mr-1`}),Xl(S)?"OK":"ERROR"))),(()=>{let P=Tg(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:")," ",Ha(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:")," ",Ha(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(),H(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,Lt=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(Lt?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"}`},Og(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}%`:`${MZ(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}},oA(P,S)),(()=>{if(Lt)return null;let Ge=lA(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:ra(Ge.inputTone,!1).color}},Ge.inputLabel)," ",Ha(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:ra(Ge.outputTone,!1).color}},Ge.outputLabel)," ",Ha(Ge.output,160)))})()),Lt&&uA(P,!1,Mg(S)))}))),(()=>{let P=Zf(S);return P?w.default.createElement("div",{className:"mt-3",onClick:W=>W.stopPropagation()},w.default.createElement(Ti,{...P,label:"Context pressure",estimated:!0})):null})(),cA(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)))))),Qf&&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=ee(ne());var ar=ee(ne());var QP={root:"#9ca3af",prompt:"#60a5fa",generate:"#a855f7",llm:"#ef4444",thinking:"#fbbf24",tool:"#22c55e",response:"#2dd4bf",embedding:"#818cf8"},jS=e=>e==null?"":e<1e3?`${Math.round(e)}ms`:`${(e/1e3).toFixed(2)}s`;function IS({traceId:e,darkMode:t}){let[r,n]=(0,ar.useState)(null),[a,i]=(0,ar.useState)(!1),[o,l]=(0,ar.useState)(!1);if((0,ar.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 ar.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 ar.default.createElement("div",{className:"mt-1",onClick:p=>p.stopPropagation()},ar.default.createElement("div",{onClick:()=>l(!o),title:o?"Collapse spans":`${c.length} spans \xB7 ${jS(r.duration_ms)} \u2014 click to expand`,style:{display:"flex",alignItems:"center",gap:"8px",cursor:"pointer"}},ar.default.createElement("div",{style:{position:"relative",height:"8px",borderRadius:"999px",background:s,overflow:"hidden",flex:1}},f.map((p,v)=>ar.default.createElement("div",{key:p.span_id||v,title:`${p.name} \xB7 ${jS(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":QP[p.type]||"#9ca3af"}}))),ar.default.createElement("span",{className:"font-mono",style:{fontSize:"10px",color:u,whiteSpace:"nowrap"}},c.length," spans ",o?"\u25BE":"\u25B8")),o&&ar.default.createElement("div",{style:{display:"grid",gap:"3px",marginTop:"6px"}},c.map((p,v)=>ar.default.createElement("div",{key:p.span_id||v,style:{display:"flex",alignItems:"center",gap:"8px",paddingLeft:`${(p.nested||0)*10}px`}},ar.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),ar.default.createElement("div",{style:{position:"relative",height:"4px",borderRadius:"2px",background:s,flex:1}},ar.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":QP[p.type]||"#9ca3af"}})),ar.default.createElement("span",{className:"font-mono",style:{fontSize:"10px",color:u,width:"52px",textAlign:"right",flexShrink:0}},jS(p.duration)))),ar.default.createElement("a",{href:rt(`/traces?trace=${r.trace_id||e}`),className:"font-mono hover:underline",style:{fontSize:"10px",color:"#3b82f6",justifySelf:"start"}},"open in Traces \u2192")))}var IZ=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=La(e?.instructions),n=0;(e?.messages||[]).forEach(i=>{i.role==="tool"&&(n+=La(i.content)+La(i.tool_arguments))}),n=Math.min(n,t.input);let a=Math.max(t.input-r-n,0);return{used:t.total,limit:Ff(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}]}},JP=["#6366f1","#10b981","#f59e0b","#ec4899","#3b82f6","#8b5cf6","#14b8a6","#f97316"],zZ=[{id:"recent",label:"Recent"},{id:"tokens",label:"Tokens"},{id:"messages",label:"Messages"}],LZ=3e4,Mi=e=>e==null?"0":e>=1e6?(e/1e6).toFixed(1)+"M":e>=1e3?(e/1e3).toFixed(1)+"K":e.toString(),BZ=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 Di({agentId:e=null,embedded:t=!1}){let{darkMode:r}=mt(),[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}=Ba(),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,LZ);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,JP[X%JP.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"),zZ.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(Ua,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(mu,{width:"100%",height:"100%"},ce.default.createElement(lc,{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(zl,{strokeDasharray:"3 3",stroke:r?"#2a2a2a":"#e5e7eb"}),ce.default.createElement(Ul,{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($l,{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(Xu,{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(Bl,{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:Mi(g.tokens)},{label:"Avg / interaction",value:Mi(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 ${Mi(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,Mi(z.tokens?.total)," tokens"),(z.tokens?.total||0)>0&&ce.default.createElement(Ti,{compact:!0,darkMode:r,label:"Context",used:(z.tokens?.input||0)+(z.tokens?.output||0)||z.tokens?.total,limit:Ff(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}},BZ(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(Oi,{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?`${Mi(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:`${Mi(Q.tokens.thinking)} thinking tokens`},"\u{1F9E0} ",Mi(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:",Mi(Q.tokens.input)),ce.default.createElement("span",{className:"text-green-600"},"out:",Mi(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:rt(`/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(IS,{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=IZ(X);return Q?ce.default.createElement("div",{className:"pt-3 mt-2 border-t",style:{borderColor:E.cardBorder}},ce.default.createElement(Ti,{...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 re=ee(ne());var zS=[{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}}],LS=[{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}}],RP=()=>document.querySelector('meta[name="csrf-token"]')?.content,UZ=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`},bg=e=>e>=.85?"high":e>=.7?"medium":"low";function qf({embedded:e=!1,agentId:t=null}){let{darkMode:r}=mt(),[n,a]=(0,re.useState)([]),[i,o]=(0,re.useState)([]),[l,s]=(0,re.useState)(!0),[u,c]=(0,re.useState)(null),[d,f]=(0,re.useState)(null),[p,v]=(0,re.useState)(!1),[h,b]=(0,re.useState)(null),[m,g]=(0,re.useState)({agent_id:t?String(t):"",name:"",sample_size:20,criteria:zS.map(N=>N.key),containsPattern:"",llmJudgePrompt:"",judgeKind:"manual",judgeModel:"",compareModels:""}),[x,A]=(0,re.useState)(null),[k,_]=(0,re.useState)(!1),M=(0,re.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,re.useEffect)(()=>{M(),fetch("/api/agents").then(N=>N.ok?N.json():{agents:[]}).then(N=>o(N.agents||[])).catch(()=>o([]))},[M]);let E=()=>{let N=[...zS,...LS].filter(T=>m.criteria.includes(T.key)).map(({key:T,type:H,config:Y})=>({key:T,type:H,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":RP()},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()}})}),H=await T.json();if(!T.ok)throw new Error((H.errors||[H.error]).filter(Boolean).join(", ")||"Failed to create evaluation");v(!1),g({...m,name:""}),await M(),f(H.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":RP()}}),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 re.default.createElement("div",{className:"flex items-center justify-center h-64"},re.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:H=!1,mono:Y=!1}={})=>re.default.createElement("div",{key:N,className:"flex items-center gap-4",style:H?{paddingLeft:"16px"}:void 0},re.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"&&re.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?re.default.createElement("div",{className:"flex-1 text-xs italic",style:{color:j.textMuted},title:T.reason},"skipped \u2014 ",T.reason):re.default.createElement(re.default.Fragment,null,re.default.createElement("div",{className:"flex-1 h-2 rounded-full overflow-hidden",style:{background:j.trackBg}},re.default.createElement("div",{className:"h-full rounded-full transition-all",style:{width:`${T.score*100}%`,background:z[bg(T.score)]}})),re.default.createElement("div",{className:"w-12 text-sm font-medium text-right",style:{color:z[bg(T.score)]},title:`min ${T.min} \xB7 max ${T.max} \xB7 ${T.passed}/${T.total} passed`},T.score.toFixed(2))));return re.default.createElement("div",{className:"space-y-6"},re.default.createElement("div",{className:`flex items-center ${e?"justify-end":"justify-between"}`},!e&&re.default.createElement("div",null,re.default.createElement("h1",{className:"text-2xl font-bold",style:{color:j.textPrimary}},"Evaluations"),re.default.createElement("p",{className:"text-sm mt-1",style:{color:j.textSecondary}},"Score outputs with LLM-as-judge, rule-based checks, or custom criteria")),re.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&&re.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&&re.default.createElement("form",{onSubmit:L,className:"rounded-xl border p-5 space-y-4",style:{backgroundColor:j.cardBg,borderColor:j.cardBorder}},re.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4"},re.default.createElement("div",null,re.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Agent"),re.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&&re.default.createElement("option",{value:""},"Select agent\u2026"),i.map(N=>re.default.createElement("option",{key:N.id,value:N.id},N.name)))),re.default.createElement("div",null,re.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Name"),re.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%"}})),re.default.createElement("div",null,re.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Sample size"),re.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%"}}))),re.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4"},re.default.createElement("div",null,re.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"KPI definition"),re.default.createElement("select",{value:m.judgeKind,onChange:N=>g({...m,judgeKind:N.target.value}),style:{...C,width:"100%"}},re.default.createElement("option",{value:"manual"},"Manual criteria"),re.default.createElement("option",{value:"judge_defined"},"Judge defines KPIs from agent goals"))),re.default.createElement("div",null,re.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Judge model (optional)"),re.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%"}})),re.default.createElement("div",null,re.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Compare models (optional, comma-separated)"),re.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"&&re.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"&&re.default.createElement(re.default.Fragment,null,re.default.createElement("div",null,re.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-2",style:{color:j.textMuted}},"Rule-based criteria (sampled generations)"),re.default.createElement("div",{className:"flex flex-wrap gap-3"},zS.map(N=>re.default.createElement("label",{key:N.key,className:"flex items-center gap-2 text-sm",style:{color:j.textPrimary}},re.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(H=>H!==N.key)})}),N.label)))),re.default.createElement("div",null,re.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-2",style:{color:j.textMuted}},"Telemetry criteria (trace aggregates)"),re.default.createElement("div",{className:"flex flex-wrap gap-3"},LS.map(N=>re.default.createElement("label",{key:N.key,className:"flex items-center gap-2 text-sm",style:{color:j.textPrimary}},re.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(H=>H!==N.key)})}),N.label)))),re.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4"},re.default.createElement("div",null,re.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"Must contain (optional pattern)"),re.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%"}})),re.default.createElement("div",null,re.default.createElement("label",{className:"block text-xs uppercase tracking-wide mb-1",style:{color:j.textMuted}},"LLM judge criterion (optional, needs provider credentials)"),re.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&&re.default.createElement("div",{className:"text-sm text-red-500"},x),re.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")),re.default.createElement("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4"},re.default.createElement("div",{className:"rounded-xl p-5 border shadow-sm",style:{backgroundColor:j.cardBg,borderColor:j.cardBorder}},re.default.createElement("div",{className:"text-sm mb-1",style:{color:j.textSecondary}},"Total Evaluations"),re.default.createElement("div",{className:"text-3xl font-bold",style:{color:j.textPrimary}},X.length)),re.default.createElement("div",{className:"rounded-xl p-5 border shadow-sm",style:{backgroundColor:j.cardBg,borderColor:j.cardBorder}},re.default.createElement("div",{className:"text-sm mb-1",style:{color:j.textSecondary}},"Average Score"),re.default.createElement("div",{className:"text-3xl font-bold",style:{color:Q==null?j.textMuted:z[bg(Q)]}},Q==null?"\u2014":`${(Q*100).toFixed(0)}%`)),re.default.createElement("div",{className:"rounded-xl p-5 border shadow-sm",style:{backgroundColor:j.cardBg,borderColor:j.cardBorder}},re.default.createElement("div",{className:"text-sm mb-1",style:{color:j.textSecondary}},"Samples Evaluated"),re.default.createElement("div",{className:"text-3xl font-bold",style:{color:j.textPrimary}},y))),re.default.createElement("div",{className:"space-y-4"},X.map(N=>{let T=N.latest_run,H=d===N.id,Y=(N.criteria||[]).some(J=>LS.some(fe=>fe.key===J.key));return re.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}},re.default.createElement("div",{className:"flex items-center justify-between p-4 cursor-pointer",onClick:()=>f(H?null:N.id)},re.default.createElement("div",{className:"flex items-center gap-3 min-w-0"},re.default.createElement("span",{className:"px-2 py-1 text-xs font-medium bg-purple-100 text-purple-700 rounded flex-shrink-0"},"EVALUATION"),re.default.createElement("span",{className:"font-medium truncate",style:{color:j.textPrimary}},N.name),re.default.createElement("span",{className:"text-sm truncate",style:{color:j.textSecondary}},N.agent?.name)),re.default.createElement("div",{className:"flex items-center gap-4 flex-shrink-0 text-sm",style:{color:j.textSecondary}},T?.status==="failed"&&re.default.createElement("span",{className:"text-red-500"},"failed"),T?.status==="complete"&&T.average_score!=null&&re.default.createElement("span",{style:{color:z[bg(T.average_score)],fontWeight:600}},(T.average_score*100).toFixed(0),"%"),re.default.createElement("span",{style:{color:j.textMuted}},UZ(T?.completed_at||N.created_at)))),H&&re.default.createElement("div",{className:"border-t",style:{borderColor:j.cardBorder}},T?.status==="failed"?re.default.createElement("div",{className:"p-4 text-sm text-red-500"},T.error_message):T?.scores?re.default.createElement("div",{className:"p-4 space-y-3"},T.scores._verdict&&re.default.createElement("div",{className:"p-3 rounded-lg text-sm",style:{background:r?"rgba(34,197,94,0.1)":"#f0fdf4"}},re.default.createElement("span",{className:"font-semibold",style:{color:"#16a34a"}},"Winner: ",T.scores._verdict.winner),re.default.createElement("span",{className:"ml-2",style:{color:j.textSecondary}},T.scores._verdict.rationale),re.default.createElement("span",{className:"ml-2 text-xs",style:{color:j.textMuted}},"judged by ",T.scores._verdict.judge)),T.scores._missing_models&&re.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)?re.default.createElement("div",{key:J,className:"space-y-1"},re.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))):re.default.createElement("div",{className:"p-4 text-sm",style:{color:j.textMuted}},"No runs yet"),re.default.createElement("div",{className:"p-4 grid grid-cols-2 md:grid-cols-5 gap-4 text-sm",style:{background:j.innerBg}},re.default.createElement("div",null,re.default.createElement("div",{style:{color:j.textMuted}},"Judge"),re.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")),re.default.createElement("div",{className:"col-span-2"},re.default.createElement("div",{style:{color:j.textMuted}},"Criteria"),re.default.createElement("div",{className:"font-medium truncate",style:{color:j.textPrimary}},(N.criteria||[]).map(J=>J.key).join(", "))),re.default.createElement("div",null,re.default.createElement("div",{style:{color:j.textMuted}},"Samples"),re.default.createElement("div",{className:"font-medium",style:{color:j.textPrimary}},T?`${T.samples_passed} / ${T.samples_evaluated} passed`:"\u2014")),re.default.createElement("div",{className:"flex items-end justify-end"},re.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&&re.default.createElement("div",{className:"text-center py-12 rounded-xl border",style:{backgroundColor:j.cardBg,borderColor:j.cardBorder}},re.default.createElement("div",{className:"text-lg",style:{color:j.textMuted}},"No evaluations yet"),re.default.createElement("p",{className:"text-sm mt-2",style:{color:j.textSecondary}},"Create an evaluation to start scoring agent outputs")))}var me=ee(ne());var $Z=[{id:"overview",label:"Overview"},{id:"traces",label:"Traces"},{id:"interactions",label:"Interactions"}];function BS({agent:e,onBack:t,embedded:r=!1}){let{darkMode:n}=mt(),a=Ys(n),[i,o]=(0,me.useState)(null),[l,s]=(0,me.useState)(!0),{timeWindow:u,days:c}=Ba(),[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(Ua,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(Ua,null))),!r&&me.default.createElement("div",{className:"flex rounded-lg p-1 w-fit",style:{background:a.mutedBg}},$Z.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(Kl,{agentClass:p,embedded:!0}),x==="interactions"&&me.default.createElement(Di,{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(wg,{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(wg,{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(wg,{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(wg,{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 ej={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 wg({title:e,value:t,icon:r,color:n,colors:a,darkMode:i}){let[o,l,s]=ej[n]||ej.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 HZ=[{id:"config",label:"Configuration"},{id:"instructions",label:"Instructions"},{id:"tools",label:"Tools"},{id:"versions",label:"Versions"},{id:"code",label:"Code"}],nj=[{id:"traces",label:"Traces"},{id:"metrics",label:"Metrics"},{id:"interactions",label:"Interactions"},{id:"evals",label:"Evals"},{id:"feedback",label:"Feedback"}],VZ=nj.map(e=>e.id),tj={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"]}},FZ={active:"success",draft:"warning",archived:"neutral",failed:"error",trial:"info"};function US({tone:e="neutral",mono:t=!1,darkMode:r,children:n}){let[a,i]=(tj[e]||tj.neutral)[r?"dark":"light"];return K.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 Gl({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:sa,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 K.default.createElement("button",{type:"button",onClick:r,disabled:n,style:{...l,...s[e]||s.secondary}},i)}function rj({tabs:e,active:t,onChange:r,colors:n}){return K.default.createElement("div",{style:{display:"flex",alignItems:"center"}},e.map(a=>{let i=t===a.id;return K.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?sa:"transparent"}`,color:i?sa:n.textSecondary,fontSize:"13px",fontWeight:i?600:500,cursor:"pointer",whiteSpace:"nowrap"}},a.label)}))}function Sg({agent:e,meta:t,onSave:r,onDelete:n,onRun:a,onDuplicate:i,onRunReport:o,onBack:l,isLoading:s,initialTab:u}){let{darkMode:c}=mt(),d=Ys(c),[f,p]=(0,K.useState)(u||"config"),[v,h]=(0,K.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,K.useState)([]),[g,x]=(0,K.useState)(!1),[A,k]=(0,K.useState)(""),[_,M]=(0,K.useState)(qs),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,K.useEffect)(()=>{let Q=!1;return Mm(v.provider).then(y=>{Q||y.length===0||M(C=>({...C,[v.provider]:y}))}),()=>{Q=!0}},[v.provider]),(0,K.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,K.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=VZ.includes(f);return K.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"20px"}},K.default.createElement("div",{style:{display:"flex",alignItems:"flex-start",gap:"12px"}},K.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"}},"<-"),K.default.createElement(Mt,{size:44}),K.default.createElement("div",{style:{flex:1,minWidth:0}},K.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"8px",flexWrap:"wrap"}},K.default.createElement("h1",{style:{fontSize:"20px",fontWeight:700,color:d.textPrimary,margin:0}},v.name||"Untitled agent"),K.default.createElement(US,{tone:FZ[v.status]||"neutral",darkMode:c},v.status),g&&K.default.createElement(US,{tone:"info",darkMode:c},"unsaved")),K.default.createElement("p",{style:{fontSize:"13px",color:d.textSecondary,margin:"4px 0 0"}},v.description||"No description")),K.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"8px",flexShrink:0}},i&&K.default.createElement(Gl,{variant:"secondary",colors:d,onClick:i},"Duplicate"),K.default.createElement(Gl,{variant:"primary",colors:d,onClick:a},"Run Agent"))),K.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"20px",borderBottom:`1px solid ${d.cardBorder}`,overflowX:"auto"}},K.default.createElement(rj,{tabs:HZ,active:f,onChange:p,colors:d}),K.default.createElement("div",{style:{width:"1px",height:"18px",background:d.borderStrong,flexShrink:0}}),K.default.createElement(rj,{tabs:nj,active:f,onChange:p,colors:d})),R?K.default.createElement("div",null,f==="traces"&&K.default.createElement(Kl,{agentClass:E,embedded:!0}),f==="metrics"&&K.default.createElement(BS,{agent:e,embedded:!0}),f==="interactions"&&K.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"12px"}},o&&K.default.createElement("div",{style:{display:"flex",justifyContent:"flex-end"}},K.default.createElement("button",{type:"button",onClick:o,style:{background:"none",border:"none",cursor:"pointer",fontFamily:Oe.mono,fontSize:"11px",color:"#3b82f6"}},"run report ->")),K.default.createElement(Di,{agentId:e.id,embedded:!0})),f==="evals"&&K.default.createElement(qf,{embedded:!0,agentId:e.id}),f==="feedback"&&K.default.createElement(XZ,{colors:d})):K.default.createElement("div",{style:{background:d.cardBg,border:`1px solid ${d.cardBorder}`,borderRadius:"12px",padding:"20px"}},f==="config"&&K.default.createElement(qZ,{formData:v,updateField:j,providerModels:_,colors:d,darkMode:c,onDelete:n}),f==="instructions"&&K.default.createElement(YZ,{formData:v,updateField:j,meta:t,toggleArrayItem:z,colors:d}),f==="tools"&&K.default.createElement(WZ,{formData:v,meta:t,toggleArrayItem:z,colors:d}),f==="versions"&&K.default.createElement(KZ,{versions:b,agentId:e.id,onRestore:L,colors:d,darkMode:c}),f==="code"&&K.default.createElement(GZ,{code:A,colors:d})),g&&K.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)"}},K.default.createElement("span",{style:{fontSize:"13px",color:d.textSecondary}},"Unsaved changes"),K.default.createElement("div",{style:{display:"flex",gap:"8px"}},K.default.createElement(Gl,{variant:"primary",size:"sm",colors:d,onClick:X,disabled:s},s?"Saving\u2026":"Save Changes"))))}var $a=e=>({display:"block",fontSize:"13px",fontWeight:500,color:e.textCell,marginBottom:"6px"}),Pi=e=>({width:"100%",padding:"8px 12px",borderRadius:"8px",border:`1px solid ${e.inputBorder}`,background:e.inputBg,color:e.textPrimary,fontFamily:"inherit",fontSize:"13px"}),aj=e=>({fontFamily:Oe.mono,fontSize:"11px",fontWeight:600,letterSpacing:"0.06em",textTransform:"uppercase",color:e.textSecondary});function qZ({formData:e,updateField:t,providerModels:r,colors:n,darkMode:a,onDelete:i}){return K.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"20px"}},K.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"16px"}},K.default.createElement("div",null,K.default.createElement("label",{style:$a(n)},"Agent Name"),K.default.createElement("input",{type:"text",value:e.name,onChange:o=>t("name",o.target.value),className:"aa-field",style:Pi(n)})),K.default.createElement("div",null,K.default.createElement("label",{style:$a(n)},"Preset Type"),K.default.createElement("select",{value:e.preset_type,onChange:o=>t("preset_type",o.target.value),className:"aa-field",style:Pi(n)},Object.keys(Vs).map(o=>K.default.createElement("option",{key:o,value:o},o))))),K.default.createElement("div",null,K.default.createElement("label",{style:$a(n)},"Description"),K.default.createElement("textarea",{value:e.description,onChange:o=>t("description",o.target.value),rows:3,className:"aa-field",style:{...Pi(n),resize:"vertical"}})),K.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"16px"}},K.default.createElement("div",null,K.default.createElement("label",{style:$a(n)},"Provider"),K.default.createElement("select",{value:e.provider,onChange:o=>{t("provider",o.target.value),t("model",r[o.target.value][0])},className:"aa-field",style:Pi(n)},Object.keys(r).map(o=>K.default.createElement("option",{key:o,value:o},o.charAt(0).toUpperCase()+o.slice(1))))),K.default.createElement("div",null,K.default.createElement("label",{style:$a(n)},"Model"),K.default.createElement("select",{value:e.model,onChange:o=>t("model",o.target.value),className:"aa-field",style:{...Pi(n),fontFamily:Oe.mono}},(r[e.provider]?.includes(e.model)?r[e.provider]:[e.model,...r[e.provider]||[]]).filter(Boolean).map(o=>K.default.createElement("option",{key:o,value:o},o))))),K.default.createElement("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"16px"}},K.default.createElement("div",null,K.default.createElement("label",{style:$a(n)},"Temperature"," ",K.default.createElement("span",{style:{fontFamily:Oe.mono,color:n.textSecondary}},e.model_config.temperature)),K.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:sa}})),K.default.createElement("div",null,K.default.createElement("label",{style:$a(n)},"Status"),K.default.createElement("select",{value:e.status,onChange:o=>t("status",o.target.value),className:"aa-field",style:Pi(n)},K.default.createElement("option",{value:"draft"},"Draft"),K.default.createElement("option",{value:"active"},"Active"),K.default.createElement("option",{value:"archived"},"Archived")))),i&&K.default.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",gap:"12px",borderTop:`1px solid ${n.cardBorder}`,paddingTop:"16px"}},K.default.createElement("div",null,K.default.createElement("div",{style:aj(n)},"Danger zone"),K.default.createElement("p",{style:{fontSize:"13px",color:n.textMuted,margin:"4px 0 0"}},"Deleting an agent removes its configuration and version history.")),K.default.createElement(Gl,{variant:"danger",size:"sm",colors:n,onClick:i},"Delete Agent")))}function YZ({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 K.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"20px"}},K.default.createElement("div",null,K.default.createElement("label",{style:$a(a)},"System Instructions"),K.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:{...Pi(a),fontFamily:Oe.mono,resize:"vertical"}})),K.default.createElement("div",null,K.default.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"6px"}},K.default.createElement("label",{style:{...$a(a),marginBottom:0}},"Action Prompts"),K.default.createElement(Gl,{variant:"secondary",size:"sm",colors:a,onClick:l},"+ Add action")),K.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 ",K.default.createElement("span",{style:{fontFamily:Oe.mono}},"#ask")," action uses the system instructions alone."),i.length>0&&K.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"12px"}},i.map((u,c)=>K.default.createElement("div",{key:c,style:{border:`1px solid ${a.cardBorder}`,borderRadius:"10px",padding:"12px",display:"flex",flexDirection:"column",gap:"8px"}},K.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"8px"}},K.default.createElement("span",{style:{fontFamily:Oe.mono,fontSize:"13px",color:a.textMuted}},"#"),K.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:{...Pi(a),flex:1,padding:"6px 10px",fontFamily:Oe.mono}}),K.default.createElement("label",{style:{display:"flex",alignItems:"center",gap:"6px",fontSize:"12px",color:a.textSecondary,whiteSpace:"nowrap"}},K.default.createElement("input",{type:"checkbox",checked:!!u.expose_as_tool,onChange:d=>o(c,{expose_as_tool:d.target.checked}),style:{accentColor:sa}}),"Expose as tool"),K.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]")),K.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:{...Pi(a),fontFamily:Oe.mono,resize:"vertical"}}))))),K.default.createElement("div",null,K.default.createElement("label",{style:$a(a)},"Instruction Sets"),K.default.createElement("div",{style:{display:"flex",flexWrap:"wrap",gap:"8px"}},r.instructionSets?.map(u=>{let c=e.instruction_sets.includes(u);return K.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?sa:a.inputBorder}`,background:c?sa:"transparent",color:c?"#ffffff":a.textCell}},u)}))))}function WZ({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 K.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"20px"}},K.default.createElement("p",{style:{fontSize:"13px",color:n.textSecondary,margin:0}},"Select the tools your agent can use."),K.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 K.default.createElement("button",{key:i,type:"button",onClick:()=>r("tools",i),style:{padding:"14px",borderRadius:"12px",textAlign:"center",cursor:"pointer",border:`1px solid ${o?sa:n.cardBorder}`,background:o?"rgba(239,68,68,0.08)":"transparent",color:o?sa:n.textCell}},K.default.createElement("span",{style:{display:"block",fontFamily:Oe.mono,fontSize:"20px",marginBottom:"6px"}},a(i)),K.default.createElement("span",{style:{fontSize:"13px",fontWeight:500,textTransform:"capitalize"}},i))})),e.tools.length>0&&K.default.createElement("div",{style:{background:n.innerBg,borderRadius:"10px",padding:"14px"}},K.default.createElement("div",{style:{...aj(n),marginBottom:"10px"}},"Selected tools \xB7 ",e.tools.length),K.default.createElement("div",{style:{display:"flex",flexWrap:"wrap",gap:"8px"}},e.tools.map(i=>K.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 KZ({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 K.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"16px"}},K.default.createElement("p",{style:{fontSize:"13px",color:n.textSecondary,margin:0}},"View and restore previous versions of this agent."),e.length>0?K.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"10px"}},e.map(o=>{let l=o.is_latest||o.isLatest;return K.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?sa:n.cardBorder}`,background:l?"rgba(239,68,68,0.06)":"transparent"}},K.default.createElement("div",{style:{minWidth:0}},K.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:"8px"}},K.default.createElement("span",{style:{fontFamily:Oe.mono,fontSize:"13px",fontWeight:600,color:n.textPrimary}},"v",o.version_number||o.versionNumber),l&&K.default.createElement(US,{tone:"error",darkMode:a},"current")),K.default.createElement("p",{style:{fontSize:"13px",color:n.textCell,margin:"4px 0 0"}},o.change_summary||o.changeSummary),K.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&&K.default.createElement(Gl,{variant:"secondary",size:"sm",colors:n,onClick:()=>i(o.id)},"Restore"))})):K.default.createElement(ij,{colors:n,glyph:"[#]",title:"No version history",hint:"Saving a change records a new version."}))}function GZ({code:e,colors:t}){let r=()=>{navigator.clipboard.writeText(e)};return K.default.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"12px"}},K.default.createElement("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between"}},K.default.createElement("p",{style:{fontSize:"13px",color:t.textSecondary,margin:0}},"Generated Ruby code for this agent."),K.default.createElement(Gl,{variant:"secondary",size:"sm",colors:t,onClick:r},"Copy")),K.default.createElement("pre",{style:{padding:"16px",background:"#111827",color:"#e5e7eb",borderRadius:"10px",overflowX:"auto",fontFamily:Oe.mono,fontSize:"12px",lineHeight:1.6,margin:0}},K.default.createElement("code",null,e||"Loading...")))}function XZ({colors:e}){return K.default.createElement("div",{style:{background:e.cardBg,border:`1px solid ${e.cardBorder}`,borderRadius:"12px",padding:"20px"}},K.default.createElement(ij,{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 ij({colors:e,glyph:t,title:r,hint:n}){return K.default.createElement("div",{style:{textAlign:"center",padding:"40px 20px"}},K.default.createElement("div",{style:{fontFamily:Oe.mono,fontSize:"20px",color:e.textMuted}},t),K.default.createElement("div",{style:{fontSize:"14px",fontWeight:600,color:e.textPrimary,marginTop:"10px"}},r),K.default.createElement("p",{style:{fontSize:"13px",color:e.textMuted,margin:"6px auto 0",maxWidth:"420px"}},n))}var te=ee(ne());async function dc(){let e=window.ACTIVE_AGENT_DASHBOARD?.meta?.upgradeUrl;if(!e)throw new Error("This dashboard has no billing configured.");window.location.href=e}var ZZ={llm:{role:"assistant",label:"LLM"},tool:{role:"tool",label:"Tool"},agent:{role:"developer",label:"Agent"},mcp:{role:"system",label:"MCP"}},QZ=e=>{let t=ZZ[e]||{role:e,label:e};return{...ra(t.role,!1),label:t.label}};function $S({agent:e,onBack:t}){let[r,n]=(0,te.useState)(""),a=["ask",...(e.action_prompts||[]).map(y=>y.name).filter(Boolean)],[i,o]=(0,te.useState)("ask"),[l,s]=(0,te.useState)([]),[u,c]=(0,te.useState)(!1),[d,f]=(0,te.useState)(null),[p,v]=(0,te.useState)(null),[h,b]=(0,te.useState)({}),[m,g]=(0,te.useState)(!1),[x,A]=(0,te.useState)(null),k=(0,te.useRef)(null);(0,te.useEffect)(()=>{_()},[e.id]),(0,te.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 H=await T.json();if(f(H.run),!["pending","running"].includes(H.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 dc({planSlug:"pro"})}catch(y){A(y.message),g(!1)}};return te.default.createElement("div",{className:"grid grid-cols-3 gap-6 h-full"},te.default.createElement("div",{className:"col-span-2 flex flex-col space-y-4"},p&&te.default.createElement("div",{className:"bg-amber-50 border border-amber-200 rounded-xl p-4 flex items-center justify-between gap-4"},te.default.createElement("div",null,te.default.createElement("p",{className:"font-medium text-amber-900"},"Monthly run limit reached"),te.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&&te.default.createElement("p",{className:"text-sm text-red-600 mt-1"},x)),te.default.createElement("div",{className:"flex items-center gap-2 flex-shrink-0"},te.default.createElement("a",{href:"/pricing",className:"px-4 py-2 text-sm text-amber-800 hover:text-amber-900"},"See plans"),te.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"))),te.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},te.default.createElement("div",{className:"flex items-start space-x-4"},te.default.createElement("div",{className:"flex-shrink-0"},te.default.createElement(Mt,{size:50})),te.default.createElement("div",{className:"flex-1"},te.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"}),te.default.createElement("div",{className:"flex items-center justify-between mt-3"},te.default.createElement("div",{className:"flex items-center gap-3"},a.length>1&&te.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=>te.default.createElement("option",{key:y,value:y},"#",y))),te.default.createElement("span",{className:"text-xs text-gray-400"},"Press ",te.default.createElement("kbd",{className:"px-1.5 py-0.5 bg-gray-100 rounded text-gray-600"},"\u2318")," + ",te.default.createElement("kbd",{className:"px-1.5 py-0.5 bg-gray-100 rounded text-gray-600"},"Enter")," to run")),te.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?te.default.createElement(te.default.Fragment,null,te.default.createElement("span",{className:"animate-spin",style:{fontFamily:Oe.mono}},"[~]"),te.default.createElement("span",null,"Running...")):te.default.createElement(te.default.Fragment,null,te.default.createElement("span",{style:{fontFamily:Oe.mono}},"[>]"),te.default.createElement("span",null,"Run"))))))),te.default.createElement("div",{className:"flex-1 bg-white rounded-xl border border-gray-200 flex flex-col overflow-hidden"},te.default.createElement("div",{className:"px-4 py-3 border-b border-gray-200 flex items-center justify-between"},te.default.createElement("h3",{className:"font-medium text-gray-900"},"Output"),d&&te.default.createElement("div",{className:"flex items-center space-x-3 text-sm"},te.default.createElement("span",{className:`px-2 py-0.5 rounded-full text-xs font-medium ${X(d.status)}`},d.status),d.output_metadata?.model&&te.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&&te.default.createElement("span",{className:"text-gray-400"},R(d.duration_ms)),d.total_tokens&&te.default.createElement("span",{className:"text-gray-400"},d.total_tokens," tokens"),d.trace_id&&te.default.createElement("a",{href:rt(`/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"))),te.default.createElement("div",{ref:k,className:"flex-1 p-4 overflow-auto bg-gray-50 font-mono text-sm"},d?te.default.createElement(te.default.Fragment,null,I(d).length>0&&te.default.createElement("div",{className:"mb-4 space-y-2"},I(d).map(y=>{let C=!!h[y.eid],O=!!(y.input||y.output),D=QZ(y.kind),N=DS(!1);return te.default.createElement("div",{key:y.eid},te.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)"}:{}},te.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),te.default.createElement("div",{className:"min-w-0 flex-1"},te.default.createElement("div",{className:"text-sm break-words text-gray-800"},y.label,y.output&&!C&&te.default.createElement("span",{className:"text-gray-400"}," \u201C",y.output.slice(0,160),y.output.length>160?"\u2026":"","\u201D")),te.default.createElement("div",{className:"text-xs mt-0.5 font-mono flex items-center gap-2 flex-wrap text-gray-400"},y.status==="started"?te.default.createElement("span",{className:"text-blue-500 animate-pulse"},"running\u2026"):te.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&&te.default.createElement("span",null,new Date(y.at).toLocaleTimeString()))),O&&te.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"},te.default.createElement("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"}))),C&&te.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&&te.default.createElement("div",null,te.default.createElement("div",{className:"text-xs uppercase tracking-wide mb-1 text-gray-400"},"Input"),te.default.createElement("pre",{style:{...N,whiteSpace:"pre-wrap",maxHeight:"160px",overflowY:"auto"}},j(y.input))),y.output&&te.default.createElement("div",null,te.default.createElement("div",{className:"text-xs uppercase tracking-wide mb-1 text-gray-400"},y.status==="error"?"Error":"Result"),te.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)?te.default.createElement("div",{className:"flex items-center space-x-2 text-gray-500"},te.default.createElement("span",{className:"animate-pulse"},"\u25CF"),te.default.createElement("span",null,I(d).length>0?"Working\u2026":"Starting run\u2026")):d.error_message?te.default.createElement("div",{className:"text-red-600"},te.default.createElement("div",{className:"font-semibold mb-2"},"Error:"),te.default.createElement("pre",{className:"whitespace-pre-wrap"},d.error_message)):te.default.createElement("pre",{className:"whitespace-pre-wrap text-gray-800"},d.output||"No output")):te.default.createElement("div",{className:"text-gray-400 text-center py-12"},"Enter a prompt and click Run to test your agent")))),te.default.createElement("div",{className:"space-y-4"},te.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},te.default.createElement("div",{className:"flex items-center space-x-3 mb-4"},te.default.createElement(Mt,{size:60}),te.default.createElement("div",null,te.default.createElement("h3",{className:"font-semibold text-gray-900"},e.name),te.default.createElement("p",{className:"text-sm text-gray-500"},e.provider," / ",e.model))),te.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")),te.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},te.default.createElement("h4",{className:"font-medium text-gray-900 mb-3"},"Configuration"),te.default.createElement("dl",{className:"space-y-2 text-sm"},te.default.createElement("div",{className:"flex justify-between"},te.default.createElement("dt",{className:"text-gray-500"},"Temperature"),te.default.createElement("dd",{className:"text-gray-900"},e.modelConfig?.temperature||e.model_config?.temperature||.7)),te.default.createElement("div",{className:"flex justify-between"},te.default.createElement("dt",{className:"text-gray-500"},"Tools"),te.default.createElement("dd",{className:"text-gray-900"},e.tools?.length||0)),te.default.createElement("div",{className:"flex justify-between"},te.default.createElement("dt",{className:"text-gray-500"},"Status"),te.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&&te.default.createElement("div",{className:"mt-3 pt-3 border-t border-gray-100"},te.default.createElement("div",{className:"text-xs text-gray-500 mb-1.5"},"System Instructions"),te.default.createElement("p",{className:"text-xs text-gray-700 whitespace-pre-wrap max-h-44 overflow-y-auto font-mono leading-relaxed"},e.instructions))),te.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 overflow-hidden"},te.default.createElement("div",{className:"px-4 py-3 border-b border-gray-100"},te.default.createElement("h4",{className:"font-medium text-gray-900"},"Recent Runs")),te.default.createElement("div",{className:"max-h-64 overflow-auto"},l.length>0?te.default.createElement("div",{className:"divide-y divide-gray-100"},l.map(y=>te.default.createElement("div",{key:y.id,className:"px-4 py-3 hover:bg-gray-50 cursor-pointer",onClick:()=>f(y)},te.default.createElement("div",{className:"flex items-center justify-between mb-1"},te.default.createElement("span",{className:`px-2 py-0.5 rounded-full text-xs font-medium ${X(y.status)}`},y.status),te.default.createElement("span",{className:"text-xs text-gray-400"},R(y.duration_ms))),te.default.createElement("p",{className:"text-sm text-gray-600 truncate"},y.input_preview||y.input_prompt?.substring(0,50)),te.default.createElement("p",{className:"text-xs text-gray-400 mt-1 flex items-center gap-2 flex-wrap"},y.model&&te.default.createElement("span",{className:"px-1.5 py-0.5 rounded bg-indigo-50 text-indigo-700 font-mono"},y.model),te.default.createElement("span",null,new Date(y.created_at).toLocaleString()))))):te.default.createElement("div",{className:"px-4 py-8 text-center text-gray-400 text-sm"},"No runs yet"))),te.default.createElement("div",{className:"bg-white rounded-xl border border-gray-200 p-4"},te.default.createElement("h4",{className:"font-medium text-gray-900 mb-3"},"Example Prompts"),te.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)=>te.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=ee(ne()),JZ=[{value:7,label:"7 days"},{value:14,label:"14 days"},{value:30,label:"30 days"},{value:90,label:"90 days"}];function HS({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"},JZ.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(Yf,{title:"Total Agents",value:t?.summary?.total_agents||0,subtitle:`${t?.summary?.active_agents||0} active`,color:"rose"}),he.default.createElement(Yf,{title:"Total Runs",value:t?.summary?.total_runs||0,subtitle:`${t?.period_days} day period`,color:"blue"}),he.default.createElement(Yf,{title:"Success Rate",value:`${t?.summary?.success_rate||0}%`,subtitle:`${t?.summary?.completed_runs||0} completed`,color:"green"}),he.default.createElement(Yf,{title:"Avg Duration",value:u(t?.summary?.avg_duration_ms),subtitle:"per run",color:"purple"}),he.default.createElement(Yf,{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 Yf({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=ee(ne());function VS({agent:e,onBack:t}){let{timeWindow:r}=Ba(),[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),H=rt(`/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(G=>G.json()).then(G=>o(G.interactions||[])).catch(()=>{}).finally(()=>g(!0))},[e.id]),(0,U.useEffect)(()=>{let G=()=>{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 G(),window.addEventListener("popstate",G),()=>window.removeEventListener("popstate",G)},[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 G=new URLSearchParams({page:j.toString(),per_page:"20",minutes:String(r.minutes)});Q&&G.append("status",Q),C&&G.append("source",C),D!=="recent"&&G.append("sort",D);let Ae=await(await fetch(`/api/agents/${e.id}/runs?${G}`)).json();a(j===1?Ae.runs:we=>[...we,...Ae.runs]),R(Ae.runs.length===20)}catch(G){console.error("Failed to load runs:",G)}finally{I(!1),b(!0)}},J=G=>{window.location.pathname!==G&&window.history.pushState({},"",G)},fe=async(G,{updateUrl:xe=!0}={})=>{try{let we=await(await fetch(`/api/runs/${G}`)).json();s(we.run),f(we.messages||[]),v("run"),c(null),xe&&J(`${H}/runs/${G}`)}catch(Ae){console.error("Failed to load run details:",Ae)}},pe=G=>{if(G.source==="reported"){let xe=G.trace_id||String(G.id).replace(/^trace-/,"");window.history.pushState(window.history.state,"",rt(`/traces/${xe}`)),window.dispatchEvent(new CustomEvent("dashboard:navigate",{detail:{path:rt(`/traces/${xe}`)}}));return}fe(G.record_id||G.id)},ge=()=>{s(null),c(null),f([]),v("run"),J(H)},ze=G=>{v("all"),G?(c({id:G.id,name:G.display_name}),J(`${H}/sessions/${G.id}`)):(c(null),J(`${H}/sessions`))},se=G=>{A.current=!0,v(G),J(G==="all"?u?`${H}/sessions/${u.id}`:`${H}/sessions`:l?`${H}/runs/${l.id}`:H)},Ne=u?u.name||i.find(G=>G.id===u.id)?.display_name||`Session #${u.id}`:null,F=()=>{let G=new Map;n.forEach(we=>{let Ke=`${we.model||"unknown"}|${we.instructions_digest||"none"}`;G.has(Ke)||G.set(Ke,{key:Ke,model:we.model,instructionsDigest:we.instructions_digest,instructionsVersion:we.instructions_version,instructionsCodename:we.instructions_codename,instructionsPreview:we.instructions_preview,runs:[]}),G.get(Ke).runs.push(we)});let xe=[...G.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,br)=>Ke+(br.tokens||0),0),avgTokens:we.runs.reduce((Ke,br)=>Ke+(br.tokens||0),0)/we.runs.length,avgDuration:we.runs.reduce((Ke,br)=>Ke+(br.duration_ms||0),0)/we.runs.length,latest:we.runs[0],sortedRuns:[...we.runs].sort((Ke,br)=>(br.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}},$=G=>{switch(G){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=G=>G?G<1e3?`${G}ms`:G<6e4?`${(G/1e3).toFixed(1)}s`:`${Math.floor(G/6e4)}m ${Math.floor(G%6e4/1e3)}s`:"-",ae=G=>G==null?"\u2014":G===0?"$0":G<.01?`$${G.toFixed(4)}`:`$${G.toFixed(2)}`,ie=G=>{let xe=new Date(G),we=new Date-xe,Ke=Math.floor(we/6e4),br=Math.floor(we/36e5),Mo=Math.floor(we/864e5);return Ke<1?"Just now":Ke<60?`${Ke}m ago`:br<24?`${br}h ago`:Mo<7?`${Mo}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(Ua,{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(G=>U.default.createElement("button",{key:G.value||"all",onClick:()=>{O(G.value),z(1)},className:`flex-1 px-2 py-1 rounded-md transition-colors ${C===G.value?"bg-white shadow text-gray-900":"text-gray-600"}`,title:G.value==="dashboard"?"Executions started from this dashboard":G.value==="reported"?"Executions reported by your app via telemetry":"Every execution, however it started"},G.label))),U.default.createElement("select",{value:Q,onChange:G=>{y(G.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:G=>{N(G.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(G=>{let xe=G.source==="reported";return U.default.createElement("div",{key:G.id,onClick:()=>pe(G),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===G.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 ${$(G.status)}`},G.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(G.created_at||G.occurred_at)))),U.default.createElement("p",{className:"text-sm text-gray-700 truncate"},G.input_preview||G.input_prompt?.substring(0,60)||(xe?`${G.action_name||"execution"} \xB7 trace ${String(G.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"},G.model&&U.default.createElement("span",{className:"px-1.5 py-0.5 rounded bg-indigo-50 text-indigo-700 font-mono"},G.model),U.default.createElement("span",{className:D==="longest"?"text-gray-700 font-medium":void 0},q(G.duration_ms)),!!G.tokens&&U.default.createElement("span",{className:D==="tokens"?"text-gray-700 font-medium":void 0},G.tokens," tokens"),G.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"},ae(G.cost))))}),X&&U.default.createElement("button",{onClick:()=>z(G=>G+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 ${$(l.status)}`},l.status)),p==="all"?U.default.createElement("div",{className:"flex-1 overflow-auto p-4 space-y-4"},(()=>{let{totals:G}=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(Mt,{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"},G.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"},G.runs?Math.round(G.completed/G.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(G.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"},G.tokens.toLocaleString()))))})(),U.default.createElement(Di,{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(Mt,{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(Oi,{messages:d,darkMode:!1}):U.default.createElement(Oi,{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:rt(`/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:G,totals:xe}=F();return G.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(Mt,{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(Mt,{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"},G.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=ee(ne());var RZ={productivity:"[+]",development:"<>",research:"[?]",creative:"[*]",data:"[#]",automation:"[>]"};function FS({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,RZ[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(Mt,{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(Mt,{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=ee(ne());function qS({currentView:e,onNavigate:t,agentCount:r,account:n,user:a,gemVersion:i}){let{darkMode:o}=mt(),[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:lr.nav.agents,badge:r},{id:"builder",label:"New Agent",icon:lr.nav.newAgent},{id:"sandbox",label:"Run Agents",icon:lr.nav.demo,highlight:!0}],p=[{id:"traces",label:"Traces",icon:lr.nav.traces},{id:"interactions",label:"Interactions",icon:lr.nav.interactions},{id:"metrics",label:"Metrics",icon:lr.nav.metrics},{id:"evaluations",label:"Evaluations",icon:lr.nav.evaluations},{id:"replay",label:"Session Replay",icon:lr.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(Mt,{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(Mt,{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"}},lr.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"}},lr.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=ee(ne());function YS({user:e,account:t}){let{darkMode:r,toggleDarkMode:n}=mt(),[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=ee(ne());var eQ=6e4,oj=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(" ")},WS=[{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 KS(){let{darkMode:e}=mt(),[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,eQ);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=WS.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:oj(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:oj(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 ",WS.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"),WS.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=ee(ne());var To=ee(ne());var Ng={logger:typeof console<"u"?console:void 0,WebSocket:typeof WebSocket<"u"?WebSocket:void 0},Bt={log(...e){this.enabled&&(e.push(Date.now()),Ng.logger.log("[ActionCable]",...e))}},Kf=()=>new Date().getTime(),Ag=e=>(Kf()-e)/1e3,Gf=class{constructor(t){this.visibilityDidChange=this.visibilityDidChange.bind(this),this.connection=t,this.reconnectAttempts=0}start(){this.isRunning()||(this.startedAt=Kf(),delete this.stoppedAt,this.startPolling(),addEventListener("visibilitychange",this.visibilityDidChange),Bt.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`))}stop(){this.isRunning()&&(this.stoppedAt=Kf(),this.stopPolling(),removeEventListener("visibilitychange",this.visibilityDidChange),Bt.log("ConnectionMonitor stopped"))}isRunning(){return this.startedAt&&!this.stoppedAt}recordMessage(){this.pingedAt=Kf()}recordConnect(){this.reconnectAttempts=0,delete this.disconnectedAt,Bt.log("ConnectionMonitor recorded connect")}recordDisconnect(){this.disconnectedAt=Kf(),Bt.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()&&(Bt.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${Ag(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`),this.reconnectAttempts++,this.disconnectedRecently()?Bt.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${Ag(this.disconnectedAt)} s`):(Bt.log("ConnectionMonitor reopening"),this.connection.reopen()))}get refreshedAt(){return this.pingedAt?this.pingedAt:this.startedAt}connectionIsStale(){return Ag(this.refreshedAt)>this.constructor.staleThreshold}disconnectedRecently(){return this.disconnectedAt&&Ag(this.disconnectedAt){(this.connectionIsStale()||!this.connection.isOpen())&&(Bt.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`),this.connection.reopen())}),200)}};Gf.staleThreshold=6;Gf.reconnectionBackoffRate=.15;var sj={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:Wf,protocols:GS}=sj,tQ=GS.slice(0,GS.length-1),lj=[].indexOf,Xf=class{constructor(t){this.open=this.open.bind(this),this.consumer=t,this.subscriptions=this.consumer.subscriptions,this.monitor=new Gf(this),this.disconnected=!0}send(t){return this.isOpen()?(this.webSocket.send(JSON.stringify(t)),!0):!1}open(){if(this.isActive())return Bt.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`),!1;{let t=[...GS,...this.consumer.subprotocols||[]];return Bt.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${t}`),this.webSocket&&this.uninstallEventHandlers(),this.webSocket=new Ng.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(Bt.log(`Reopening WebSocket, current state is ${this.getState()}`),this.isActive())try{return this.close()}catch(t){Bt.log("Failed to reopen WebSocket",t)}finally{Bt.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 lj.call(tQ,this.getProtocol())>=0}isState(...t){return lj.call(t,this.getState())>=0}getState(){if(this.webSocket){for(let t in Ng.WebSocket)if(Ng.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(){}}};Xf.reopenDelay=500;Xf.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 Wf.welcome:return this.triedToReconnect()&&(this.reconnectAttempted=!0),this.monitor.recordConnect(),this.subscriptions.reload();case Wf.disconnect:return Bt.log(`Disconnecting. Reason: ${n}`),this.close({allowReconnect:a});case Wf.ping:return null;case Wf.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 Wf.rejection:return this.subscriptions.reject(t);default:return this.subscriptions.notify(t,"received",r)}},open(){if(Bt.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`),this.disconnected=!1,!this.isProtocolSupported())return Bt.log("Protocol is unsupported. Stopping monitor and disconnecting."),this.close({allowReconnect:!1})},close(e){if(Bt.log("WebSocket onclose event"),!this.disconnected)return this.disconnected=!0,this.monitor.recordDisconnect(),this.subscriptions.notifyAll("disconnected",{willAttemptReconnect:this.monitor.isRunning()})},error(){Bt.log("WebSocket onerror event")}};var rQ=function(e,t){if(t!=null)for(let r in t){let n=t[r];e[r]=n}return e},XS=class{constructor(t,r={},n){this.consumer=t,this.identifier=JSON.stringify(r),rQ(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)}},ZS=class{constructor(t){this.subscriptions=t,this.pendingSubscriptions=[]}guarantee(t){this.pendingSubscriptions.indexOf(t)==-1?(Bt.log(`SubscriptionGuarantor guaranteeing ${t.identifier}`),this.pendingSubscriptions.push(t)):Bt.log(`SubscriptionGuarantor already guaranteeing ${t.identifier}`),this.startGuaranteeing()}forget(t){Bt.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=>{Bt.log(`SubscriptionGuarantor resubscribing ${t.identifier}`),this.subscriptions.subscribe(t)}))}),500)}},QS=class{constructor(t){this.consumer=t,this.guarantor=new ZS(this),this.subscriptions=[]}create(t,r){let n=t,a=typeof n=="object"?n:{channel:n},i=new XS(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){Bt.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})}},JS=class{constructor(t){this._url=t,this.subscriptions=new QS(this),this.connection=new Xf(this),this.subprotocols=[]}get url(){return nQ(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 nQ(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 uj(e=aQ("url")||sj.default_mount_path){return new JS(e)}function aQ(e){let t=document.head.querySelector(`meta[name='action-cable-${e}']`);if(t)return t.getAttribute("content")}var _g=null;function iQ(){if(_g===null)try{_g=uj()}catch{_g=!1}return _g||null}function cj(e,t,r,n=!0){let a=(0,To.useRef)(null),i=(0,To.useRef)(r);return(0,To.useEffect)(()=>{i.current=r},[r]),(0,To.useEffect)(()=>{if(!n||!e)return;let l=iQ();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,To.useCallback)((l,s)=>{a.current&&a.current.perform(l,s)},[])}}var Cg=[{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"}],oQ={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"}]},dj={max_runs:10,timeout_seconds:300,session_duration_minutes:15};function RS({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(ae=>({...ae,[q.provider]:{status:"running",started_at:q.started_at,provider:q.provider}})):d(ae=>({...ae,status:"running",provider:q.provider}));break;case"run_complete":X?(y(ae=>({...ae,[q.provider]:{...q.run,provider:q.provider}})),O(ae=>ae.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(ae=>{let ie=ae.filter(G=>G!==q.provider);return ie.length===0&&p(!1),ie});break;case"run_error":X?(y(ae=>({...ae,[q.provider]:{status:"failed",error:q.error,provider:q.provider}})),O(ae=>ae.filter(ie=>ie!==q.provider))):(d(ae=>({...ae,status:"failed",error:q.error})),p(!1)),q.sandbox&&i(q.sandbox);break}},[X]);cj("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 ae=await q.json();M(ae.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 ae=new Date,ie=new Date(a.expires_at),G=Math.max(0,Math.floor((ie-ae)/1e3));x(G),G===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 ae=await q.json();i(ae.sandbox),u([]),d(null)}catch(q){m(q.message)}finally{h(!1)}},H=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 ae=await fetch(`/api/sandboxes/${a.session_id}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({task:o,provider:q})}),ie=await ae.json();if(ae.status===402&&ie.upgrade_required){M(ie.usage),k(!0),d(G=>({...G,status:"failed",error:ie.message||"Plan limit reached"})),p(!1);return}if(!ae.ok)throw new Error(ie.error||"Task execution failed");ie.usage&&M(ie.usage),ge(ie.run_id)}catch(ae){d(ie=>({...ie,status:"failed",error:ae.message})),p(!1)}},J=async()=>{p(!0),O([...j]),y({});let q={};j.forEach(ae=>{q[ae]={status:"running",result:"",started_at:new Date().toISOString()}}),y(q);try{let ae=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(!ae.ok){let G=await ae.json();throw new Error(G.error||"Comparison failed")}let ie=await ae.json();ie.sandbox&&i(ie.sandbox),setTimeout(()=>{C.length>0&&fe()},6e4)}catch(ae){j.forEach(ie=>{y(G=>({...G,[ie]:{status:"failed",error:ae.message}}))}),p(!1),O([])}},fe=async()=>{if(a?.session_id)try{let ae=await(await fetch(`/api/sandboxes/${a.session_id}`)).json();i(ae.sandbox),u(ae.sandbox.runs||[]),(ae.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(ae=>ae.includes(q)?ae.length===1?ae:ae.filter(ie=>ie!==q):[...ae,q])},ge=async q=>{let ae=async()=>{try{let G=await(await fetch(`/api/sandboxes/${a.session_id}`)).json();i(G.sandbox);let xe=G.sandbox.runs||[],Ae=xe[xe.length-1];if(Ae&&(d(Ae),u(xe),Ae.status==="completed"||Ae.status==="failed")){p(!1);return}setTimeout(ae,1e3)}catch{p(!1)}};ae()},ze=q=>{q.key==="Enter"&&(q.metaKey||q.ctrlKey)&&H()},se=q=>{if(!q)return"--:--";let ae=Math.floor(q/60),ie=q%60;return`${ae}:${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 dc({planSlug:"pro",billingInterval:q})}catch(ae){console.error("Checkout error:",ae),m(ae.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(Mt,{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:H,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 ae=Cg.find(G=>G.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 ${ae?.color||"bg-gray-400"}`}),B.default.createElement("span",{className:"font-medium text-gray-900"},ae?.name||q),B.default.createElement("span",{className:"text-xs text-gray-500"},ae?.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 ${Cg.find(q=>q.id===c.provider)?.color||"bg-gray-400"}`}),B.default.createElement("span",{className:"text-sm text-gray-600"},Cg.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"},Cg.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"},oQ[r]?.map((q,ae)=>B.default.createElement("button",{key:ae,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||dj.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"},dj.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,ae)=>B.default.createElement("div",{key:q.id||ae,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:()=>$("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=ee(ne());var Eg={SCREENSHOT:"screenshot",IFRAME:"iframe"},fj={navigate:"[->]",click:"[*]",type:"[T]",form_fill:"[F]",key_press:"[K]",snapshot:"[S]",hover:"[~]",select:"[v]",file_upload:"[^]",dialog:"[!]",evaluate:"[js]",wait:"[..]",scroll:"[|]",drag:"[<>]"},eA={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"},lQ=` + @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 tA({recordingId:e,onHandoff:t,onClose:r}){let{darkMode:n}=mt(),[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)(Eg.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 $=await F.json();i($.recordings||[]),!o&&$.recordings?.length>0&&l($.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],$=c[g+1];if(!$){m(!1);return}let q=($.timestamp_ms-F.timestamp_ms)/A;return L.current=setTimeout(()=>{x(ae=>ae+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(Eg.SCREENSHOT)):j===Eg.IFRAME&&O(F)},[g,c,j]);let O=(0,ve.useCallback)(F=>{if(!F||!I.current)return;let $=I.current;try{let q=s?.metadata?.page_url||"/";switch($.src!==q&&$.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 $=I.current.contentDocument||I.current.contentWindow?.document;if(!$)return;$.querySelectorAll(".session-replay-highlight").forEach(ae=>{ae.classList.remove("session-replay-highlight"),ae.style.outline=""});let q=$.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,$)=>{if(!(!F||!$||!I.current))try{let q=I.current.contentDocument||I.current.contentWindow?.document;if(!q)return;let ae=q.querySelector(F);if(ae&&(ae.tagName==="INPUT"||ae.tagName==="TEXTAREA")){let ie=0,G=setInterval(()=>{ie<$.length?(ae.value=$.substring(0,ie+1),ie++):clearInterval(G)},50)}}catch{}},T=async F=>{if(!F){u(null),d([]),x(0),m(!1),p(!1),h(null);return}p(!0),h(null);try{let $=await fetch(`/api/session_recordings/${F}`);if(!$.ok)throw new Error("Failed to load recording");let q=await $.json();if(u(q.recording),x(0),m(!1),q.recording?.id){let ae=await fetch(`/api/session_recordings/${q.recording.id}/actions`);if(ae.ok){let ie=await ae.json();d(ie.actions||q.recording.timeline||[])}else d(q.recording.timeline||[])}}catch($){h($.message)}finally{p(!1)}},H=()=>{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 $=E.current.getBoundingClientRect(),ie=(F.clientX-$.left)/$.width*(s?.duration_ms||c[c.length-1]?.timestamp_ms||0),G=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 $=await F.json();t&&t($)}catch(F){h(F.message)}},ge=F=>{if(!F)return"0:00";let $=Math.floor(F/1e3),q=Math.floor($/60),ae=$%60;return`${q}:${ae.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:lQ}}),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===Eg.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:eA[ze.action_type]||"#94a3b8",fontWeight:"600"}},fj[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,$)=>{let q=$===g,ae=${x($),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:ae?.6:1}:{opacity:ae?.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"}},fj[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,$)=>{let q=s?.duration_ms?F.timestamp_ms/s.duration_ms*100:0,ae=eA[F.action_type]||"#94a3b8";return ve.default.createElement("div",{key:$,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:$===g?ae:"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:H,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=ee(ne());var kg=e=>e==null?"0":e>=1e6?(e/1e6).toFixed(1)+"M":e>=1e3?(e/1e3).toFixed(1)+"K":e.toString();function rA({account:e,user:t,subscription:r,agentCount:n=0}){let{darkMode:a}=mt(),[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 dc({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"}`},kg(i?.traces_used),i?.traces_limit>0&&de.default.createElement("span",{className:`text-sm font-normal ${a?"text-gray-500":"text-gray-400"}`}," / ",kg(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"}`},kg(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"}`},kg(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=ee(ne());var pj={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 nA({user:e,account:t}){let{darkMode:r,toggleDarkMode:n}=mt(),[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 H=await N.json(),Y=await T.json();l(H.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 H=await T.json().catch(()=>({}));throw new Error(Array.isArray(H.error)?H.error.join(", "):"save failed")}M(null),L(""),await z()}catch(T){k(`Could not save the ${pj[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:H,hint:Y})=>{let J=pj[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 ${H?r?"text-green-400":"text-green-600":r?"text-gray-400":"text-gray-500"}`},H?T?Y:`Configured (${Y})`:"Not configured"))),le.default.createElement("div",{className:"flex items-center space-x-2"},H&&!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":H?"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 sQ({user:e,initialAgents:t=[],meta:r={},account:n=null,subscription:a=null}){let{darkMode:i}=mt(),[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({},"",rt(`/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({},"",rt()))}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({},"",rt(`/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=rt();y==="builder"?O=rt("/agents/new"):y==="editor"&&C?O=rt(`/agents/${C.id}/edit`):y==="runner"&&C?O=rt(`/agents/${C.id}/run`):y==="agent-analytics"&&C?O=rt(`/agents/${C.id}/analytics`):y==="history"&&C?O=rt(`/agents/${C.id}/interactions`):y==="analytics"?O=rt("/analytics"):y==="traces"?O=rt("/traces"):y==="metrics"?O=rt("/metrics"):y==="interactions"?O=rt("/interactions"):y==="evaluations"?O=rt("/evaluations"):y==="benchmarks"?O=rt("/benchmarks"):y==="replay"?O=rt("/replay"):y==="sandbox"?O=rt("/sandbox"):y==="organization"?O=rt("/organization"):y==="settings"&&(O=rt("/settings")),window.history.pushState({},"",O)},Q=()=>{switch(s){case"builder":return He.default.createElement(Ab,{meta:r,onSave:E,onCancel:()=>R("list"),isLoading:f});case"editor":return c?He.default.createElement(Sg,{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($S,{agent:c,onBack:()=>R("editor",c)}):null;case"agent-analytics":return c?He.default.createElement(Sg,{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(VS,{agent:c,onBack:()=>R("editor",c)}):null;case"analytics":return He.default.createElement(HS,{onSelectAgent:y=>{A(y.id,"agent-analytics")}});case"traces":return He.default.createElement(Kl,null);case"metrics":return He.default.createElement(KS,null);case"interactions":return He.default.createElement(Di,null);case"evaluations":return He.default.createElement(qf,null);case"replay":return He.default.createElement(tA,{onHandoff:y=>{M("Taking over session...","info"),R("sandbox")},onClose:()=>R("list")});case"sandbox":return He.default.createElement(RS,{initialType:"playwright_mcp",onClose:()=>R("list")});case"organization":return He.default.createElement(rA,{user:e,account:n,subscription:a,agentCount:o.length});case"settings":return He.default.createElement(nA,{user:e});default:return He.default.createElement(Sb,{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(qS,{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(YS,{user:e,account:n}),He.default.createElement("main",{className:"flex-1 p-6 overflow-auto"},Q())),b&&He.default.createElement(FS,{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 aA(e){return He.default.createElement(nE,null,He.default.createElement(XP,null,He.default.createElement(sQ,{...e})))}var uQ="active-agent-dashboard";function cQ(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 mj(){let e=document.getElementById(uQ);if(!e)return;let t=JSON.parse(e.dataset.props||"{}");window.ACTIVE_AGENT_DASHBOARD=t,cQ(t.mountPath||"/"),(0,hj.createRoot)(e).render(vj.default.createElement(aA,{...t}))}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",mj):mj(); +/*! 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/actionagent/app/controllers/action_agent/api/agent_runs_controller.rb b/actionagent/app/controllers/action_agent/api/agent_runs_controller.rb new file mode 100644 index 00000000..531ee5c4 --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/agent_runs_controller.rb @@ -0,0 +1,128 @@ +# frozen_string_literal: true + +module ActionAgent + 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 diff --git a/actionagent/app/controllers/action_agent/api/agents_controller.rb b/actionagent/app/controllers/action_agent/api/agents_controller.rb new file mode 100644 index 00000000..503a5600 --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/agents_controller.rb @@ -0,0 +1,411 @@ +# frozen_string_literal: true + +module ActionAgent + 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 diff --git a/actionagent/app/controllers/action_agent/api/analytics_controller.rb b/actionagent/app/controllers/action_agent/api/analytics_controller.rb new file mode 100644 index 00000000..22315ebd --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/analytics_controller.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +module ActionAgent + 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 = ActionAgent::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 diff --git a/actionagent/app/controllers/action_agent/api/api_keys_controller.rb b/actionagent/app/controllers/action_agent/api/api_keys_controller.rb new file mode 100644 index 00000000..7e3508a7 --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/api_keys_controller.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module ActionAgent + 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 diff --git a/actionagent/app/controllers/action_agent/api/base_controller.rb b/actionagent/app/controllers/action_agent/api/base_controller.rb new file mode 100644 index 00000000..736d3515 --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/base_controller.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +module ActionAgent + module Api + # Base class for the dashboard's JSON API — everything the React + # dashboard reads and writes. + # + # Authentication is the host app's (ActionAgent + # .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 < ActionAgent::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. + # An unresolved owner scopes to nothing rather than to + # `where(id: nil)`, which would match every unowned row and leak them + # across tenants. + def owned(relation) + klass = relation.respond_to?(:klass) ? relation.klass : relation + + case klass.owner_association + when :account then current_account ? relation.where(account_id: current_account.id) : relation.none + when :user then current_user ? relation.where(user_id: current_user.id) : relation.none + else relation.all + end + end + + # The agents the caller can see. Not simply `owned(Agent)`: a host + # app can define reachability more broadly than ownership (an + # account's key reaching every member's agents, say). + def owner_agents + ActionAgent.agents_for(current_owner) + end + + # Reported traces visible to the caller. Scoped to the tenant in a + # multi-tenant install; every trace otherwise. + def owned_traces + ActionAgent.trace_model.for_account(current_account) + end + + # The tenant, when the host app has one. current_owner already + # resolves it in multi-tenant mode; single-tenant installs have none. + def current_account + ActionAgent.multi_tenant? ? current_owner : 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 ActionAgent.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) + denial = ActionAgent.quota_denial(current_owner, kind) + return if denial.blank? + + body = { error: "Plan limit reached", upgrade_required: true } + # A checker can answer with a message, or with a hash carrying + # whatever else the host app wants the client to see (its own usage + # numbers, an upgrade link). + body = denial.is_a?(Hash) ? body.merge(denial) : body.merge(message: denial) + + render json: body, status: :payment_required + end + + def enforce_execution_quota! = enforce_quota!(:execution) + + def record_execution_usage + ActionAgent.record_usage(current_owner, :execution) + end + + # Execution can be turned off entirely, leaving a read-only + # observability dashboard. + def require_execution_enabled! + return if ActionAgent.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 diff --git a/actionagent/app/controllers/action_agent/api/evaluations_controller.rb b/actionagent/app/controllers/action_agent/api/evaluations_controller.rb new file mode 100644 index 00000000..1b28dd8a --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/evaluations_controller.rb @@ -0,0 +1,148 @@ +# frozen_string_literal: true + +module ActionAgent + 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 diff --git a/actionagent/app/controllers/action_agent/api/instance_tiers_controller.rb b/actionagent/app/controllers/action_agent/api/instance_tiers_controller.rb new file mode 100644 index 00000000..66f84645 --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/instance_tiers_controller.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +module ActionAgent + 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 diff --git a/actionagent/app/controllers/action_agent/api/interactions_controller.rb b/actionagent/app/controllers/action_agent/api/interactions_controller.rb new file mode 100644 index 00000000..3788fbd7 --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/interactions_controller.rb @@ -0,0 +1,162 @@ +# frozen_string_literal: true + +module ActionAgent + 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 ActionAgent.trace_model.none unless agent + + ActionAgent.trace_model.where(agent_id: agent.id) + .or(ActionAgent.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 diff --git a/actionagent/app/controllers/action_agent/api/mcp_controller.rb b/actionagent/app/controllers/action_agent/api/mcp_controller.rb new file mode 100644 index 00000000..bf6fe447 --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/mcp_controller.rb @@ -0,0 +1,218 @@ +# frozen_string_literal: true + +module ActionAgent + 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 + ActionAgent.agents_for(@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 ActionAgent.execution_enabled? + raise McpError.new("Agent execution is disabled on this dashboard") + end + if (denial = ActionAgent.quota_denial(@owner, :execution)).present? + raise McpError.new(denial.is_a?(Hash) ? denial[:message] || denial["message"] : denial) + end + + run = agent.test_execute(message, action: action) + ActionAgent.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 diff --git a/actionagent/app/controllers/action_agent/api/metrics_controller.rb b/actionagent/app/controllers/action_agent/api/metrics_controller.rb new file mode 100644 index 00000000..803212a8 --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/metrics_controller.rb @@ -0,0 +1,178 @@ +# frozen_string_literal: true + +module ActionAgent + module Api + # Read API for telemetry metrics, backing the dashboard Metrics view. + # + # Exposes the same aggregates as the gem dashboard's metrics page + # (ActionAgent::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 + ActionAgent.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) + traces = ActionAgent.trace_model + bucket_sql = Arel.sql(traces.hour_bucket_sql) + to_epoch = ->(hash) { hash.transform_keys { |key| traces.hour_bucket_epoch(key) } } + + counts = to_epoch.call(scope.group(bucket_sql).count) + latencies = to_epoch.call(scope.group(bucket_sql).average(:total_duration_ms)) + + 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 = ActionAgent.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 diff --git a/actionagent/app/controllers/action_agent/api/provider_keys_controller.rb b/actionagent/app/controllers/action_agent/api/provider_keys_controller.rb new file mode 100644 index 00000000..c08d13ad --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/provider_keys_controller.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module ActionAgent + 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 diff --git a/actionagent/app/controllers/action_agent/api/provider_models_controller.rb b/actionagent/app/controllers/action_agent/api/provider_models_controller.rb new file mode 100644 index 00000000..23db09ec --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/provider_models_controller.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +module ActionAgent + 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 diff --git a/actionagent/app/controllers/action_agent/api/sandboxes_controller.rb b/actionagent/app/controllers/action_agent/api/sandboxes_controller.rb new file mode 100644 index 00000000..0cdd89b9 --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/sandboxes_controller.rb @@ -0,0 +1,224 @@ +# frozen_string_literal: true + +module ActionAgent + module Api + class SandboxesController < BaseController + # Authenticated like the rest of the dashboard. This controller used to + # opt out entirely for an anonymous free tier, which made #run and + # #compare an open proxy: any unauthenticated caller could execute + # arbitrary prompts against the host app's provider credentials, and + # #show would read back any session by id. Opting out also skipped the + # callback that refuses to serve an unauthenticated dashboard outside + # development, so it bypassed that safeguard too. + + before_action :require_execution_enabled!, only: [ :run, :compare ] + before_action :enforce_execution_quota!, only: [ :compare ] + 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? + owned(SandboxSession).find_by!(session_id: sandbox_id) + else + s = SandboxSession.new(sandbox_type: params[:sandbox_type] || "playwright_mcp") + s.user = current_user if s.respond_to?(:user=) + s.save! + 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, owned by whoever opened it. + def create + @sandbox = SandboxSession.new(sandbox_params) + # Guarded: the association only exists when the host app configured a + # user model, and a single-user install configures none. + @sandbox.user = current_user if @sandbox.respond_to?(:user=) + @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 = ActionAgent.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 = owned(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 diff --git a/actionagent/app/controllers/action_agent/api/session_recordings_controller.rb b/actionagent/app/controllers/action_agent/api/session_recordings_controller.rb new file mode 100644 index 00000000..22491303 --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/session_recordings_controller.rb @@ -0,0 +1,372 @@ +# frozen_string_literal: true + +module ActionAgent + module Api + class SessionRecordingsController < BaseController + # Every action authenticates. Recordings carry a replayable timeline of + # a real browser session — including values typed into forms — so the + # lander-analytics exemption that used to cover start_user_session, + # record_action, complete_session and demo published exactly the data + # that most needs a login. A marketing site that wants to record its + # own visitors should do so against its own endpoint, not one the + # engine exposes on every host that mounts it. + + 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/: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]) + return not_found unless can_manage_recording?(recording) + + 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]) + return not_found unless can_manage_recording?(recording) + + 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 + + # Scoped through can_manage_recording? rather than owned(): nothing in + # the engine writes user_id/account_id onto a recording, so an + # ownership scope would hide it from the person who made it. 404 rather + # than 403 so ids stay unenumerable. + def set_recording + @recording = SessionRecording.find(params[:id]) + return if can_manage_recording?(@recording) + + not_found + 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 diff --git a/actionagent/app/controllers/action_agent/api/templates_controller.rb b/actionagent/app/controllers/action_agent/api/templates_controller.rb new file mode 100644 index 00000000..be64dffd --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/templates_controller.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +module ActionAgent + module Api + class TemplatesController < BaseController + # No anonymous exemption: #show looks a template up by bare id, so the + # exemption served unpublished drafts and private prompt libraries to + # anyone walking the id space. #index was already limited to public + # templates; #show was not. + + # 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 diff --git a/actionagent/app/controllers/action_agent/api/trace_reports_controller.rb b/actionagent/app/controllers/action_agent/api/trace_reports_controller.rb new file mode 100644 index 00000000..3341843a --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/trace_reports_controller.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module ActionAgent + module Api + # Read API for telemetry traces, backing the dashboard's Traces view. + # + # Query logic mirrors the server-rendered TracesController (same scopes + # on ActionAgent.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 ?", "#{ActionAgent.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 + ActionAgent.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 diff --git a/actionagent/app/controllers/action_agent/api/traces_controller.rb b/actionagent/app/controllers/action_agent/api/traces_controller.rb new file mode 100644 index 00000000..c0e0787d --- /dev/null +++ b/actionagent/app/controllers/action_agent/api/traces_controller.rb @@ -0,0 +1,136 @@ +# frozen_string_literal: true + +module ActionAgent + module Api + # Telemetry ingestion endpoint. + # + # Receives traces from ActiveAgent::Telemetry::Reporter and stores them + # for analysis and visualization in the dashboard. + # + # Supports two modes: + # - Local mode: synchronous processing; unauthenticated unless + # ActionAgent.ingest_api_key is set (set it whenever the + # mount is reachable beyond your own machine) + # - Multi-tenant mode: per-account Bearer token auth, async processing + # via job + # + # @example Local mode request + # POST /api/traces (e.g. /activeagents/api/traces) + # Content-Type: application/json + # + # { + # "traces": [...], + # "sdk": { "name": "activeagent", "version": "0.5.0" } + # } + # + # @example Multi-tenant mode request + # POST /api/traces (e.g. /activeagents/api/traces) + # Authorization: Bearer + # Content-Type: application/json + # + # { + # "traces": [...], + # "sdk": { "name": "activeagent", "version": "0.5.0" } + # } + # + class TracesController < ActionController::API + before_action :authenticate_api_key!, if: -> { ActionAgent.multi_tenant? } + before_action :authenticate_ingest_key!, unless: -> { ActionAgent.multi_tenant? } + + # Maximum traces accepted per request (mirrors + # ProcessTelemetryTracesJob::MAX_TRACES_PER_JOB). + MAX_TRACES_PER_REQUEST = 100 + + # POST /api/traces (e.g. /activeagents/api/traces) + def create + traces = Array(params[:traces]).take(MAX_TRACES_PER_REQUEST) + sdk_info = params[:sdk] || {} + + return head :accepted if traces.empty? + + if ActionAgent.multi_tenant? + # Multi-tenant mode: process in background + ActionAgent::ProcessTelemetryTracesJob.perform_later( + account_id: @account&.id, + traces: traces.as_json, + sdk_info: sdk_info.as_json, + received_at: Time.current.iso8601(6) + ) + else + # Local mode: process synchronously + process_traces_synchronously(traces, sdk_info) + end + + head :accepted + rescue ActionController::ParameterMissing => e + render json: { error: e.message }, status: :bad_request + rescue StandardError => e + Rails.logger.error("[ActionAgent] Trace ingestion error: #{e.message}") + render json: { error: "Internal server error" }, status: :internal_server_error + end + + private + + # Authenticates the request using Bearer token from Authorization header. + # Only used in multi-tenant mode. + def authenticate_api_key! + token = extract_bearer_token + + if token.blank? + render json: { error: "Missing Authorization header" }, status: :unauthorized + return + end + + account_class = ActionAgent.account_class.constantize + @account = account_class.find_by(telemetry_api_key: token) + + if @account.nil? + render json: { error: "Invalid API key" }, status: :unauthorized + return + end + + # Track usage for rate limiting (if the account responds to it) + @account.increment_telemetry_usage! if @account.respond_to?(:increment_telemetry_usage!) + end + + # Requires the configured single-tenant ingest key when one is set. + # The telemetry reporter and ruby_llm_telemetry both send their + # api_key as a Bearer header, so remote apps work unchanged. + def authenticate_ingest_key! + expected = ActionAgent.ingest_api_key + return if expected.blank? + + token = extract_bearer_token + return if token.present? && ActiveSupport::SecurityUtils.secure_compare(token, expected) + + render json: { error: "Invalid API key" }, status: :unauthorized + end + + # Extracts Bearer token from Authorization header. + def extract_bearer_token + auth_header = request.headers["Authorization"] + return nil if auth_header.blank? + + match = auth_header.match(/^Bearer\s+(.+)$/i) + match[1] if match + end + + # Process traces synchronously for local development. + def process_traces_synchronously(traces, sdk_info) + model = ActionAgent.trace_model + + traces.each do |trace| + # Skip if trace already exists (idempotency) + next if model.exists?(trace_id: trace["trace_id"]) + + model.create_from_payload(trace, sdk_info) + rescue StandardError => e + Rails.logger.error( + "[ActionAgent] Failed to process trace #{trace['trace_id']}: " \ + "#{e.class} - #{e.message}" + ) + end + end + end + end +end diff --git a/actionagent/app/controllers/action_agent/application_controller.rb b/actionagent/app/controllers/action_agent/application_controller.rb new file mode 100644 index 00000000..0052fdc2 --- /dev/null +++ b/actionagent/app/controllers/action_agent/application_controller.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +module ActionAgent + # Base controller for the ActiveAgent Dashboard. + # + # Handles authentication and provides helper methods for multi-tenant mode. + class ApplicationController < ActionController::Base + protect_from_forgery with: :exception + + before_action :authenticate_dashboard! + + # Use custom layout if configured, otherwise use engine layout + layout -> { ActionAgent.layout || "action_agent/application" } + + 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! + if ActionAgent.authentication_method.nil? + # Traces contain prompts, outputs, and error backtraces. Refuse to + # serve them unauthenticated anywhere but a local development or + # test environment — a staging or review-app deployment runs under + # its own RAILS_ENV and is just as reachable as production. + unless Rails.env.local? + render plain: "ActiveAgent Dashboard: set ActionAgent.authentication_method " \ + "(see docs/framework/dashboard.md) to enable access in production.", + status: :forbidden + end + return + end + + result = ActionAgent.authentication_method.call(self) + head :unauthorized unless result + rescue StandardError => e + Rails.logger.error("[ActionAgent] Authentication error: #{e.message}") + head :unauthorized + end + + # Returns the current user from the host application. + def current_user + return @current_user if defined?(@current_user) + + @current_user = resolve_actor( + ActionAgent.current_user_resolver, + ActionAgent.current_user_method, + :current_user + ) + end + + # Returns the current owner (account in multi-tenant, user otherwise). + def current_owner + # No fallback to the user in multi-tenant mode: a signed-in user with + # no tenant owns nothing here, and quietly substituting them would + # hand them a scope they are not part of. + if ActionAgent.multi_tenant? + resolve_actor( + ActionAgent.current_account_resolver, + ActionAgent.current_account_method, + :current_owner + ) + else + current_user + end + end + + # Prefers the host app's lambda. A named method is only called when the + # controller really responds to it and it isn't the accessor we are + # already inside — configuring current_user_method = :current_user is + # the obvious thing to write, and it would otherwise recurse forever. + # + # The re-entrancy guard covers the same mistake in lambda form. The + # engine's controllers are their own base class, so the natural-looking + # `->(c) { c.current_user }` resolves to *this* method rather than the + # host app's, and without the guard it recurses until SystemStackError. + # Degrading to nil turns a 500 into an unresolved owner, which now + # scopes to nothing rather than to everything. + def resolve_actor(resolver, method_name, own_name) + @resolving_actors ||= {} + return nil if @resolving_actors[own_name] + + @resolving_actors[own_name] = true + begin + return resolver.call(self) if resolver + + return nil if method_name.nil? || method_name.to_sym == own_name + return nil unless respond_to?(method_name, true) + + send(method_name) + ensure + @resolving_actors[own_name] = false + end + rescue NoMethodError + nil + end + end +end diff --git a/actionagent/app/controllers/action_agent/dashboard_controller.rb b/actionagent/app/controllers/action_agent/dashboard_controller.rb new file mode 100644 index 00000000..f3e71d4b --- /dev/null +++ b/actionagent/app/controllers/action_agent/dashboard_controller.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 + # The React dashboard brings its own chrome, so it does not use the + # server-rendered layout the traces views share. + layout -> { ActionAgent.layout || "action_agent/react" } + + def index + render "action_agent/dashboard/index", locals: { props: props } + end + + private + + 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 owner_agents + ActionAgent.agents_for(current_owner) + end + + def meta_props + { + activeagentVersion: ActiveAgent::VERSION, + providers: Agent::PROVIDERS, + presetTypes: Agent::PRESET_TYPES, + instructionSets: Agent::INSTRUCTION_SETS, + availableTools: Agent::AVAILABLE_TOOLS, + executionEnabled: ActionAgent.execution_enabled?, + multiTenant: ActionAgent.multi_tenant?, + upgradeUrl: ActionAgent.upgrade_url + } + end + + def serialize_agents(agents) + agents = agents.to_a + scorecards = AgentScorecard.for_agents(agents) + + agents.map do |agent| + { + id: agent.id, + name: agent.name, + slug: agent.slug, + description: agent.description, + provider: agent.provider, + model: agent.model, + status: agent.status, + presetType: agent.preset_type, + appearance: agent.appearance, + versionCount: agent.version_count, + 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 { name: "Guest" } unless current_user + + { + id: current_user.id, + 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 + + { + id: current_owner.id, + 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 diff --git a/actionagent/app/controllers/action_agent/traces_controller.rb b/actionagent/app/controllers/action_agent/traces_controller.rb new file mode 100644 index 00000000..30dcb934 --- /dev/null +++ b/actionagent/app/controllers/action_agent/traces_controller.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +module ActionAgent + # Controller for viewing telemetry traces. + # + # Provides: + # - List view with filtering and pagination + # - Detail view with span timeline + # - Metrics overview + # - Live updates via Turbo Streams + class TracesController < ApplicationController + def index + @traces = fetch_traces + @metrics = calculate_metrics + + respond_to do |format| + format.html + # turbo-rails is optional; responding to an unregistered MIME + # type raises in host apps without it. + format.turbo_stream if turbo_stream_available? + end + end + + def show + @trace = scoped_traces.find(params[:id]) + end + + def metrics + @metrics = calculate_metrics + @agent_stats = agent_statistics + @time_series = time_series_data + + respond_to do |format| + format.html + format.turbo_stream if turbo_stream_available? + end + end + + private + + def turbo_stream_available? + Mime::Type.lookup_by_extension(:turbo_stream).present? + end + + # All queries honor the configured trace model and, in multi-tenant + # mode, the current owner's account (for_account no-ops otherwise). + # + # In multi-tenant mode an unresolvable owner returns nothing rather + # than falling through: current_owner degrades to current_user when + # current_account_method is unset, which would scope by a user id + # against account_id — another tenant's traces. + def scoped_traces + model = ActionAgent.trace_model + return model.none if ActionAgent.multi_tenant? && current_owner.nil? + + model.for_account(current_owner) + end + + def fetch_traces + traces = scoped_traces.recent + + traces = traces.for_agent(params[:agent]) if params[:agent].present? + traces = traces.with_errors if params[:status] == "error" + traces = traces.for_service(params[:service]) if params[:service].present? + + if params[:start_date].present? && params[:end_date].present? + traces = traces.for_date_range( + Time.parse(params[:start_date]), + Time.parse(params[:end_date]) + ) + end + + traces.limit(params[:limit] || 50) + end + + def calculate_metrics + traces = scoped_traces.where( + "created_at > ?", 24.hours.ago + ) + + { + total_traces: traces.count, + total_tokens: traces.sum(:total_input_tokens) + traces.sum(:total_output_tokens), + avg_duration_ms: traces.average(:total_duration_ms)&.round(2) || 0, + error_rate: calculate_error_rate(traces), + unique_agents: traces.distinct.count(:agent_class) + } + end + + def calculate_error_rate(traces) + total = traces.count + return 0.0 if total.zero? + + errors = traces.with_errors.count + ((errors.to_f / total) * 100).round(2) + end + + def agent_statistics + scoped_traces + .where("created_at > ?", 24.hours.ago) + .group(:agent_class) + .select( + "agent_class", + "COUNT(*) as trace_count", + "SUM(total_input_tokens + total_output_tokens) as total_tokens", + "AVG(total_duration_ms) as avg_duration", + "SUM(CASE WHEN status = 'ERROR' THEN 1 ELSE 0 END) as error_count" + ) + end + + def time_series_data + scoped_traces + .where("created_at > ?", 1.hour.ago) + .group_by_minute(:created_at) + .count + rescue NoMethodError + # Fallback if groupdate gem not available + {} + end + end +end diff --git a/actionagent/app/jobs/action_agent/agent_execution_job.rb b/actionagent/app/jobs/action_agent/agent_execution_job.rb new file mode 100644 index 00000000..fcf19faf --- /dev/null +++ b/actionagent/app/jobs/action_agent/agent_execution_job.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module ActionAgent + class AgentExecutionJob < ApplicationJob + queue_as :agents + + 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_record = run.agent + + # Build the agent class dynamically based on configuration + result = execute_agent(agent_record, run) + + run.update!( + output: result[:output], + output_metadata: result[:metadata], + status: :complete, + completed_at: Time.current, + duration_ms: ((Time.current - run.started_at) * 1000).to_i, + input_tokens: result.dig(:usage, :input_tokens), + 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, + completed_at: Time.current, + 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_record, run) + AgentExecutionService.call(agent_record, run) + end + end +end diff --git a/actionagent/app/jobs/action_agent/application_job.rb b/actionagent/app/jobs/action_agent/application_job.rb new file mode 100644 index 00000000..1cc7afed --- /dev/null +++ b/actionagent/app/jobs/action_agent/application_job.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module ActionAgent + # Base class for all Dashboard engine jobs. + class ApplicationJob < ActiveJob::Base + # Retry failed jobs + retry_on StandardError, wait: :polynomially_longer, attempts: 3 + + # Discard jobs for records that no longer exist + discard_on ActiveRecord::RecordNotFound + end +end diff --git a/lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb b/actionagent/app/jobs/action_agent/process_telemetry_traces_job.rb similarity index 82% rename from lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb rename to actionagent/app/jobs/action_agent/process_telemetry_traces_job.rb index 9c202f94..f5b53da4 100644 --- a/lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb +++ b/actionagent/app/jobs/action_agent/process_telemetry_traces_job.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module ActiveAgent +module ActionAgent # Processes telemetry traces received from ActiveAgent clients. # # This job handles the asynchronous processing of trace data to avoid @@ -10,14 +10,14 @@ module ActiveAgent # - Handles any errors gracefully # # @example Local mode - # ActiveAgent::ProcessTelemetryTracesJob.perform_later( + # ActionAgent::ProcessTelemetryTracesJob.perform_later( # traces: [...], # sdk_info: { name: "activeagent", version: "0.5.0" }, # received_at: "2024-01-15T10:30:00Z" # ) # # @example Multi-tenant mode - # ActiveAgent::ProcessTelemetryTracesJob.perform_later( + # ActionAgent::ProcessTelemetryTracesJob.perform_later( # account_id: 1, # traces: [...], # sdk_info: { name: "activeagent", version: "0.5.0" }, @@ -34,7 +34,7 @@ def perform(account_id: nil, traces:, sdk_info:, received_at:) account = resolve_account(account_id) # In multi-tenant mode, require an account - if ActiveAgent::Dashboard.multi_tenant? && account.nil? + if ActionAgent.multi_tenant? && account.nil? Rails.logger.warn("[ProcessTelemetryTracesJob] Skipping traces - no valid account") return end @@ -63,19 +63,19 @@ def perform(account_id: nil, traces:, sdk_info:, received_at:) private def resolve_account(account_id) - return nil unless ActiveAgent::Dashboard.multi_tenant? + return nil unless ActionAgent.multi_tenant? return nil unless account_id - account_class = ActiveAgent::Dashboard.account_class.constantize + account_class = ActionAgent.account_class.constantize account_class.find_by(id: account_id) end def process_trace(trace, sdk_info, account) - model = ActiveAgent::Dashboard.trace_model + model = ActionAgent.trace_model # Build uniqueness scope scope = model.where(trace_id: trace["trace_id"]) - scope = scope.where(account: account) if ActiveAgent::Dashboard.multi_tenant? && account + scope = scope.where(account: account) if ActionAgent.multi_tenant? && account # Skip if trace already exists (idempotency) return if scope.exists? diff --git a/actionagent/app/jobs/action_agent/sandbox_cleanup_job.rb b/actionagent/app/jobs/action_agent/sandbox_cleanup_job.rb new file mode 100644 index 00000000..ed122a9e --- /dev/null +++ b/actionagent/app/jobs/action_agent/sandbox_cleanup_job.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module ActionAgent + class SandboxCleanupJob < ApplicationJob + queue_as :sandboxes + + # Clean up Cloud Run resources for an expired sandbox + def perform(sandbox_session_id) + sandbox = SandboxSession.find_by(id: sandbox_session_id) + return unless sandbox + + Rails.logger.info("Cleaning up sandbox: #{sandbox.session_id}") + + # 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 + + # Optionally delete old sandbox records + # For now, keep for analytics + sandbox.update!(cloud_run_url: nil, cloud_run_job_id: nil) + end + + # Periodic cleanup of all expired sandboxes + def self.cleanup_expired! + SandboxSession.expired_sessions.active.find_each do |sandbox| + sandbox.expire! + end + end + + 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/actionagent/app/jobs/action_agent/sandbox_provision_job.rb b/actionagent/app/jobs/action_agent/sandbox_provision_job.rb new file mode 100644 index 00000000..e3d04b45 --- /dev/null +++ b/actionagent/app/jobs/action_agent/sandbox_provision_job.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module ActionAgent + class SandboxProvisionJob < ApplicationJob + 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) + sandbox = SandboxSession.find(sandbox_session_id) + return if sandbox.ready? || sandbox.expired? + + # 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 ActionAgent.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 simulate_provisioning(sandbox) + # Simulate a small delay for provisioning + sleep(0.5) + + sandbox.mark_ready!( + cloud_run_url: "http://localhost:3000/api/sandbox", + cloud_run_job_id: "local-#{sandbox.session_id[0..7]}" + ) + end + + def broadcast_sandbox_update(sandbox) + ActionCable.server.broadcast( + "sandbox_#{sandbox.session_id}", + { type: "status_update", sandbox: sandbox.summary } + ) + end + end +end diff --git a/actionagent/app/jobs/action_agent/sandbox_run_job.rb b/actionagent/app/jobs/action_agent/sandbox_run_job.rb new file mode 100644 index 00000000..728d62df --- /dev/null +++ b/actionagent/app/jobs/action_agent/sandbox_run_job.rb @@ -0,0 +1,285 @@ +# frozen_string_literal: true + +module ActionAgent + 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 diff --git a/actionagent/app/jobs/action_agent/trace_retention_job.rb b/actionagent/app/jobs/action_agent/trace_retention_job.rb new file mode 100644 index 00000000..8832e66f --- /dev/null +++ b/actionagent/app/jobs/action_agent/trace_retention_job.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module ActionAgent + # 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: + # + # ActionAgent.trace_retention = 30.days + # + # # config/recurring.yml + # trace_retention: + # class: ActionAgent::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 = ActionAgent.trace_retention + return if window.nil? + + if ActionAgent.multi_tenant? && (owners = ActionAgent.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 + ActionAgent.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 diff --git a/actionagent/app/models/action_agent/agent.rb b/actionagent/app/models/action_agent/agent.rb new file mode 100644 index 00000000..1a4364e4 --- /dev/null +++ b/actionagent/app/models/action_agent/agent.rb @@ -0,0 +1,343 @@ +# frozen_string_literal: true + +module ActionAgent + class Agent < ApplicationRecord + include Ownable + owned_by :user, :account + + 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 + ActionAgent.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 + validate :validate_action_prompts + + # Status enum + # `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 + after_create :create_initial_version + after_update :create_version_on_config_change, if: :configuration_changed? + + # Scopes + scope :active_agents, -> { where(status: :active) } + scope :by_provider, ->(provider) { where(provider: provider) } + # 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? + # Cast both sides: @> is a jsonb operator, and a host app mounting + # the engine over its own pre-existing tables may have declared the + # column as json. + where("tools::jsonb @> ?::jsonb", [ tool ].to_json) + else + where("tools LIKE ?", "%\"#{tool}\"%") + end + } + + # Available presets matching AgentAvatar component + PRESET_TYPES = %w[ + terminal webDeveloper documentAnalysis writing translation + playwright research imageAnalysis computerUse productDesign + ].freeze + + # Available instruction sets + INSTRUCTION_SETS = %w[ + github ruby rails aws gcp python typescript docker kubernetes + ].freeze + + # Available tools/MCPs + AVAILABLE_TOOLS = %w[ + terminal playwright filesystem code database slack fetch search edit translate memory agents + ].freeze + + # Available providers + 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 + { + name: name, + description: description, + provider: provider, + model: model, + instructions: instructions, + action_prompts: action_prompts, + preset_type: preset_type, + appearance: appearance, + instruction_sets: instruction_sets, + tools: tools, + mcp_servers: mcp_servers, + model_config: model_config, + response_format: response_format + } + end + + # Restore from a version + 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"], + tools: config["tools"], + mcp_servers: config["mcp_servers"], + model_config: config["model_config"], + response_format: config["response_format"] + ) + end + + # Get the latest version + 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 + end + + # Generate Ruby agent class code + def to_agent_class_code + <<~RUBY + class #{agent_class_name || name.camelize}Agent < ApplicationAgent + generate_with :#{provider}, model: "#{model}"#{model_config_code} + + def perform + prompt#{instructions_code} + end + end + RUBY + end + + # Execute a run with this agent + 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 + AgentExecutionJob.perform_later(run.id) + + run + end + + # Quick test execution (synchronous) + 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, + started_at: Time.current + ) + + begin + # Build and execute the agent + result = AgentExecutionService.call(self, run) + + run.update!( + output: result[:output], + output_metadata: result[:metadata], + status: :complete, + completed_at: Time.current, + duration_ms: ((Time.current - run.started_at) * 1000).to_i, + input_tokens: result.dig(:usage, :input_tokens), + output_tokens: result.dig(:usage, :output_tokens), + total_tokens: result.dig(:usage, :total_tokens) + ) + rescue => e + run.update!( + status: :failed, + completed_at: Time.current, + error_message: e.message, + error_backtrace: e.backtrace&.first(10)&.join("\n") + ) + end + + run + end + + 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 + + # Checked globally rather than per owner: a host app may have a global + # unique index on slug (ours does), and suffixing costs one query. + counter = 1 + while self.class.exists?(slug: slug) + self.slug = "#{base_slug}-#{counter}" + counter += 1 + end + end + + def create_initial_version + agent_versions.create!( + version_number: 1, + change_summary: "Initial creation", + configuration_snapshot: configuration_snapshot + ) + 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? { |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 { |key| VERSIONED_FIELDS.include?(key) } + + agent_versions.create!( + version_number: next_version, + change_summary: "Updated: #{changed_fields.join(', ')}", + configuration_snapshot: configuration_snapshot + ) + 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? + + configs = model_config.map { |k, v| "#{k}: #{v.inspect}" }.join(", ") + ", #{configs}" + end + + def instructions_code + return "" if instructions.blank? + + "\n prompt instructions: <<~INSTRUCTIONS\n #{instructions.gsub("\n", "\n ")}\n INSTRUCTIONS" + end + end +end diff --git a/actionagent/app/models/action_agent/agent_context.rb b/actionagent/app/models/action_agent/agent_context.rb new file mode 100644 index 00000000..10ec1554 --- /dev/null +++ b/actionagent/app/models/action_agent/agent_context.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/models/action_agent/agent_generation.rb b/actionagent/app/models/action_agent/agent_generation.rb new file mode 100644 index 00000000..8d416db0 --- /dev/null +++ b/actionagent/app/models/action_agent/agent_generation.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module ActionAgent + # 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? + + ActionAgent.trace_model.find_by(trace_id: trace_id) + end + end +end diff --git a/actionagent/app/models/action_agent/agent_memory.rb b/actionagent/app/models/action_agent/agent_memory.rb new file mode 100644 index 00000000..c28d1024 --- /dev/null +++ b/actionagent/app/models/action_agent/agent_memory.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/models/action_agent/agent_memory_entry.rb b/actionagent/app/models/action_agent/agent_memory_entry.rb new file mode 100644 index 00000000..cf869767 --- /dev/null +++ b/actionagent/app/models/action_agent/agent_memory_entry.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/models/action_agent/agent_message.rb b/actionagent/app/models/action_agent/agent_message.rb new file mode 100644 index 00000000..4319f820 --- /dev/null +++ b/actionagent/app/models/action_agent/agent_message.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/models/action_agent/agent_run.rb b/actionagent/app/models/action_agent/agent_run.rb new file mode 100644 index 00000000..394b8fb0 --- /dev/null +++ b/actionagent/app/models/action_agent/agent_run.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +module ActionAgent + class AgentRun < ApplicationRecord + belongs_to :agent + + # Status enum + enum :status, { pending: 0, running: 1, complete: 2, failed: 3, cancelled: 4 } + + # Validations + validates :trace_id, presence: true + + # Scopes + scope :recent, -> { order(created_at: :desc) } + scope :successful, -> { where(status: :complete) } + scope :failed_runs, -> { where(status: :failed) } + scope :today, -> { where("created_at >= ?", Time.current.beginning_of_day) } + + # Callbacks + before_validation :set_trace_id, on: :create + after_update_commit :broadcast_update, if: :saved_change_to_status? + + # Add a log entry + def add_log(message, level: :info) + new_logs = logs || [] + new_logs << { + timestamp: Time.current.iso8601, + level: level.to_s, + message: message + } + 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? + return nil unless started_at && completed_at + + ((completed_at - started_at) * 1000).to_i + end + + # Check if run is still in progress + def in_progress? + pending? || running? + end + + # Check if run is finished + def finished? + complete? || failed? || cancelled? + end + + # Get a summary for display + def summary + { + id: id, + status: status, + input_preview: input_prompt&.truncate(100), + 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 + } + end + + # Stream output updates via ActionCable + def broadcast_update + 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 + def cancel! + return unless in_progress? + + update!( + status: :cancelled, + completed_at: Time.current, + error_message: "Cancelled by user" + ) + broadcast_update + end + + private + + def set_trace_id + self.trace_id ||= SecureRandom.uuid + end + end +end diff --git a/actionagent/app/models/action_agent/agent_template.rb b/actionagent/app/models/action_agent/agent_template.rb new file mode 100644 index 00000000..1cb0fb11 --- /dev/null +++ b/actionagent/app/models/action_agent/agent_template.rb @@ -0,0 +1,182 @@ +# frozen_string_literal: true + +module ActionAgent + class AgentTemplate < ApplicationRecord + # Validations + validates :name, presence: true + validates :slug, presence: true, uniqueness: true + validates :category, presence: true + + # Scopes + scope :featured, -> { where(featured: true) } + scope :by_category, ->(cat) { where(category: cat) } + scope :popular, -> { order(usage_count: :desc) } + scope :public_templates, -> { where(public: true) } + scope :free_tier, -> { where(free_tier: true) } + + # Categories + CATEGORIES = %w[ + productivity + development + research + creative + data + automation + ].freeze + + # 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, + model: model, + instructions: instructions, + preset_type: preset_type, + appearance: appearance, + instruction_sets: instruction_sets, + tools: tools, + mcp_servers: mcp_servers, + model_config: model_config, + status: :draft + ) + + if agent.save + increment!(:usage_count) + end + + agent + end + + # Seed default templates + def self.seed_defaults! + templates = [ + { + name: "Code Assistant", + slug: "code-assistant", + description: "A helpful coding assistant that can explain code, suggest improvements, and help debug issues.", + category: "development", + provider: "openai", + model: "gpt-4o", + preset_type: "terminal", + appearance: { hat: "fedora", heldItem: "terminal" }, + instruction_sets: %w[github ruby rails typescript], + tools: %w[terminal code filesystem], + 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 + }, + { + name: "Research Assistant", + slug: "research-assistant", + description: "Helps research topics, summarize information, and organize findings.", + category: "research", + provider: "anthropic", + model: "claude-sonnet-5", + preset_type: "research", + appearance: { hat: "safari", heldItem: "magnifyingGlass" }, + instruction_sets: %w[github python], + tools: %w[fetch search memory], + 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 + }, + { + name: "Writing Assistant", + slug: "writing-assistant", + description: "Helps with writing, editing, and improving text content.", + category: "creative", + provider: "openai", + model: "gpt-4o", + preset_type: "writing", + appearance: { hat: "fedora", hatAccessory: "feather", heldItem: "scroll" }, + instruction_sets: [], + tools: %w[edit translate], + 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 + }, + { + name: "Browser Automation", + slug: "browser-automation", + description: "Automates web browsing tasks like form filling, data extraction, and testing.", + category: "automation", + provider: "anthropic", + model: "claude-sonnet-5", + preset_type: "playwright", + appearance: { hat: "fedora", hatAccessory: "theaterMasks", heldItem: "browser" }, + instruction_sets: %w[typescript], + tools: %w[playwright filesystem], + 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 + }, + { + name: "Data Analyst", + slug: "data-analyst", + description: "Analyzes data, creates visualizations, and provides insights.", + category: "data", + provider: "openai", + model: "gpt-4o", + preset_type: "documentAnalysis", + appearance: { hat: "fedora", heldItem: "document" }, + instruction_sets: %w[python], + tools: %w[code database filesystem], + 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 + }, + { + name: "DevOps Assistant", + slug: "devops-assistant", + description: "Helps with infrastructure, deployments, and system administration.", + category: "development", + provider: "openai", + model: "gpt-4o", + preset_type: "terminal", + appearance: { hat: "fedora", heldItem: "terminal" }, + instruction_sets: %w[docker kubernetes aws gcp], + tools: %w[terminal filesystem code], + 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 + }, + { + name: "PlaywrightMCP Demo", + slug: "playwright-mcp-demo", + description: "Free browser automation demo using Playwright MCP. Navigate sites, take screenshots, and extract content.", + category: "automation", + provider: "anthropic", + model: "claude-sonnet-5", + preset_type: "playwright", + appearance: { hat: "fedora", hatAccessory: "theaterMasks", heldItem: "browser" }, + instruction_sets: [], + tools: %w[playwright], + mcp_servers: { + playwright: { + command: "npx", + args: [ "-y", "@anthropic/mcp-server-playwright" ] + } + }, + model_config: { temperature: 0.2, max_tokens: 4096 }, + instructions: "You are a browser automation assistant using Playwright MCP.\n\nAvailable actions:\n- browser_navigate: Go to a URL\n- browser_snapshot: Get the accessibility tree\n- browser_click: Click on an element\n- browser_type: Type text into an input\n- browser_take_screenshot: Capture the page\n- browser_wait_for: Wait for text or element\n\nGuidelines:\n1. Always take a snapshot first to understand the page\n2. Use element refs from snapshots for interactions\n3. Wait for page loads before taking actions\n4. Handle errors gracefully\n5. Limit yourself to 10 steps maximum\n\nAlways describe what you see and what actions you're taking.", + icon: "🎭", + featured: true, + free_tier: true + } + ] + + templates.each do |template_attrs| + AgentTemplate.find_or_create_by!(slug: template_attrs[:slug]) do |t| + t.assign_attributes(template_attrs) + end + end + end + end +end diff --git a/actionagent/app/models/action_agent/agent_version.rb b/actionagent/app/models/action_agent/agent_version.rb new file mode 100644 index 00000000..8dd77e56 --- /dev/null +++ b/actionagent/app/models/action_agent/agent_version.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module ActionAgent + class AgentVersion < ApplicationRecord + belongs_to :agent + + validates :version_number, presence: true, uniqueness: { scope: :agent_id } + validates :configuration_snapshot, presence: true + + # Scopes + scope :recent, -> { order(version_number: :desc) } + scope :by_version, ->(num) { where(version_number: num) } + + # Compare two versions + def diff(other_version) + return {} unless other_version + + changes = {} + configuration_snapshot.each do |key, value| + other_value = other_version.configuration_snapshot[key] + if value != other_value + changes[key] = { from: other_value, to: value } + end + end + changes + end + + # Get previous version + def previous + agent.agent_versions.where("version_number < ?", version_number).order(version_number: :desc).first + end + + # Get next version + def next_version + agent.agent_versions.where("version_number > ?", version_number).order(version_number: :asc).first + end + + # Check if this is the latest version + def latest? + agent.latest_version&.id == id + end + + # Check if this is the initial version + def initial? + version_number == 1 + end + end +end diff --git a/actionagent/app/models/action_agent/api_key.rb b/actionagent/app/models/action_agent/api_key.rb new file mode 100644 index 00000000..c0189598 --- /dev/null +++ b/actionagent/app/models/action_agent/api_key.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 ActionAgent.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 + owned_by :account, :user + + encrypts :token, deterministic: true if ActionAgent.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 diff --git a/actionagent/app/models/action_agent/application_record.rb b/actionagent/app/models/action_agent/application_record.rb new file mode 100644 index 00000000..70b6457b --- /dev/null +++ b/actionagent/app/models/action_agent/application_record.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module ActionAgent + # Base class for all Dashboard engine models. + # + # Table names come from ActionAgent.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 + include AdapterAware + + 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 + def owner_association = nil + def for_owner(_owner) = all + end + end +end diff --git a/actionagent/app/models/action_agent/evaluation.rb b/actionagent/app/models/action_agent/evaluation.rb new file mode 100644 index 00000000..d84ae63c --- /dev/null +++ b/actionagent/app/models/action_agent/evaluation.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/models/action_agent/evaluation_run.rb b/actionagent/app/models/action_agent/evaluation_run.rb new file mode 100644 index 00000000..da67b517 --- /dev/null +++ b/actionagent/app/models/action_agent/evaluation_run.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/models/action_agent/model_pricing.rb b/actionagent/app/models/action_agent/model_pricing.rb new file mode 100644 index 00000000..76cb476c --- /dev/null +++ b/actionagent/app/models/action_agent/model_pricing.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/models/action_agent/provider_key.rb b/actionagent/app/models/action_agent/provider_key.rb new file mode 100644 index 00000000..294a8127 --- /dev/null +++ b/actionagent/app/models/action_agent/provider_key.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 + owned_by :account, :user + + encrypts :credential if ActionAgent.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, "has already been taken") if siblings.exists?(provider: provider) + end + end +end diff --git a/actionagent/app/models/action_agent/recording_action.rb b/actionagent/app/models/action_agent/recording_action.rb new file mode 100644 index 00000000..bdc40bc1 --- /dev/null +++ b/actionagent/app/models/action_agent/recording_action.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +module ActionAgent + class RecordingAction < ApplicationRecord + belongs_to :session_recording + has_one :snapshot, class_name: "RecordingSnapshot", dependent: :nullify + + ACTION_TYPES = %w[ + navigate + click + type + scroll + snapshot + select + hover + drag + file_upload + dialog + evaluate + wait + form_fill + key_press + focus + submit + handoff + user_action + completion + ].freeze + + validates :action_type, presence: true, inclusion: { in: ACTION_TYPES } + validates :sequence, presence: true, uniqueness: { scope: :session_recording_id } + validates :timestamp_ms, presence: true + + scope :ordered, -> { order(:sequence) } + scope :with_screenshots, -> { where.not(screenshot_key: nil) } + + # Get the screenshot URL (signed URL from storage) + def screenshot_url(expires_in: 15.minutes) + return nil unless screenshot_key.present? + + 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? + + SessionRecordingService.fetch_snapshot(dom_snapshot_key) + end + + # Format for API response + def as_json_for_api + { + id: id, + action_type: action_type, + sequence: sequence, + timestamp_ms: timestamp_ms, + selector: selector, + value: redacted_value, + screenshot_url: screenshot_url, + has_dom_snapshot: dom_snapshot_key.present?, + metadata: safe_metadata, + created_at: created_at.iso8601 + } + end + + # Get browser state at this action (for handoff) + def browser_state + { + url: extract_url, + form_values: metadata["form_values"], + scroll_position: metadata["scroll_position"], + active_element: selector, + action_type: action_type + } + end + + private + + def redacted_value + return value unless should_redact? + + "[REDACTED]" + end + + def should_redact? + return false unless value.present? + + sensitive_patterns = [ + /password/i, + /credit.?card/i, + /cvv/i, + /ssn/i, + /social.?security/i, + /\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) } + value_is_sensitive = sensitive_patterns.any? { |p| value.match?(p) } + + selector_is_sensitive || value_is_sensitive + end + + def safe_metadata + # Remove any sensitive data from metadata + metadata.except("password", "credit_card", "cvv", "ssn") + end + + def extract_url + case action_type + when "navigate" + value + else + metadata["url"] || metadata["page_url"] + end + end + end +end diff --git a/actionagent/app/models/action_agent/recording_snapshot.rb b/actionagent/app/models/action_agent/recording_snapshot.rb new file mode 100644 index 00000000..5d926ef9 --- /dev/null +++ b/actionagent/app/models/action_agent/recording_snapshot.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +module ActionAgent + class RecordingSnapshot < ApplicationRecord + belongs_to :session_recording + belongs_to :recording_action, optional: true + + # Guarded: a host app created with --skip-active-storage (or --minimal) + # has no has_one_attached, and the gem depends on railties rather than + # rails, so it may not even have the gem to require. Without this the + # engine dies during eager load at boot. + has_one_attached :file if defined?(ActiveStorage) + + # Whether file attachment is available in this host app. + def self.attachments_available? + defined?(ActiveStorage) && method_defined?(:file) + end + + SNAPSHOT_TYPES = %w[screenshot dom full_page].freeze + + validates :storage_key, presence: true, uniqueness: true + validates :snapshot_type, presence: true, inclusion: { in: SNAPSHOT_TYPES } + + scope :screenshots, -> { where(snapshot_type: "screenshot") } + scope :dom_snapshots, -> { where(snapshot_type: "dom") } + scope :ordered, -> { order(:created_at) } + + # Get a signed URL for the file + def signed_url(expires_in: 15.minutes) + return nil unless self.class.attachments_available? + return nil unless file.attached? + + file.url(expires_in: expires_in) + rescue StandardError + # Fallback if storage not configured + nil + end + + # Store file data. Without Active Storage the row still records the + # snapshot's metadata; only the blob is skipped. + def store!(data, filename: nil, content_type: nil) + content_type ||= infer_content_type + filename ||= generate_filename + + if self.class.attachments_available? + file.attach( + io: StringIO.new(data), + filename: filename, + content_type: content_type + ) + end + + update!(file_size_bytes: data.bytesize) + end + + # For API response + def as_json_for_api + { + id: id, + storage_key: storage_key, + snapshot_type: snapshot_type, + width: width, + height: height, + file_size_bytes: file_size_bytes, + url: signed_url, + created_at: created_at.iso8601 + } + end + + private + + def infer_content_type + case snapshot_type + when "screenshot", "full_page" + "image/png" + when "dom" + "text/html" + else + "application/octet-stream" + end + end + + def generate_filename + ext = snapshot_type == "dom" ? "html" : "png" + "#{storage_key.tr('/', '_')}.#{ext}" + end + end +end diff --git a/actionagent/app/models/action_agent/sandbox_instance_tier.rb b/actionagent/app/models/action_agent/sandbox_instance_tier.rb new file mode 100644 index 00000000..48819bfc --- /dev/null +++ b/actionagent/app/models/action_agent/sandbox_instance_tier.rb @@ -0,0 +1,368 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/models/action_agent/sandbox_run.rb b/actionagent/app/models/action_agent/sandbox_run.rb new file mode 100644 index 00000000..ed0910f2 --- /dev/null +++ b/actionagent/app/models/action_agent/sandbox_run.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module ActionAgent + # SandboxRun tracks individual agent task executions within a sandbox container. + # This model uses SQLite in sandbox mode for isolated, ephemeral storage. + class SandboxRun < ApplicationRecord + enum :status, { + pending: 0, + running: 1, + completed: 2, + failed: 3, + cancelled: 4 + } + + validates :task, presence: true + validates :status, presence: true + + scope :recent, -> { order(created_at: :desc) } + scope :completed_runs, -> { where(status: [ :completed, :failed ]) } + + # Summary for API responses + def summary + { + id: id, + task: task.truncate(100), + status: status, + duration_ms: duration_ms, + tokens_used: tokens_used, + created_at: created_at&.iso8601, + completed_at: completed_at&.iso8601 + } + end + + # Detailed info including full result + def details + summary.merge( + task: task, + result: result, + error: error, + screenshots: screenshots || [], + started_at: started_at&.iso8601 + ) + end + end +end diff --git a/actionagent/app/models/action_agent/sandbox_session.rb b/actionagent/app/models/action_agent/sandbox_session.rb new file mode 100644 index 00000000..a4caccff --- /dev/null +++ b/actionagent/app/models/action_agent/sandbox_session.rb @@ -0,0 +1,152 @@ +# frozen_string_literal: true + +module ActionAgent + class SandboxSession < ApplicationRecord + include Ownable + owned_by :user, :account + + belongs_to :agent_template, optional: true + + # Session statuses + enum :status, { + pending: 0, + provisioning: 1, + ready: 2, + running: 3, + completed: 4, + expired: 5, + failed: 6 + } + + # Sandbox types + SANDBOX_TYPES = %w[playwright_mcp terminal research].freeze + + # Free tier limits + FREE_TIER_LIMITS = { + max_runs: 10, + timeout_seconds: 300, + max_tokens: 50_000, + session_duration_minutes: 15 + }.freeze + + # Validations + validates :session_id, presence: true, uniqueness: true + validates :sandbox_type, inclusion: { in: SANDBOX_TYPES } + + # Callbacks + before_validation :generate_session_id, on: :create + before_create :set_expiration + + # Scopes + scope :active, -> { where(status: [ :pending, :provisioning, :ready, :running ]) } + scope :expired_sessions, -> { where("expires_at < ?", Time.current) } + scope :by_type, ->(type) { where(sandbox_type: type) } + scope :anonymous, -> { where(user_id: nil) } + scope :recent, -> { order(created_at: :desc) } + + # Check if session is still valid + def active? + !expired? && !failed? && !completed? && expires_at > Time.current + end + + # Check if can run more tasks + def can_run? + active? && runs_count < max_runs + end + + # Record a new run (thread-safe for parallel execution) + def record_run!(task:, result:, duration_ms:, tokens:, screenshots: [], provider: nil) + run = { + id: SecureRandom.uuid, + task: task, + result: result, + duration_ms: duration_ms, + tokens: tokens, + screenshots: screenshots, + provider: provider, + status: "completed", + created_at: Time.current.iso8601 + } + + # Use pessimistic locking to prevent race conditions when multiple providers run in parallel + with_lock do + reload # Reload to get the latest state + self.runs = runs + [ run ] + self.runs_count = runs.size + self.total_tokens += tokens + self.total_duration_ms += duration_ms + self.last_activity_at = Time.current + save! + end + + run + end + + # Provision the Cloud Run sandbox + def provision! + return if provisioning? || ready? + + update!(status: :provisioning) + + # In development, run synchronously for immediate feedback + if Rails.env.development? || Rails.env.test? + SandboxProvisionJob.perform_now(id) + else + SandboxProvisionJob.perform_later(id) + end + end + + # Mark as ready with Cloud Run URL + def mark_ready!(cloud_run_url:, cloud_run_job_id: nil) + update!( + status: :ready, + cloud_run_url: cloud_run_url, + cloud_run_job_id: cloud_run_job_id + ) + end + + # Expire the session + def expire! + update!(status: :expired) + # Cleanup Cloud Run resources + SandboxCleanupJob.perform_later(id) if cloud_run_job_id.present? + end + + # Summary for API responses + def summary + { + id: id, + session_id: session_id, + sandbox_type: sandbox_type, + status: status, + runs_count: runs_count, + max_runs: max_runs, + total_tokens: total_tokens, + expires_at: expires_at&.iso8601, + created_at: created_at.iso8601, + cloud_run_url: cloud_run_url + } + end + + # Detailed info including runs + def details + summary.merge( + runs: runs, + total_duration_ms: total_duration_ms, + last_activity_at: last_activity_at&.iso8601 + ) + end + + private + + def generate_session_id + self.session_id ||= SecureRandom.uuid + end + + 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/actionagent/app/models/action_agent/session_recording.rb b/actionagent/app/models/action_agent/session_recording.rb new file mode 100644 index 00000000..cedcf382 --- /dev/null +++ b/actionagent/app/models/action_agent/session_recording.rb @@ -0,0 +1,178 @@ +# frozen_string_literal: true + +module ActionAgent + class SessionRecording < ApplicationRecord + include Ownable + owned_by :user, :account + + belongs_to :agent_run, optional: true + belongs_to :sandbox_session, optional: true + + has_many :recording_actions, dependent: :destroy + has_many :recording_snapshots, dependent: :destroy + + enum :status, { recording: 0, completed: 1, failed: 2 } + + validates :status, presence: true + validate :must_have_parent, unless: -> { demo_recording? || user_session? } + + scope :recent, -> { order(created_at: :desc) } + scope :for_agent, ->(agent_id) { joins(:agent_run).where(agent_runs: { agent_id: agent_id }) } + scope :demo, -> { where(name: "lander_demo") } + scope :user_sessions, -> { where("name LIKE ?", "user_takeover_%") } + + # Check if this is a demo recording (doesn't require parent) + def demo_recording? + name&.start_with?("lander_") || name == "demo" + end + + # Check if this is a user takeover session (doesn't require parent) + def user_session? + name&.start_with?("user_takeover_") + end + + # Start a new recording session + def self.start!(agent_run: nil, sandbox_session: nil, name: nil) + create!( + agent_run: agent_run, + sandbox_session: sandbox_session, + name: name || generate_name(agent_run, sandbox_session), + status: :recording, + metadata: { started_at: Time.current.iso8601 } + ) + end + + # Start a user takeover session (for lander demo analytics) + def self.start_user_session!(visitor_id: nil, parent_demo_id: nil, page_url: nil) + create!( + name: "user_takeover_#{Time.current.strftime('%Y%m%d_%H%M%S')}_#{SecureRandom.hex(4)}", + status: :recording, + metadata: { + started_at: Time.current.iso8601, + session_type: "user_takeover", + visitor_id: visitor_id, + parent_demo_id: parent_demo_id, + page_url: page_url, + user_agent: nil # Will be set from request + } + ) + end + + # Record a browser action + def record_action!(action_type:, selector: nil, value: nil, screenshot: nil, dom_snapshot: nil, metadata: {}) + raise "Recording already completed" unless recording? + + action = recording_actions.create!( + action_type: action_type, + sequence: next_sequence, + timestamp_ms: elapsed_ms, + selector: selector, + value: value, + 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) + end + + increment!(:action_count) + action + end + + # Complete the recording + def complete! + return unless recording? + + update!( + status: :completed, + duration_ms: elapsed_ms, + metadata: metadata.merge(completed_at: Time.current.iso8601) + ) + end + + # Mark recording as failed + def fail!(error_message = nil) + return unless recording? + + update!( + status: :failed, + duration_ms: elapsed_ms, + metadata: metadata.merge( + failed_at: Time.current.iso8601, + error: error_message + ) + ) + end + + # Get timeline data for playback + def timeline + recording_actions.order(:sequence).map do |action| + { + id: action.id, + type: action.action_type, + sequence: action.sequence, + timestamp_ms: action.timestamp_ms, + selector: action.selector, + value: action.value, + screenshot_key: action.screenshot_key, + metadata: action.metadata + } + end + end + + private + + def must_have_parent + return if agent_run.present? || sandbox_session.present? + + errors.add(:base, "must belong to an agent_run or sandbox_session") + end + + def self.generate_name(agent_run, sandbox_session) + prefix = if agent_run&.agent + agent_run.agent.name.parameterize + elsif sandbox_session&.agent_template + sandbox_session.agent_template.name.parameterize + else + "session" + end + + "#{prefix}_#{Time.current.strftime('%Y%m%d_%H%M%S')}" + end + + def next_sequence + (recording_actions.maximum(:sequence) || 0) + 1 + end + + def elapsed_ms + ((Time.current - created_at) * 1000).to_i + end + + 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, + snapshot_type: snapshot_type, + file_size_bytes: data.bytesize + ) + end + + def generate_storage_key(snapshot_type, sequence = nil) + parts = [ "recordings", id, snapshot_type.to_s ] + parts << sequence.to_s if sequence + parts << SecureRandom.hex(8) + parts.join("/") + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb b/actionagent/app/models/action_agent/telemetry_trace.rb similarity index 79% rename from lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb rename to actionagent/app/models/action_agent/telemetry_trace.rb index 5fcd9438..edd1e9ca 100644 --- a/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb +++ b/actionagent/app/models/action_agent/telemetry_trace.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module ActiveAgent +module ActionAgent # Stores telemetry traces from ActiveAgent clients. # # Each trace represents a complete generation lifecycle, including prompt @@ -11,18 +11,20 @@ module ActiveAgent # - Multi-tenant mode: With account association (for activeagents.ai platform) # # @example Creating a trace from ingested data (local mode) - # ActiveAgent::TelemetryTrace.create_from_payload(trace_payload, sdk_info) + # ActionAgent::TelemetryTrace.create_from_payload(trace_payload, sdk_info) # # @example Creating a trace with account (multi-tenant mode) - # ActiveAgent::TelemetryTrace.create_from_payload(trace_payload, sdk_info, account: account) + # ActionAgent::TelemetryTrace.create_from_payload(trace_payload, sdk_info, account: account) # class TelemetryTrace < ::ActiveRecord::Base + include ActionAgent::AdapterAware + self.table_name = "active_agent_telemetry_traces" # Optional account association for multi-tenant mode # The host app can add: belongs_to :account if needed - if ActiveAgent::Dashboard.multi_tenant? - belongs_to :account, class_name: ActiveAgent::Dashboard.account_class + if ActionAgent.multi_tenant? + belongs_to :account, class_name: ActionAgent.account_class end # Status values for traces @@ -39,7 +41,10 @@ class TelemetryTrace < ::ActiveRecord::Base scope :for_environment, ->(env) { where(environment: env) } scope :for_agent, ->(agent_class) { where(agent_class: agent_class) } scope :for_date_range, ->(start_date, end_date) { where(timestamp: start_date..end_date) } - scope :for_account, ->(account) { where(account: account) if ActiveAgent::Dashboard.multi_tenant? } + # The dashboard agent this trace was attributed to on ingest, if any. + belongs_to :agent, class_name: "ActionAgent::Agent", optional: true + + scope :for_account, ->(account) { where(account: account) if ActionAgent.multi_tenant? } # Creates a TelemetryTrace from an ingested trace payload. # @@ -50,6 +55,32 @@ 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( + # spans is cast rather than assumed to be jsonb: the column is + # json on every install created before the migration template + # started picking jsonb per adapter, and jsonb_array_elements + # rejects a json argument outright. + "(SELECT s.value -> 'attributes' ->> 'llm.model' " \ + "FROM jsonb_array_elements(spans::jsonb) 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 || {} @@ -102,7 +133,7 @@ def self.create_from_payload(trace, sdk_info = {}, account: nil) } # Add account if in multi-tenant mode - attrs[:account] = account if ActiveAgent::Dashboard.multi_tenant? && account + attrs[:account] = account if ActionAgent.multi_tenant? && account create!(attrs) end diff --git a/actionagent/app/models/concerns/action_agent/adapter_aware.rb b/actionagent/app/models/concerns/action_agent/adapter_aware.rb new file mode 100644 index 00000000..cb9b2479 --- /dev/null +++ b/actionagent/app/models/concerns/action_agent/adapter_aware.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 + + # SQL grouping +column+ into hourly buckets. PostgreSQL has + # date_trunc; SQLite and MySQL get the equivalent format string. + # Pair with hour_bucket_epoch, which normalises what each adapter + # returns into epoch seconds so callers compare buckets the same way. + def hour_bucket_sql(column = :timestamp) + case connection.adapter_name.to_s.downcase + when /postgres/ then "date_trunc('hour', #{column})" + when /mysql/ then "DATE_FORMAT(#{column}, '%Y-%m-%d %H:00:00')" + else "strftime('%Y-%m-%d %H:00:00', #{column})" + end + end + + # PostgreSQL hands back a Time; the others a UTC string. + def hour_bucket_epoch(value) + return value.to_i if value.is_a?(Time) || value.is_a?(DateTime) + + Time.find_zone("UTC").parse(value.to_s).to_i + end + end + end +end diff --git a/actionagent/app/models/concerns/action_agent/ownable.rb b/actionagent/app/models/concerns/action_agent/ownable.rb new file mode 100644 index 00000000..3d12a23e --- /dev/null +++ b/actionagent/app/models/concerns/action_agent/ownable.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +module ActionAgent + # Attaches a dashboard record to whatever the host app calls an owner. + # + # Each model names the associations that could own it, most preferred + # first, and the first one the host app has actually configured wins: + # + # 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 + + 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 = ActionAgent.public_send(CLASS_FOR.fetch(candidate)) + next if class_name.blank? + + belongs_to candidate, class_name: class_name, optional: true + end + end + + def owner_candidates + @owner_candidates || [] + end + + # 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| ActionAgent.public_send(CLASS_FOR.fetch(c)).present? } + end + + # Scopes to records owned by +owner+. + # + # A nil owner is read two different ways, and the difference is the + # whole point: + # + # * No owner model configured at all — the single-user self-hosted + # install. Nothing is owned, so everything is visible. + # * An owner model IS configured but did not resolve — a signed-out + # request, or a resolver that returned nil. Returning `all` here + # would hand one tenant every other tenant's records, so it + # returns nothing instead. + def for_owner(owner) + return all if owner_association.nil? + return none 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 configuration, or nil. + def owner + association = self.class.owner_association + association && public_send(association) + end + + # Assigns +owner+ to whichever association this install uses. A no-op + # when the host app configured no owner model. + def owner=(record) + association = self.class.owner_association + public_send(:"#{association}=", record) if association + end + end +end diff --git a/actionagent/app/models/concerns/action_agent/session_recordable.rb b/actionagent/app/models/concerns/action_agent/session_recordable.rb new file mode 100644 index 00000000..eb02de1f --- /dev/null +++ b/actionagent/app/models/concerns/action_agent/session_recordable.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/queries/action_agent/agent_executions.rb b/actionagent/app/queries/action_agent/agent_executions.rb new file mode 100644 index 00000000..e5bf8bbc --- /dev/null +++ b/actionagent/app/queries/action_agent/agent_executions.rb @@ -0,0 +1,201 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 = ActionAgent.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" + # A single-tenant dashboard reads every trace it holds. A multi-tenant + # one has no business reading any without a tenant to scope them to. + def include_reported? + return false if ActionAgent.multi_tenant? && @owner.nil? + + @source.nil? || @source == "reported" + end + + 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 diff --git a/actionagent/app/serializers/action_agent/agent_message_serializer.rb b/actionagent/app/serializers/action_agent/agent_message_serializer.rb new file mode 100644 index 00000000..1110ac36 --- /dev/null +++ b/actionagent/app/serializers/action_agent/agent_message_serializer.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/serializers/action_agent/telemetry_trace_serializer.rb b/actionagent/app/serializers/action_agent/telemetry_trace_serializer.rb new file mode 100644 index 00000000..828ad3ab --- /dev/null +++ b/actionagent/app/serializers/action_agent/telemetry_trace_serializer.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/serializers/action_agent/trace_interaction_serializer.rb b/actionagent/app/serializers/action_agent/trace_interaction_serializer.rb new file mode 100644 index 00000000..87aae453 --- /dev/null +++ b/actionagent/app/serializers/action_agent/trace_interaction_serializer.rb @@ -0,0 +1,228 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/services/action_agent/agent_execution_service.rb b/actionagent/app/services/action_agent/agent_execution_service.rb new file mode 100644 index 00000000..d37f7adc --- /dev/null +++ b/actionagent/app/services/action_agent/agent_execution_service.rb @@ -0,0 +1,572 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 + ActionAgent.agents_for(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. contextable: false — the context + # is loaded explicitly in the action below. + # + # The model classes are named explicitly because solid_agent infers + # bare "AgentContext"/"AgentMessage"/"AgentGeneration" and resolves + # them against Object. The engine's models are namespaced, so the + # inferred names only resolve in a host app that happens to have + # top-level models of its own. + include SolidAgent::HasContext + has_context contextable: false, + class_name: "ActionAgent::AgentContext", + message_class: "ActionAgent::AgentMessage", + generation_class: "ActionAgent::AgentGeneration" + + 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 = ActionAgent.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 = ActionAgent.trace_model + tenant = ActionAgent.tenant_for(owner) + return if trace_model.for_account(tenant).exists?(trace_id: root_span.trace_id) + + trace_model.create_from_payload(payload, sdk_info, account: tenant) + 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 diff --git a/actionagent/app/services/action_agent/agent_registrar.rb b/actionagent/app/services/action_agent/agent_registrar.rb new file mode 100644 index 00000000..25ba89d8 --- /dev/null +++ b/actionagent/app/services/action_agent/agent_registrar.rb @@ -0,0 +1,197 @@ +# frozen_string_literal: true + +module ActionAgent + # 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? && ActionAgent.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 = ActionAgent.trace_owner_resolver + return resolver.call(@trace) if resolver + + ActionAgent.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 diff --git a/actionagent/app/services/action_agent/agent_scorecard.rb b/actionagent/app/services/action_agent/agent_scorecard.rb new file mode 100644 index 00000000..53740d2b --- /dev/null +++ b/actionagent/app/services/action_agent/agent_scorecard.rb @@ -0,0 +1,191 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 = ActionAgent.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. + def self.trace_models(traces) + table = ActionAgent.trace_model.table_name + + ActionAgent.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 + + # 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 diff --git a/actionagent/app/services/action_agent/agent_toolbox.rb b/actionagent/app/services/action_agent/agent_toolbox.rb new file mode 100644 index 00000000..37ef4cae --- /dev/null +++ b/actionagent/app/services/action_agent/agent_toolbox.rb @@ -0,0 +1,504 @@ +# frozen_string_literal: true + +module ActionAgent + 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 diff --git a/actionagent/app/services/action_agent/evaluation_runner_service.rb b/actionagent/app/services/action_agent/evaluation_runner_service.rb new file mode 100644 index 00000000..45677532 --- /dev/null +++ b/actionagent/app/services/action_agent/evaluation_runner_service.rb @@ -0,0 +1,481 @@ +# frozen_string_literal: true + +module ActionAgent + # 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(AgentContext.table_name => { 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 ActionAgent.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) + ActionAgent.trace_model + .for_account(ActionAgent.tenant_for(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 = ActionAgent.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 diff --git a/actionagent/app/services/action_agent/mcp_recording_middleware.rb b/actionagent/app/services/action_agent/mcp_recording_middleware.rb new file mode 100644 index 00000000..553a4b0f --- /dev/null +++ b/actionagent/app/services/action_agent/mcp_recording_middleware.rb @@ -0,0 +1,241 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/services/action_agent/mock_sandbox_backend.rb b/actionagent/app/services/action_agent/mock_sandbox_backend.rb new file mode 100644 index 00000000..6a0d4d8a --- /dev/null +++ b/actionagent/app/services/action_agent/mock_sandbox_backend.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 ActionAgent.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 diff --git a/actionagent/app/services/action_agent/playwright_mcp_client.rb b/actionagent/app/services/action_agent/playwright_mcp_client.rb new file mode 100644 index 00000000..d35af643 --- /dev/null +++ b/actionagent/app/services/action_agent/playwright_mcp_client.rb @@ -0,0 +1,148 @@ +# frozen_string_literal: true + +module ActionAgent + 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 diff --git a/actionagent/app/services/action_agent/sandbox_orchestrator.rb b/actionagent/app/services/action_agent/sandbox_orchestrator.rb new file mode 100644 index 00000000..848e68e8 --- /dev/null +++ b/actionagent/app/services/action_agent/sandbox_orchestrator.rb @@ -0,0 +1,242 @@ +# frozen_string_literal: true + +module ActionAgent + # 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: + # + # ActionAgent.sandbox_backends = { + # "cloud_run" => "CloudRunService" + # } + BUILT_IN_BACKENDS = { "mock" => "ActionAgent::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(ActionAgent.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 || ActionAgent.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 diff --git a/actionagent/app/services/action_agent/session_recording_service.rb b/actionagent/app/services/action_agent/session_recording_service.rb new file mode 100644 index 00000000..3dc1b05e --- /dev/null +++ b/actionagent/app/services/action_agent/session_recording_service.rb @@ -0,0 +1,228 @@ +# frozen_string_literal: true + +module ActionAgent + # 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 diff --git a/actionagent/app/views/action_agent/dashboard/index.html.erb b/actionagent/app/views/action_agent/dashboard/index.html.erb new file mode 100644 index 00000000..73b6f037 --- /dev/null +++ b/actionagent/app/views/action_agent/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/active_agent/dashboard/traces/_trace_detail.html.erb b/actionagent/app/views/action_agent/traces/_trace_detail.html.erb similarity index 100% rename from lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/_trace_detail.html.erb rename to actionagent/app/views/action_agent/traces/_trace_detail.html.erb diff --git a/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb b/actionagent/app/views/action_agent/traces/index.html.erb similarity index 100% rename from lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb rename to actionagent/app/views/action_agent/traces/index.html.erb diff --git a/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb b/actionagent/app/views/action_agent/traces/metrics.html.erb similarity index 100% rename from lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb rename to actionagent/app/views/action_agent/traces/metrics.html.erb diff --git a/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb b/actionagent/app/views/action_agent/traces/show.html.erb similarity index 100% rename from lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb rename to actionagent/app/views/action_agent/traces/show.html.erb diff --git a/lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb b/actionagent/app/views/layouts/action_agent/application.html.erb similarity index 100% rename from lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb rename to actionagent/app/views/layouts/action_agent/application.html.erb diff --git a/actionagent/app/views/layouts/action_agent/react.html.erb b/actionagent/app/views/layouts/action_agent/react.html.erb new file mode 100644 index 00000000..e1305902 --- /dev/null +++ b/actionagent/app/views/layouts/action_agent/react.html.erb @@ -0,0 +1,19 @@ + + + + + + Active Agent + <%= csrf_meta_tags %> + <%# + Bundles ship prebuilt in the gem (see 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 "action_agent", "data-turbo-track": "reload" %> + <%= javascript_include_tag "action_agent", type: "module", defer: true %> + + + <%= yield %> + + diff --git a/actionagent/config/routes.rb b/actionagent/config/routes.rb new file mode 100644 index 00000000..44a96b70 --- /dev/null +++ b/actionagent/config/routes.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +ActionAgent::Engine.routes.draw do + # The React dashboard's entry point. Its own paths (/traces, /metrics, + # /agents/1/edit, ...) are matched by the catch-all at the bottom of this + # file, so deep links and the browser's back button both work wherever the + # engine is mounted. + root to: "dashboard#index" + + # The server-rendered console. Same data, no JavaScript — useful when the + # bundle can't run, and the surface the dashboard shipped with before the + # React app moved into the engine. + scope :console do + resources :traces, only: [ :index, :show ], controller: "traces" do + collection do + get :metrics + end + end + end + + # 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 ActionAgent.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 + 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" + + # Everything else under the mount is a client-side route: render the + # dashboard and let the browser resolve it. Anchored last so it can only + # ever catch what the routes above did not, and refuses /api paths so a + # mistyped endpoint answers as an API would rather than returning a page. + get "*path", to: "dashboard#index", constraints: ->(request) { !request.path.include?("/api/") } +end diff --git a/actionagent/frontend/components/AgentAvatar.jsx b/actionagent/frontend/components/AgentAvatar.jsx new file mode 100644 index 00000000..8366f73e --- /dev/null +++ b/actionagent/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/actionagent/frontend/components/dashboard/AgentAnalytics.jsx b/actionagent/frontend/components/dashboard/AgentAnalytics.jsx new file mode 100644 index 00000000..1f97153b --- /dev/null +++ b/actionagent/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/actionagent/frontend/components/dashboard/AgentBuilder.jsx b/actionagent/frontend/components/dashboard/AgentBuilder.jsx new file mode 100644 index 00000000..bbcc02f6 --- /dev/null +++ b/actionagent/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" + /> +
+ +
+ +