diff --git a/README.md b/README.md
index 512be46e1..46a587aa4 100644
--- a/README.md
+++ b/README.md
@@ -32,10 +32,11 @@ Welcome to the official repository for the Microsoft AI Agentic Workshop! This r
## Key Features
- **[Microsoft Agent Framework](https://github.com/microsoft/agent-framework) Integration** - Single-agent, multi-agent Magentic orchestration, and handoff-based domain routing with MCP tools. [Pattern guide →](agentic_ai/agents/agent_framework/README.md)
-- **[Workflow Orchestration](agentic_ai/workflow/)** - Pregel-style execution, checkpointing, human-in-the-loop patterns, and real-time observability. [Fraud Detection Demo →](agentic_ai/workflow/fraud_detection/)
-- **Advanced UI Options** - React frontend with streaming visualization or Streamlit for quick prototyping
+- **[Workflow Orchestration](agentic_ai/workflow/)** - Hybrid Workflow + Durable Task architecture with fan-out/fan-in topology, human-in-the-loop, and real-time observability. [Fraud Detection Demo →](agentic_ai/workflow/fraud_detection_durable/)
+- **[Observability with Application Insights](agentic_ai/observability/)** - Full tracing of agent executions, tool calls, and LLM invocations with pre-built Grafana dashboards. [Setup Guide →](agentic_ai/observability/README.md)
+- **Advanced UI Options** - React frontend with interactive workflow visualization and step-by-step tool call details
- **[MCP Server Integration](mcp/)** - Model Context Protocol for enhanced agent tool capabilities with advanced features: authentication, RBAC, and APIM integration
-- **[Emerging Agentic Scenarios](agentic_ai/scenarios/)** - Long-running workflows, progress updates, and durable agent patterns
+- **[Agent Evaluations](agentic_ai/evaluations/)** - Evaluate agent performance with custom metrics and test datasets
- **Agent State & History Persistence** - In-memory or CosmosDB backend for conversation history and agent state
- **[Enterprise-Ready Reference Architecture](infra/README.md)** - Production-grade deployment with VNet integration, private endpoints, managed identity, Terraform/Bicep IaC, and GitHub Actions CI/CD
@@ -46,7 +47,7 @@ Welcome to the official repository for the Microsoft AI Agentic Workshop! This r
1. Review the [Setup Instructions](./SETUP.md) for environment prerequisites and step-by-step installation.
2. Explore the [Business Scenario and Agent Design](./SCENARIO.md) to understand the workshop challenge.
3. Check out the **[Agent Framework Implementation Patterns](agentic_ai/agents/agent_framework/README.md)** to choose the right multi-agent approach (single-agent, Magentic orchestration, or handoff pattern).
-4. Try the **[Fraud Detection Workflow Demo](agentic_ai/workflow/fraud_detection/)** to see enterprise orchestration patterns in action.
+4. Try the **[Durable Fraud Detection Workflow](agentic_ai/workflow/fraud_detection_durable/)** to see hybrid Workflow + Durable Task orchestration with human-in-the-loop.
5. Dive into [System Architecture](./ARCHITECTURE.md) before building and customizing your agent solutions.
6. Utilize the [Support Guide](./SUPPORT.md) for troubleshooting and assistance.
diff --git a/agentic_ai/applications/.env.sample b/agentic_ai/applications/.env.sample
index 6608c22cc..a014a428f 100644
--- a/agentic_ai/applications/.env.sample
+++ b/agentic_ai/applications/.env.sample
@@ -98,3 +98,20 @@ MAGENTIC_MAX_ROUNDS=10
# 0 = no context transfer (domain isolation)
# N = transfer last N turns (1 turn = user message + assistant response)
HANDOFF_CONTEXT_TRANSFER_TURNS=-1
+
+############################################
+# Application Insights Observability #
+############################################
+# Connection string from Azure Portal > Application Insights > Overview > Connection String
+# APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=xxx;IngestionEndpoint=https://xxx.in.applicationinsights.azure.com/;LiveEndpoint=https://xxx.livediagnostics.monitor.azure.com/"
+
+# Enable sensitive data in traces (prompts, responses) - DEV ONLY!
+# ENABLE_SENSITIVE_DATA=true
+
+# Service name for telemetry (appears in Application Insights and Grafana)
+# OTEL_SERVICE_NAME="contoso-agent"
+
+# Grafana Dashboards (after setting up Azure Managed Grafana):
+# - Agent Overview: https://aka.ms/amg/dash/af-agent
+# - Workflow Overview: https://aka.ms/amg/dash/af-workflow
+
diff --git a/agentic_ai/applications/backend.py b/agentic_ai/applications/backend.py
index 85cddef08..45083b94d 100644
--- a/agentic_ai/applications/backend.py
+++ b/agentic_ai/applications/backend.py
@@ -15,6 +15,9 @@
from pathlib import Path
from typing import Dict, List, Any, Optional, Set, DefaultDict
from collections import defaultdict
+
+# Add parent directory to path for observability module
+sys.path.insert(0, str(Path(__file__).parent.parent))
import httpx
import jwt
@@ -29,10 +32,29 @@
from dotenv import load_dotenv
# ------------------------------------------------------------------
-# Environment
+# Environment (load first so observability can read connection string)
# ------------------------------------------------------------------
load_dotenv() # read .env if present
+# ------------------------------------------------------------------
+# Observability (must be before any agent imports)
+# ------------------------------------------------------------------
+from observability import setup_observability
+
+# Initialize Application Insights tracing if configured
+# All agents (single, reflection, handoff, etc.) are automatically traced
+_observability_enabled = setup_observability(
+ service_name="contoso-agent-backend",
+ enable_live_metrics=True,
+ enable_sensitive_data=os.getenv("ENABLE_SENSITIVE_DATA", "false").lower() in ("1", "true", "yes"),
+)
+if _observability_enabled:
+ logging.getLogger(__name__).info("✅ Application Insights observability enabled")
+
+# ------------------------------------------------------------------
+# Auth Configuration
+# ------------------------------------------------------------------
+
# Feature flag: disable auth for local dev / demos
DISABLE_AUTH = os.getenv("DISABLE_AUTH", "false").lower() in ("1", "true", "yes")
diff --git a/agentic_ai/applications/pyproject.toml b/agentic_ai/applications/pyproject.toml
index c8f6dfa01..eacd63989 100644
--- a/agentic_ai/applications/pyproject.toml
+++ b/agentic_ai/applications/pyproject.toml
@@ -9,6 +9,7 @@ dependencies = [
"azure-ai-evaluation>=1.14.0",
"azure-ai-projects>=2.0.0b2",
"azure-cosmos==4.9.0",
+ "azure-monitor-opentelemetry>=1.6.0",
"fastapi==0.115.12",
"flasgger==0.9.7.1",
"flask==3.0.3",
diff --git a/agentic_ai/applications/uv.lock b/agentic_ai/applications/uv.lock
index 022275724..47af74d72 100644
--- a/agentic_ai/applications/uv.lock
+++ b/agentic_ai/applications/uv.lock
@@ -499,6 +499,7 @@ dependencies = [
{ name = "azure-ai-evaluation" },
{ name = "azure-ai-projects" },
{ name = "azure-cosmos" },
+ { name = "azure-monitor-opentelemetry" },
{ name = "fastapi" },
{ name = "flasgger" },
{ name = "flask" },
@@ -529,6 +530,7 @@ requires-dist = [
{ name = "azure-ai-evaluation", specifier = ">=1.14.0" },
{ name = "azure-ai-projects", specifier = ">=2.0.0b2" },
{ name = "azure-cosmos", specifier = "==4.9.0" },
+ { name = "azure-monitor-opentelemetry", specifier = ">=1.6.0" },
{ name = "fastapi", specifier = "==0.115.12" },
{ name = "flasgger", specifier = "==0.9.7.1" },
{ name = "flask", specifier = "==3.0.3" },
@@ -553,6 +555,15 @@ dev = [
{ name = "pytest-timeout", specifier = ">=2.4.0" },
]
+[[package]]
+name = "asgiref"
+version = "3.11.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345 },
+]
+
[[package]]
name = "asyncio"
version = "4.0.0"
@@ -645,6 +656,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b1/3c/b90d5afc2e47c4a45f4bba00f9c3193b0417fad5ad3bb07869f9d12832aa/azure_core-1.36.0-py3-none-any.whl", hash = "sha256:fee9923a3a753e94a259563429f3644aaf05c486d45b1215d098115102d91d3b", size = 213302 },
]
+[[package]]
+name = "azure-core-tracing-opentelemetry"
+version = "1.0.0b12"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "azure-core" },
+ { name = "opentelemetry-api" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5a/7f/5de13a331a5f2919417819cc37dcf7c897018f02f83aa82b733e6629a6a6/azure_core_tracing_opentelemetry-1.0.0b12.tar.gz", hash = "sha256:bb454142440bae11fd9d68c7c1d67ae38a1756ce808c5e4d736730a7b4b04144", size = 26010 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/76/5e/97a471f66935e7f89f521d0e11ae49c7f0871ca38f5c319dccae2155c8d8/azure_core_tracing_opentelemetry-1.0.0b12-py3-none-any.whl", hash = "sha256:38fd42709f1cc4bbc4f2797008b1c30a6a01617e49910c05daa3a0d0c65053ac", size = 11962 },
+]
+
[[package]]
name = "azure-cosmos"
version = "4.9.0"
@@ -718,6 +742,46 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/26/94/7c902e966b28e7cb5080a8e0dd6bffc22ba44bc907f09c4c633d2b7c4f6a/azure_keyvault_secrets-4.10.0-py3-none-any.whl", hash = "sha256:9dbde256077a4ee1a847646671580692e3f9bea36bcfc189c3cf2b9a94eb38b9", size = 125237 },
]
+[[package]]
+name = "azure-monitor-opentelemetry"
+version = "1.8.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "azure-core" },
+ { name = "azure-core-tracing-opentelemetry" },
+ { name = "azure-monitor-opentelemetry-exporter" },
+ { name = "opentelemetry-instrumentation-django" },
+ { name = "opentelemetry-instrumentation-fastapi" },
+ { name = "opentelemetry-instrumentation-flask" },
+ { name = "opentelemetry-instrumentation-psycopg2" },
+ { name = "opentelemetry-instrumentation-requests" },
+ { name = "opentelemetry-instrumentation-urllib" },
+ { name = "opentelemetry-instrumentation-urllib3" },
+ { name = "opentelemetry-resource-detector-azure" },
+ { name = "opentelemetry-sdk" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8a/2c/572dc7442f69bf3e97d93bb9073bb270437332f3ee8eaeeffbaf734f69f3/azure_monitor_opentelemetry-1.8.6.tar.gz", hash = "sha256:8301c377f2c0550dc9b87b273b746d5841ef8e5117b6c542c9a6f7f3c7f34c20", size = 60541 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a9/53/a3eeafd014947d2cb6142d0e405fb1e524248d0df49ec8fe2384d359afe7/azure_monitor_opentelemetry-1.8.6-py3-none-any.whl", hash = "sha256:2323eeba15bd2f016806e15f5bd6d24ddd62cc06beae0a06ab3fcfab38d3d120", size = 29350 },
+]
+
+[[package]]
+name = "azure-monitor-opentelemetry-exporter"
+version = "1.0.0b47"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "azure-core" },
+ { name = "azure-identity" },
+ { name = "msrest" },
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-sdk" },
+ { name = "psutil" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c7/3b/d0e4d8e0f61cb82fd3e94e52291036a7415321f9f7d5386ddb1277d31faa/azure_monitor_opentelemetry_exporter-1.0.0b47.tar.gz", hash = "sha256:c1207bd1c356aa77255e256f1af8eb2ac40a3bf51f90735f456056def7ac38c0", size = 279165 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8d/b1/67361bdb9047591f84b2bbd1e03c3161cf85f718a7532b78b4e48f6eaa38/azure_monitor_opentelemetry_exporter-1.0.0b47-py2.py3-none-any.whl", hash = "sha256:be1eca7ddfc07436793981313a68662e14713902f7e7fa7cf81736f1cf6d8bf8", size = 201193 },
+]
+
[[package]]
name = "azure-search-documents"
version = "11.7.0b2"
@@ -2114,42 +2178,224 @@ wheels = [
[[package]]
name = "opentelemetry-api"
-version = "1.39.1"
+version = "1.39.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767 }
+sdist = { url = "https://files.pythonhosted.org/packages/c0/0b/e5428c009d4d9af0515b0a8371a8aaae695371af291f45e702f7969dce6b/opentelemetry_api-1.39.0.tar.gz", hash = "sha256:6130644268c5ac6bdffaf660ce878f10906b3e789f7e2daa5e169b047a2933b9", size = 65763 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/05/85/d831a9bc0a9e0e1a304ff3d12c1489a5fbc9bf6690a15dcbdae372bbca45/opentelemetry_api-1.39.0-py3-none-any.whl", hash = "sha256:3c3b3ca5c5687b1b5b37e5c5027ff68eacea8675241b29f13110a8ffbb8f0459", size = 66357 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-semantic-conventions" },
+ { name = "packaging" },
+ { name = "wrapt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/55/3c/bd53dbb42eff93d18e3047c7be11224aa9966ce98ac4cc5bfb860a32c95a/opentelemetry_instrumentation-0.60b0.tar.gz", hash = "sha256:4e9fec930f283a2677a2217754b40aaf9ef76edae40499c165bc7f1d15366a74", size = 31707 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5c/7b/5b5b9f8cfe727a28553acf9cd287b1d7f706f5c0a00d6e482df55b169483/opentelemetry_instrumentation-0.60b0-py3-none-any.whl", hash = "sha256:aaafa1483543a402819f1bdfb06af721c87d60dd109501f9997332862a35c76a", size = 33096 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-asgi"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "asgiref" },
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-instrumentation" },
+ { name = "opentelemetry-semantic-conventions" },
+ { name = "opentelemetry-util-http" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b0/0a/715ea7044708d3c215385fb2a1c6ffe429aacb3cd23a348060aaeda52834/opentelemetry_instrumentation_asgi-0.60b0.tar.gz", hash = "sha256:928731218050089dca69f0fe980b8bfe109f384be8b89802d7337372ddb67b91", size = 26083 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/8c/c6c59127fd996107243ca45669355665a7daff578ddafb86d6d2d3b01428/opentelemetry_instrumentation_asgi-0.60b0-py3-none-any.whl", hash = "sha256:9d76a541269452c718a0384478f3291feb650c5a3f29e578fdc6613ea3729cf3", size = 16907 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-dbapi"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-instrumentation" },
+ { name = "opentelemetry-semantic-conventions" },
+ { name = "wrapt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/12/7f/b4c1fbce01b29daad5ef1396427c9cd3c7a55ee68e75f8c11089c7e2533d/opentelemetry_instrumentation_dbapi-0.60b0.tar.gz", hash = "sha256:2b7eb38e46890cebe5bc1a1c03d2ab07fc159b0b7b91342941ee33dd73876d84", size = 16311 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/23/0a/65e100c6d803de59a9113a993dcd371a4027453ba15ce4dabdb0343ca154/opentelemetry_instrumentation_dbapi-0.60b0-py3-none-any.whl", hash = "sha256:429d8ca34a44a4296b9b09a1bd373fff350998d200525c6e79883c3328559b03", size = 13966 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-django"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-instrumentation" },
+ { name = "opentelemetry-instrumentation-wsgi" },
+ { name = "opentelemetry-semantic-conventions" },
+ { name = "opentelemetry-util-http" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7c/d2/8ddd9a5c61cd5048d422be8d22fac40f603aa82f0babf9f7c40db871080c/opentelemetry_instrumentation_django-0.60b0.tar.gz", hash = "sha256:461e6fca27936ba97eec26da38bb5f19310783370478c7ca3a3e40faaceac9cc", size = 26596 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/18/d6/28684547bf6c699582e998a172ba8bb08405cf6706729b0d6a16042e998f/opentelemetry_instrumentation_django-0.60b0-py3-none-any.whl", hash = "sha256:95495649c8c34ce9217c6873cdd10fc4fcaa67c25f8329adc54f5b286999e40b", size = 21169 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-fastapi"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-instrumentation" },
+ { name = "opentelemetry-instrumentation-asgi" },
+ { name = "opentelemetry-semantic-conventions" },
+ { name = "opentelemetry-util-http" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fe/51/a021a7c929b5103fcb6bfdfa5a99abcaeb3b505faf9e3ee3ec14612c1ef9/opentelemetry_instrumentation_fastapi-0.60b0.tar.gz", hash = "sha256:5d34d67eb634a08bfe9e530680d6177521cd9da79285144e6d5a8f42683ed1b3", size = 24960 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b1/5a/e238c108eb65a726d75184439377a87d532050036b54e718e4c789b26d1a/opentelemetry_instrumentation_fastapi-0.60b0-py3-none-any.whl", hash = "sha256:415c6602db01ee339276ea4cabe3e80177c9e955631c087f2ef60a75e31bfaee", size = 13478 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-flask"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-instrumentation" },
+ { name = "opentelemetry-instrumentation-wsgi" },
+ { name = "opentelemetry-semantic-conventions" },
+ { name = "opentelemetry-util-http" },
+ { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/30/cc/e0758c23d66fd49956169cb24b5b06130373da2ce8d49945abce82003518/opentelemetry_instrumentation_flask-0.60b0.tar.gz", hash = "sha256:560f08598ef40cdcf7ca05bfb2e3ea74fab076e676f4c18bb36bb379bf5c4a1b", size = 20336 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/b5/387ce11f59e5ce65b890adc3f9c457877143b8a6d107a3a0b305397933a1/opentelemetry_instrumentation_flask-0.60b0-py3-none-any.whl", hash = "sha256:106e5774f79ac9b86dd0d949c1b8f46c807a8af16184301e10d24fc94e680d04", size = 15189 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-psycopg2"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-instrumentation" },
+ { name = "opentelemetry-instrumentation-dbapi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f8/68/5ae8a3b9a28c2fdf8d3d050e451ddb2612ca963679b08a2959f01f6dda4b/opentelemetry_instrumentation_psycopg2-0.60b0.tar.gz", hash = "sha256:59e527fd97739440380634ffcf9431aa7f2965d939d8d5829790886e2b54ede9", size = 11266 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d4/24/66b5a41a2b0d1d07cc9b0fbd80f8b5c66b46a4d4731743505891da8b3cbe/opentelemetry_instrumentation_psycopg2-0.60b0-py3-none-any.whl", hash = "sha256:ea136a32babd559aa717c04dddf6aa78aa94b816fb4e10dfe06751727ef306d4", size = 11284 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-requests"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-instrumentation" },
+ { name = "opentelemetry-semantic-conventions" },
+ { name = "opentelemetry-util-http" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/26/0f/94c6181e95c867f559715887c418170a9eadd92ea6090122d464e375ff56/opentelemetry_instrumentation_requests-0.60b0.tar.gz", hash = "sha256:5079ed8df96d01dab915a0766cd28a49be7c33439ce43d6d39843ed6dee3204f", size = 16173 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f1/e1/2f13b41c5679243ba8eae651170c4ce2f532349877819566ae4a89a2b47f/opentelemetry_instrumentation_requests-0.60b0-py3-none-any.whl", hash = "sha256:e9957f3a650ae55502fa227b29ff985b37d63e41c85e6e1555d48039f092ea83", size = 13122 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-urllib"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-instrumentation" },
+ { name = "opentelemetry-semantic-conventions" },
+ { name = "opentelemetry-util-http" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/95/db/be895de04bd56d7a2b2ef6d267a4c52f6cd325b6647d1c15ae888b1b0f6a/opentelemetry_instrumentation_urllib-0.60b0.tar.gz", hash = "sha256:89b8796f9ab64d0ea0833cfea98745963baa0d7e4a775b3d2a77791aa97cf3f9", size = 13931 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356 },
+ { url = "https://files.pythonhosted.org/packages/2b/e0/178914d5cec77baef797c6d47412da478ff871b05eb8732d64037b87c868/opentelemetry_instrumentation_urllib-0.60b0-py3-none-any.whl", hash = "sha256:80e3545d02505dc0ea61b3a0a141ec2828e11bee6b7dedfd3ee7ed9a7adbf862", size = 12673 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-urllib3"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-instrumentation" },
+ { name = "opentelemetry-semantic-conventions" },
+ { name = "opentelemetry-util-http" },
+ { name = "wrapt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/25/a8/16a32239e84741fae1a2932badeade5e72b73bfc331b53f7049a648ca00b/opentelemetry_instrumentation_urllib3-0.60b0.tar.gz", hash = "sha256:6ae1640a993901bae8eda5496d8b1440fb326a29e4ba1db342738b8868174aad", size = 15789 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/16/b2/ca27479eaf1f3f4825481769eb0cb200cad839040b8d5f42662d0398a256/opentelemetry_instrumentation_urllib3-0.60b0-py3-none-any.whl", hash = "sha256:9a07504560feae650a9205b3e2a579a835819bb1d55498d26a5db477fe04bba0", size = 13187 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-wsgi"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "opentelemetry-api" },
+ { name = "opentelemetry-instrumentation" },
+ { name = "opentelemetry-semantic-conventions" },
+ { name = "opentelemetry-util-http" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/10/ad/ae04e35f3b96d9c20d5d3df94a4c296eabf7a54d35d6c831179471128270/opentelemetry_instrumentation_wsgi-0.60b0.tar.gz", hash = "sha256:5815195b1b9890f55c4baafec94ff98591579a7d9b16256064adea8ee5784651", size = 19104 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/73/0e/1ed4d3cdce7b2e00a24f79933b3472e642d4db98aaccc09769be5cbe5296/opentelemetry_instrumentation_wsgi-0.60b0-py3-none-any.whl", hash = "sha256:0ff80614c1e73f7e94a5860c7e6222a51195eebab3dc5f50d89013db3d5d2f13", size = 14553 },
+]
+
+[[package]]
+name = "opentelemetry-resource-detector-azure"
+version = "0.1.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "opentelemetry-sdk" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/67/e4/0d359d48d03d447225b30c3dd889d5d454e3b413763ff721f9b0e4ac2e59/opentelemetry_resource_detector_azure-0.1.5.tar.gz", hash = "sha256:e0ba658a87c69eebc806e75398cd0e9f68a8898ea62de99bc1b7083136403710", size = 11503 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c3/ae/c26d8da88ba2e438e9653a408b0c2ad6f17267801250a8f3cc6405a93a72/opentelemetry_resource_detector_azure-0.1.5-py3-none-any.whl", hash = "sha256:4dcc5d54ab5c3b11226af39509bc98979a8b9e0f8a24c1b888783755d3bf00eb", size = 14252 },
]
[[package]]
name = "opentelemetry-sdk"
-version = "1.39.1"
+version = "1.39.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-api" },
{ name = "opentelemetry-semantic-conventions" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460 }
+sdist = { url = "https://files.pythonhosted.org/packages/51/e3/7cd989003e7cde72e0becfe830abff0df55c69d237ee7961a541e0167833/opentelemetry_sdk-1.39.0.tar.gz", hash = "sha256:c22204f12a0529e07aa4d985f1bca9d6b0e7b29fe7f03e923548ae52e0e15dde", size = 171322 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565 },
+ { url = "https://files.pythonhosted.org/packages/a4/b4/2adc8bc83eb1055ecb592708efb6f0c520cc2eb68970b02b0f6ecda149cf/opentelemetry_sdk-1.39.0-py3-none-any.whl", hash = "sha256:90cfb07600dfc0d2de26120cebc0c8f27e69bf77cd80ef96645232372709a514", size = 132413 },
]
[[package]]
name = "opentelemetry-semantic-conventions"
-version = "0.60b1"
+version = "0.60b0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-api" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935 }
+sdist = { url = "https://files.pythonhosted.org/packages/71/0e/176a7844fe4e3cb5de604212094dffaed4e18b32f1c56b5258bcbcba85c2/opentelemetry_semantic_conventions-0.60b0.tar.gz", hash = "sha256:227d7aa73cbb8a2e418029d6b6465553aa01cf7e78ec9d0bc3255c7b3ac5bf8f", size = 137935 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982 },
+ { url = "https://files.pythonhosted.org/packages/d0/56/af0306666f91bae47db14d620775604688361f0f76a872e0005277311131/opentelemetry_semantic_conventions-0.60b0-py3-none-any.whl", hash = "sha256:069530852691136018087b52688857d97bba61cd641d0f8628d2d92788c4f78a", size = 219981 },
]
[[package]]
@@ -2161,6 +2407,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/35/b5/cf25da2218910f0d6cdf7f876a06bed118c4969eacaf60a887cbaef44f44/opentelemetry_semantic_conventions_ai-0.4.13-py3-none-any.whl", hash = "sha256:883a30a6bb5deaec0d646912b5f9f6dcbb9f6f72557b73d0f2560bf25d13e2d5", size = 6080 },
]
+[[package]]
+name = "opentelemetry-util-http"
+version = "0.60b0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/38/0d/786a713445cf338131fef3a84fab1378e4b2ef3c3ea348eeb0c915eb804a/opentelemetry_util_http-0.60b0.tar.gz", hash = "sha256:e42b7bb49bba43b6f34390327d97e5016eb1c47949ceaf37c4795472a4e3a82d", size = 10576 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/53/5d/a448862f6d10c95685ed0e703596b6bd1784074e7ad90bffdc550abb7b68/opentelemetry_util_http-0.60b0-py3-none-any.whl", hash = "sha256:4f366f1a48adb74ffa6f80aee26f96882e767e01b03cd1cfb948b6e1020341fe", size = 8742 },
+]
+
[[package]]
name = "orderedmultidict"
version = "1.0.2"
@@ -2465,6 +2720,34 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/cc/7e77861000a0691aeea8f4566e5d3aa716f2b1dece4a24439437e41d3d25/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5", size = 172823 },
]
+[[package]]
+name = "psutil"
+version = "7.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595 },
+ { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082 },
+ { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476 },
+ { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062 },
+ { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893 },
+ { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589 },
+ { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664 },
+ { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087 },
+ { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383 },
+ { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210 },
+ { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228 },
+ { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284 },
+ { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090 },
+ { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859 },
+ { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560 },
+ { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997 },
+ { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972 },
+ { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266 },
+ { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737 },
+ { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617 },
+]
+
[[package]]
name = "pyarrow"
version = "22.0.0"
@@ -3482,6 +3765,55 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498 },
]
+[[package]]
+name = "wrapt"
+version = "1.17.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998 },
+ { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020 },
+ { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098 },
+ { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036 },
+ { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156 },
+ { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102 },
+ { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732 },
+ { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705 },
+ { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877 },
+ { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885 },
+ { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003 },
+ { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025 },
+ { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108 },
+ { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072 },
+ { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214 },
+ { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105 },
+ { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766 },
+ { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711 },
+ { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885 },
+ { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896 },
+ { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132 },
+ { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091 },
+ { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172 },
+ { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163 },
+ { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963 },
+ { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945 },
+ { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857 },
+ { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178 },
+ { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310 },
+ { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266 },
+ { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544 },
+ { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283 },
+ { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366 },
+ { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571 },
+ { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094 },
+ { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659 },
+ { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946 },
+ { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717 },
+ { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334 },
+ { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471 },
+ { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591 },
+]
+
[[package]]
name = "yarl"
version = "1.22.0"
diff --git a/agentic_ai/evaluations/run_agent_eval.py b/agentic_ai/evaluations/run_agent_eval.py
index 232da713e..a8f31ce8f 100644
--- a/agentic_ai/evaluations/run_agent_eval.py
+++ b/agentic_ai/evaluations/run_agent_eval.py
@@ -73,9 +73,6 @@
# Import evaluation framework
from evaluations import AgentEvaluationRunner, AgentTrace
-# Import utilities
-from applications.utils import get_state_store
-
class ToolCallTracker:
"""Captures tool calls emitted via the agent's WebSocket-style broadcast.
@@ -595,7 +592,7 @@ async def main():
parser.add_argument("--agent", default=None,
help="Agent type: single, reflection, handoff (overrides --agent-name)")
parser.add_argument("--agent-name", default="agent_eval", help="Name for telemetry tracking")
- parser.add_argument("--backend-url", default="http://localhost:700", help="Backend URL to send requests to")
+ parser.add_argument("--backend-url", default="http://localhost:7000", help="Backend URL to send requests to")
parser.add_argument("--remote", action="store_true", help="Run evaluation in Azure AI Foundry portal only (skip local)")
parser.add_argument("--local", action="store_true", help="Run local evaluation only (default if neither specified)")
parser.add_argument("--limit", type=int, default=0, help="Limit number of test cases to run (0 = all)")
diff --git a/agentic_ai/observability/README.md b/agentic_ai/observability/README.md
new file mode 100644
index 000000000..fb030f79f
--- /dev/null
+++ b/agentic_ai/observability/README.md
@@ -0,0 +1,198 @@
+# Agent Observability with Application Insights
+
+This module provides full observability for Agent Framework applications using Azure Application Insights and Grafana dashboards.
+
+## Features
+
+- **Traces**: Full span hierarchy for agent executions, tool calls, and LLM invocations
+- **Logs**: Structured logging with trace context correlation
+- **Metrics**: Token usage, latency, and custom metrics
+- **Grafana Dashboards**: Pre-built dashboards for agent and workflow visualization
+
+## Quick Start
+
+### 1. Get your Application Insights Connection String
+
+From Azure Portal:
+1. Navigate to your Application Insights resource
+2. Go to **Overview** → **Connection String**
+3. Copy the full connection string
+
+### 2. Configure Environment
+
+Add to your `.env` file (in `agentic_ai/applications/`):
+
+```bash
+APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=xxx;IngestionEndpoint=https://xxx.in.applicationinsights.azure.com/;LiveEndpoint=https://xxx.livediagnostics.monitor.azure.com/"
+
+# Optional: Enable sensitive data (prompts/responses) - DEV ONLY!
+ENABLE_SENSITIVE_DATA=true
+
+# Optional: Custom service name
+OTEL_SERVICE_NAME="contoso-agent"
+```
+
+### 3. Install Dependencies
+
+```bash
+cd agentic_ai/applications
+uv sync
+```
+
+### 4. Run the Sample
+
+```bash
+# Start MCP server first
+cd mcp && uv run python mcp_service.py
+
+# In another terminal, run the sample
+cd agentic_ai/applications
+uv run python ../observability/sample_agent_with_tracing.py
+```
+
+## Usage in Your Code
+
+### Basic Setup
+
+```python
+from observability import setup_observability, get_tracer
+
+# Initialize once at application startup
+setup_observability(
+ connection_string="InstrumentationKey=...", # Or set APPLICATIONINSIGHTS_CONNECTION_STRING
+ service_name="my-agent-app",
+ enable_live_metrics=True,
+ enable_sensitive_data=False, # Set True only in dev!
+)
+
+# Use tracer for custom spans
+tracer = get_tracer()
+with tracer.start_as_current_span("my-operation"):
+ # Your code here
+ pass
+```
+
+### With Agents
+
+```python
+from observability import setup_observability, get_tracer, get_trace_id
+from agent_framework import ChatAgent
+from opentelemetry.trace import SpanKind
+
+# Setup observability BEFORE creating agents
+setup_observability()
+
+tracer = get_tracer()
+
+# Create a parent span for the session
+with tracer.start_as_current_span("customer-session", kind=SpanKind.SERVER) as span:
+ trace_id = get_trace_id()
+ print(f"Trace ID: {trace_id}")
+
+ # Add custom attributes
+ span.set_attribute("customer.id", "12345")
+ span.set_attribute("session.type", "support")
+
+ # Run your agent - all tool calls and LLM invocations are traced automatically
+ agent = ChatAgent(...)
+ async for update in agent.run_stream(query, thread=thread):
+ print(update.text, end="")
+```
+
+## Viewing Telemetry
+
+### Azure Monitor Dashboards (Recommended)
+
+Pre-built dashboards are available via Azure Monitor Workbooks - **no Grafana setup required!**
+
+| Dashboard | URL | Description |
+|-----------|-----|-------------|
+| **Agent Overview** | https://aka.ms/amg/dash/af-agent | Agent execution, tool calls, token usage, response times |
+| **Workflow Overview** | https://aka.ms/amg/dash/af-workflow | Workflow execution, step timing, fan-out/fan-in |
+
+**To use:**
+1. Click the dashboard link above
+2. Select your **Subscription** and **Application Insights** resource from the dropdowns
+3. View your live agent telemetry immediately!
+
+
+
+### Azure Portal (Manual)
+
+1. Go to Application Insights → **Transaction search**
+2. Filter by Trace ID to see the full execution tree
+3. Use **Live Metrics** for real-time monitoring
+
+### KQL Queries
+
+Use these queries in Application Insights → Logs:
+
+**View all spans for recent traces:**
+```kusto
+dependencies
+| where operation_Id in (dependencies
+ | project operation_Id, timestamp
+ | order by timestamp desc
+ | summarize operations = make_set(operation_Id), timestamp = max(timestamp) by operation_Id
+ | order by timestamp desc
+ | project operation_Id
+ | take 5)
+| evaluate bag_unpack(customDimensions)
+| extend tool_call_id = tostring(["gen_ai.tool.call.id"])
+| project-keep timestamp, target, operation_Id, duration, gen_ai*
+| order by timestamp asc
+```
+
+**Token usage by model:**
+```kusto
+customMetrics
+| where name contains "gen_ai.client.token"
+| summarize TotalTokens = sum(value) by name, bin(timestamp, 1h)
+| render timechart
+```
+
+## What Gets Traced
+
+The Agent Framework automatically captures:
+
+| Span Type | Attributes |
+|-----------|------------|
+| **Agent Run** | `gen_ai.agent.name`, `gen_ai.agent.id` |
+| **LLM Call** | `gen_ai.system`, `gen_ai.request.model`, `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens` |
+| **Tool Call** | `gen_ai.tool.name`, `gen_ai.tool.call.id`, arguments, results |
+| **MCP Tool** | `mcp.server.url`, `mcp.tool.name` |
+| **Workflow** | `workflow.name`, `workflow.step`, fan-out/fan-in relationships |
+
+## Configuration Options
+
+| Environment Variable | Description | Default |
+|---------------------|-------------|---------|
+| `APPLICATIONINSIGHTS_CONNECTION_STRING` | App Insights connection string | Required |
+| `ENABLE_SENSITIVE_DATA` | Include prompts/responses in traces | `false` |
+| `OTEL_SERVICE_NAME` | Service name in telemetry | `agent_framework` |
+| `OTEL_SERVICE_VERSION` | Service version | Package version |
+
+## Troubleshooting
+
+### No data in Application Insights
+
+1. Verify connection string is correct
+2. Check that `setup_observability()` returns `True`
+3. Allow 2-5 minutes for data to appear in the portal
+4. Check for firewall rules blocking outbound HTTPS
+
+### Missing tool calls
+
+Ensure you're using Agent Framework's MCP integration with `McpServerManager` which has built-in instrumentation.
+
+### Sensitive data not appearing
+
+Set `ENABLE_SENSITIVE_DATA=true` or pass `enable_sensitive_data=True` to `setup_observability()`.
+
+> ⚠️ **Warning**: Never enable sensitive data in production as it may expose PII and confidential information.
+
+## Related Documentation
+
+- [Agent Framework Observability](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/observability)
+- [Azure Monitor OpenTelemetry](https://learn.microsoft.com/en-us/azure/azure-monitor/app/opentelemetry-enable)
+- [OpenTelemetry GenAI Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/)
diff --git a/agentic_ai/observability/__init__.py b/agentic_ai/observability/__init__.py
new file mode 100644
index 000000000..9f5667379
--- /dev/null
+++ b/agentic_ai/observability/__init__.py
@@ -0,0 +1,33 @@
+# Copyright (c) Microsoft. All rights reserved.
+
+"""
+Observability module for Agent Framework applications.
+
+This module provides easy-to-use telemetry setup for:
+- Traces (spans for agent/tool execution)
+- Logs (structured logging with context)
+- Metrics (token usage, latency, etc.)
+
+Quick Start:
+ from observability import setup_observability, get_tracer
+
+ # Initialize once at startup
+ setup_observability()
+
+ # Use tracer for custom spans
+ with get_tracer().start_as_current_span("my-operation"):
+ # Your code here
+ pass
+"""
+
+from .setup import (
+ setup_observability,
+ get_tracer,
+ get_trace_id,
+)
+
+__all__ = [
+ "setup_observability",
+ "get_tracer",
+ "get_trace_id",
+]
diff --git a/agentic_ai/observability/sample_agent_with_tracing.py b/agentic_ai/observability/sample_agent_with_tracing.py
new file mode 100644
index 000000000..fbcccb5de
--- /dev/null
+++ b/agentic_ai/observability/sample_agent_with_tracing.py
@@ -0,0 +1,158 @@
+# Copyright (c) Microsoft. All rights reserved.
+
+"""
+Sample agent with full observability using Application Insights.
+
+This demonstrates how to:
+1. Configure observability with Application Insights
+2. Create custom spans for operations
+3. Run agents with full tracing of tool calls and LLM invocations
+4. View results in Grafana dashboards
+
+Usage:
+ cd agentic_ai/applications
+ uv run python ../observability/sample_agent_with_tracing.py
+
+Prerequisites:
+ - Set APPLICATIONINSIGHTS_CONNECTION_STRING in .env
+ - MCP server running (cd mcp && uv run python mcp_service.py)
+ - Azure OpenAI credentials configured
+
+Grafana Dashboards:
+ - Agent Overview: https://aka.ms/amg/dash/af-agent
+ - Workflow Overview: https://aka.ms/amg/dash/af-workflow
+"""
+
+import asyncio
+import os
+import sys
+from pathlib import Path
+
+# Add parent directories to path
+current_dir = Path(__file__).parent
+agentic_ai_dir = current_dir.parent
+applications_dir = agentic_ai_dir / "applications"
+sys.path.insert(0, str(agentic_ai_dir))
+sys.path.insert(0, str(applications_dir))
+
+# Load environment variables
+from dotenv import load_dotenv
+load_dotenv(applications_dir / ".env")
+
+# Import observability BEFORE creating any agents
+from observability import setup_observability, get_tracer, get_trace_id
+from opentelemetry.trace import SpanKind
+
+# Setup observability first
+APPINSIGHTS_CONNECTION_STRING = os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING")
+
+if not APPINSIGHTS_CONNECTION_STRING:
+ print("❌ APPLICATIONINSIGHTS_CONNECTION_STRING not set in environment")
+ print(" Add it to agentic_ai/applications/.env")
+ sys.exit(1)
+
+success = setup_observability(
+ connection_string=APPINSIGHTS_CONNECTION_STRING,
+ service_name="contoso-agent-demo",
+ enable_live_metrics=True,
+ enable_sensitive_data=True, # Enable to see prompts/responses (dev only!)
+)
+
+if not success:
+ print("❌ Failed to configure observability")
+ sys.exit(1)
+
+
+async def run_agent_with_tracing():
+ """Run a sample agent with full observability."""
+
+ from agent_framework import ChatAgent
+ from agent_framework.mcp import McpServerManager
+ from agent_framework.openai import AzureOpenAIChatClient
+ from azure.identity import DefaultAzureCredential
+
+ # Get tracer for custom spans
+ tracer = get_tracer("contoso-agent-demo")
+
+ # MCP server URL
+ mcp_url = os.environ.get("MCP_SERVER_URI", "http://localhost:8000/mcp")
+
+ # Azure OpenAI configuration
+ azure_endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT")
+ deployment_name = os.environ.get("AZURE_OPENAI_CHAT_DEPLOYMENT", "gpt-4o")
+
+ if not azure_endpoint:
+ print("❌ AZURE_OPENAI_ENDPOINT not set")
+ return
+
+ print("=" * 60)
+ print("Agent with Application Insights Observability")
+ print("=" * 60)
+
+ # Create a parent span for the entire session
+ with tracer.start_as_current_span("customer-service-session", kind=SpanKind.SERVER) as session_span:
+ trace_id = get_trace_id()
+ print(f"\n📊 Trace ID: {trace_id}")
+ print(f" View in Azure Portal or Grafana after completion")
+ print()
+
+ # Add custom attributes to the span
+ session_span.set_attribute("customer.scenario", "billing-inquiry")
+ session_span.set_attribute("session.type", "demo")
+
+ async with McpServerManager(mcp_url) as mcp_manager:
+ mcp_tools = await mcp_manager.list_tools()
+ print(f"📦 Loaded {len(mcp_tools)} MCP tools")
+
+ # Create chat client
+ chat_client = AzureOpenAIChatClient(
+ azure_endpoint=azure_endpoint,
+ azure_deployment=deployment_name,
+ credential=DefaultAzureCredential(),
+ )
+
+ # Create agent
+ agent = ChatAgent(
+ chat_client=chat_client,
+ tools=mcp_tools,
+ name="CustomerServiceAgent",
+ instructions="""You are a helpful customer service agent for Contoso Wireless.
+ Use the available tools to look up customer information, billing details, and data usage.
+ Be concise and helpful in your responses.""",
+ id="customer-service-agent",
+ )
+
+ # Sample queries to demonstrate observability
+ queries = [
+ "What's the billing summary for customer 1?",
+ "Show me the data usage for subscription 1 from 2025-01-01 to 2025-01-15",
+ ]
+
+ thread = agent.get_new_thread()
+
+ for query in queries:
+ print(f"\n👤 User: {query}")
+ print(f"🤖 Agent: ", end="")
+
+ # Each query gets its own span
+ with tracer.start_as_current_span(
+ "agent-query",
+ kind=SpanKind.CLIENT
+ ) as query_span:
+ query_span.set_attribute("user.query", query)
+
+ async for update in agent.run_stream(query, thread=thread):
+ if update.text:
+ print(update.text, end="")
+
+ print()
+
+ print("\n" + "=" * 60)
+ print("✅ Session complete!")
+ print(f"📊 View traces at: https://aka.ms/amg/dash/af-agent")
+ print(f" Filter by Trace ID: {trace_id}")
+ print("=" * 60)
+
+
+if __name__ == "__main__":
+ asyncio.run(run_agent_with_tracing())
diff --git a/agentic_ai/observability/setup.py b/agentic_ai/observability/setup.py
new file mode 100644
index 000000000..d91e6ab17
--- /dev/null
+++ b/agentic_ai/observability/setup.py
@@ -0,0 +1,104 @@
+# Copyright (c) Microsoft. All rights reserved.
+
+"""
+Observability setup for Agent Framework applications with Application Insights.
+
+This module configures OpenTelemetry to send traces, logs, and metrics to
+Azure Application Insights, enabling full observability of agent executions.
+
+Azure Monitor Dashboards (no Grafana required):
+ - Agent Overview: https://aka.ms/amg/dash/af-agent
+ - Workflow Overview: https://aka.ms/amg/dash/af-workflow
+
+Usage:
+ 1. Set APPLICATIONINSIGHTS_CONNECTION_STRING in your .env
+ 2. Call setup_observability() once at app startup
+ 3. All Agent Framework traces are captured automatically
+"""
+
+import os
+import logging
+from typing import Optional
+
+logger = logging.getLogger(__name__)
+
+# Track initialization state
+_initialized = False
+
+
+def setup_observability(
+ connection_string: Optional[str] = None,
+ service_name: str = "contoso-agent",
+ enable_live_metrics: bool = True,
+ enable_sensitive_data: bool = False,
+) -> bool:
+ """
+ Configure Application Insights for Agent Framework observability.
+
+ This follows the pattern from agent-framework/python/samples/getting_started/observability/.
+
+ Args:
+ connection_string: App Insights connection string (or set APPLICATIONINSIGHTS_CONNECTION_STRING).
+ service_name: Service name shown in App Insights.
+ enable_live_metrics: Enable Live Metrics stream.
+ enable_sensitive_data: Include prompts/responses in traces (dev only!).
+
+ Returns:
+ True if setup succeeded, False otherwise.
+ """
+ global _initialized
+
+ if _initialized:
+ return True
+
+ # Get connection string from parameter or environment
+ conn_str = connection_string or os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING")
+
+ if not conn_str:
+ logger.debug("No APPLICATIONINSIGHTS_CONNECTION_STRING - observability disabled")
+ return False
+
+ try:
+ from azure.monitor.opentelemetry import configure_azure_monitor
+ from agent_framework.observability import create_resource, enable_instrumentation
+
+ # Set service name via standard env var
+ os.environ.setdefault("OTEL_SERVICE_NAME", service_name)
+
+ # Configure Azure Monitor (same pattern as agent-framework samples)
+ configure_azure_monitor(
+ connection_string=conn_str,
+ resource=create_resource(),
+ enable_live_metrics=enable_live_metrics,
+ )
+
+ # Enable Agent Framework instrumentation
+ enable_instrumentation(enable_sensitive_data=enable_sensitive_data)
+
+ _initialized = True
+ logger.info(f"✅ Application Insights observability enabled (service: {service_name})")
+ return True
+
+ except ImportError as e:
+ logger.warning(f"Observability dependencies not installed: {e}")
+ return False
+ except Exception as e:
+ logger.warning(f"Failed to configure observability: {e}")
+ return False
+
+
+def get_tracer(name: str = "contoso-agent"):
+ """Get an OpenTelemetry tracer for creating custom spans."""
+ from agent_framework.observability import get_tracer as af_get_tracer
+ return af_get_tracer(name)
+
+
+def get_trace_id() -> Optional[str]:
+ """Get the current trace ID for correlation."""
+ from opentelemetry import trace
+ from opentelemetry.trace.span import format_trace_id
+
+ current_span = trace.get_current_span()
+ if current_span and current_span.get_span_context().is_valid:
+ return format_trace_id(current_span.get_span_context().trace_id)
+ return None
diff --git a/agentic_ai/evaluations/telemetry.py b/agentic_ai/observability/telemetry.py
similarity index 100%
rename from agentic_ai/evaluations/telemetry.py
rename to agentic_ai/observability/telemetry.py
diff --git a/agentic_ai/scenarios/durable_agent/README.md b/agentic_ai/scenarios/durable_agent/README.md
deleted file mode 100644
index a5cb6820c..000000000
--- a/agentic_ai/scenarios/durable_agent/README.md
+++ /dev/null
@@ -1,110 +0,0 @@
-# Durable-Agent Demo 🚀
-
-> An experiment in making **AI agents durable** – i.e. able to
-> preserve state, survive crashes / restarts and handle long-running
-> workflows without blocking.
-
----
-
-## 1. Why “durable” agents?
-
-Normal conversational agents keep all their short-term memory **in RAM**.
-If the process dies, the whole context is lost. Durable agents, in contrast, are designed to:
-
-1. **Persist state** – every turn they write their internal state to an external store (Cosmos DB in Azure or an in-memory dict for local runs).
-2. **Resume seamlessly** – on the next request they reload that state and continue the conversation exactly where they left off.
-3. **Survive long-running work** – they can kick off background jobs or remote orchestrations (Azure Durable Functions, workflows, etc.), go completely idle, and later integrate the result back into the conversation once it is available.
-
-These three abilities together make a durable agent *resilient*, *tolerant to infra hiccups*, and *suitable for complex, multi-step business flows*.
-
----
-
-## 2. High-level architecture
-
-```mermaid
-flowchart TD
- A["Streamlit UI"] -- REST --> B["FastAPI Backend"]
- subgraph Backend
- B --> C["Durable Agent
Round-Robin
Group Chat"]
- C -- "load / save" --> D["State Store
CosmosDB/dict"]
- C -- "calls" --> E["MCP & Local Tools"]
- C -- "schedules" --> F["Background Worker
(thread/Durable Fn.)"]
- F -- "update" --> D
- end
-```
-
-## 3. Sequence of a long-running operation
-```mermaid
-sequenceDiagram
- participant U as User (UI)
- participant BE as FastAPI
- participant AG as Durable Agent
- participant BG as Background Job
- participant SS as State Store
-
- U->>BE: "Activate line …"
- BE->>AG: chat(task)
- AG->>AG: tool call activate_new_line
- AG->>BG: spawn thread & return
- AG->>SS: save state
- AG->>BE: "Task scheduled, please wait…"
- BE->>U: immediate reply
-
- Note over BG: ~long running task
- BG-->>SS: append result message
-
- U->>BE: "Is it done?"
- BE->>AG: chat(next turn)
- AG->>SS: load state (+result)
- AG-->>BE: "✅ Done, line is active."
- BE-->>U: final reply
-```
-## 4. Current Demo – What is Implemented?
-
-| Capability | Status | Notes |
-|---------------------------------------|:------:|--------------------------------------------------------------------------------------------------------|
-| Resilient state persistence | ✅ | Cosmos DB or in-mem dict |
-| Agent re-hydration | ✅ | Agent loads TeamState on every request |
-| Long-running tool scheduling | ✅ | Simulated via Python threading (20 s sleep) |
-| Result injection after completion | ✅ | Worker edits saved state (`FunctionExecutionResultMessage`) |
-| Push notifications to UI (SSE) | ♻️ | Optional add-on shown in docs |
-| Integration with Azure Durable Fn. | 🟡 | Conceptually supported; not wired-up in repo |
-| Full error-handling / retries | 🟡 | Basic happy-path only |
-
----
-
-## 5. Code Hotspots
-
-- **agents/base_agent.py**
- Abstract helper that reads env-vars and exposes `chat_async`.
-
-- **agents/durable_agent/loop_agent.py**
- - Builds a `RoundRobinGroupChat` with one `AssistantAgent`.
- - Registers normal MCP tools and a custom long-running tool `activate_new_line`.
- - Spawns a background thread that, after the fake 20 s delay, loads the saved `TeamState`, injects a synthetic `ToolCallRequestEvent` plus matching `ToolCallExecutionEvent`, and rewrites the state.
-
-- **utils/CosmosDBStateStore**
- Thin wrapper around Cosmos container ⇒ “dict-like” API.
-
-- **backend.py**
- FastAPI endpoints:
- `/chat`, `/history/{id}`, `/reset_session`.
- (Optional) `/events/{id}` for Server-Sent Events push.
-
----
-
-## 6. Extending the Pattern
-
-- Swap the background threading code for Azure Durable Functions or Azure Container Apps Jobs – post the final status to the same state store key.
-- Support multiple parallel long-running calls by including a unique tool `call_id` per job; each worker injects its own result message.
-- Add retry metadata (e.g. “attempt #”, “next ETA”) inside the state so the agent can reason about failures and keep the user informed.
-- Implement push UI (SSE / WebSockets) for real-time updates without polling.
-
----
-
-## 7. Limitations & Next Steps
-
-- Persisted messages grow indefinitely – add TTL / pruning.
-- Background thread approach works only for single-process deployments – use external orchestrators in prod.
-- No security model yet (authN / authZ on APIs, encrypt Cosmos).
-
diff --git a/agentic_ai/scenarios/durable_agent/loop_agent.py b/agentic_ai/scenarios/durable_agent/loop_agent.py
deleted file mode 100644
index e383a7c43..000000000
--- a/agentic_ai/scenarios/durable_agent/loop_agent.py
+++ /dev/null
@@ -1,295 +0,0 @@
-import os
-from dotenv import load_dotenv
-from typing import Any, Dict
-from autogen_agentchat.agents import AssistantAgent
-from autogen_agentchat.teams import RoundRobinGroupChat
-from autogen_agentchat.conditions import TextMessageTermination
-from autogen_core import CancellationToken
-from autogen_ext.models.openai import AzureOpenAIChatCompletionClient
-from autogen_ext.tools.mcp import StreamableHttpServerParams, mcp_server_tools
-from agents.base_agent import BaseAgent
-import threading, asyncio, uuid
-from datetime import datetime,timezone
-from autogen_core.model_context import BufferedChatCompletionContext
-import json
-
-load_dotenv()
-
-# NEW imports
-import asyncio, threading, time
-from typing import Any, Dict
-
-
-
-# ---------------------------------------------------------------------
-# 1. User-visible tool (LLM will call this)
-# ---------------------------------------------------------------------
-async def activate_new_line(customer_id: str, phone_number: str) -> str:
- """
- Handle the activation of a new line for a customer.
-
- The operation is long-running (a few minutes).
- Immediately acknowledge that the task was scheduled; the customer will be
- notified once activation is complete.
- """
- # Body never executed – see wrapper registration further below.
-# ───────────────────────────────────────────────────────────────────────
-# 2) internal helper – does the real scheduling
-# ───────────────────────────────────────────────────────────────────────
-# ---------------------------------------------------------------------
-# 2. Internal helper, does the real scheduling
-# ---------------------------------------------------------------------
-async def _activate_new_line_impl(
- customer_id: str,
- phone_number: str,
- *,
- __session_id__: str,
- __state_store__: Dict[str, Any],
-) -> str:
- """
- Internal helper invoked by wrapper; identical semantics but with context.
- """
-
- # ── background job that will run AFTER 20 s ──────────────────────
-
-
- async def _post_completion() -> None:
- await asyncio.sleep(25) # 1️⃣ simulate work (shortened for testing)
- print(f"🔔 Background completion starting for session {__session_id__}")
-
- # 2️⃣ Load the persisted TeamState dict
- team_state: dict[str, Any] | None = __state_store__.get(__session_id__)
- if team_state is None:
- return # session was wiped
-
- # 3️⃣ Compute helper refs
- ai_ctx = team_state["agent_states"]["ai_assistant"]["agent_state"]["llm_context"]["messages"]
- thread = team_state["agent_states"]["RoundRobinGroupChatManager"]["message_thread"]
-
- # 4️⃣ Build a NEW tool-call id & arguments
- new_call_id = f"call_{uuid.uuid4().hex}"
- arguments_json = json.dumps(
- {"customer_id": customer_id, "phone_number": phone_number}
- )
-
- # 5️⃣ Tool-CALL message (AssistantMessage / ToolCallRequestEvent)
- call_msg_assistant = {
- "content": [
- {
- "id": new_call_id,
- "arguments": arguments_json,
- "name": "activate_new_line_result_update",
- }
- ],
- "thought": None,
- "source": "ai_assistant",
- "type": "AssistantMessage",
- }
- call_msg_thread = {
- "id": str(uuid.uuid4()),
- "source": "ai_assistant",
- "models_usage": None,
- "metadata": {},
- "created_at": datetime.utcnow().isoformat(timespec="milliseconds") + "Z",
- "content": call_msg_assistant["content"],
- "type": "ToolCallRequestEvent",
- }
-
- # 6️⃣ EXECUTION / result message
- exec_payload = {
- "content": (
- f"✅ Activation complete – customer {customer_id}, "
- f"phone {phone_number} is now live."
- ),
- "name": "activate_new_line_result_update",
- "call_id": new_call_id,
- "is_error": False,
- }
- exec_msg_assistant = {
- "content": [exec_payload],
- "type": "FunctionExecutionResultMessage",
- }
- exec_msg_thread = {
- "id": str(uuid.uuid4()),
- "source": "ai_assistant",
- "models_usage": None,
- "metadata": {},
- "created_at": datetime.utcnow().isoformat(timespec="milliseconds") + "Z",
- "content": [exec_payload],
- "type": "ToolCallExecutionEvent",
- }
-
- # 7️⃣ Append to assistant LLM context (keeps order)
- ai_ctx.extend([call_msg_assistant, exec_msg_assistant])
-
- # 8️⃣ Append to group-chat message thread
- thread.extend([call_msg_thread, exec_msg_thread])
-
- # 9️⃣ Persist updated state
- __state_store__[__session_id__] = team_state
-
- # 🔟 Try to live-push these synthetic events to any websocket clients
- print(f"🔔 Attempting to broadcast completion events for session {__session_id__}")
- try:
- # Access MANAGER through builtins (set by backend)
- import builtins
- MANAGER = getattr(builtins, 'GLOBAL_WS_MANAGER', None)
- if MANAGER:
- print(f"Found MANAGER via builtins")
- else:
- print("No GLOBAL_WS_MANAGER found in builtins")
- except Exception as e:
- print(f"Error accessing builtins: {e}")
- MANAGER = None
-
- if MANAGER:
- print(f"Broadcasting completion events to {len(MANAGER.sessions.get(__session_id__, []))} clients")
- # Mirror Autogen stream shapes used in serialize_autogen_event
- try:
- await MANAGER.broadcast(__session_id__, {
- "type": "tool_call",
- "calls": [{
- "name": "activate_new_line_result_update",
- "arguments": {"customer_id": customer_id, "phone_number": phone_number}
- }]
- })
- await MANAGER.broadcast(__session_id__, {
- "type": "tool_result",
- "results": [{
- "name": "activate_new_line_result_update",
- "is_error": False,
- "content": f"✅ Activation complete – customer {customer_id}, phone {phone_number} is now live."
- }]
- })
- await MANAGER.broadcast(__session_id__, {
- "type": "message",
- "role": "assistant",
- "content": f"Activation finished for {phone_number}."
- })
- print("Successfully broadcast all completion events")
- except Exception as e:
- print(f"Error broadcasting: {e}")
- else:
- print("No MANAGER found - background events will not be pushed to UI")
- # Schedule background coroutine on current loop (preferred over new loop in thread)
- try:
- loop = asyncio.get_running_loop()
- loop.create_task(_post_completion())
- except RuntimeError:
- # Fallback (should not normally happen inside async context)
- threading.Thread(target=lambda: asyncio.run(_post_completion()), daemon=True).start()
-
- # Immediate (first) response shown to the user
- return (
- "🔔 Background task scheduled. "
- "Activation will take a few minutes; you will be notified once it is done."
- )
-# ---------------------------------------------------------------------------
-class Agent(BaseAgent):
- def __init__(self, state_store, session_id, access_token: str | None = None) -> None:
- super().__init__(state_store, session_id)
- self.loop_agent = None
- self._initialized = False
- self._access_token = access_token
-
- async def _setup_loop_agent(self) -> None:
- """Initialize the assistant and tools once."""
- if self._initialized:
- return
-
- headers = {"Content-Type": "application/json"}
- if self._access_token:
- headers["Authorization"] = f"Bearer {self._access_token}"
-
- server_params = StreamableHttpServerParams(
- url=self.mcp_server_uri,
- headers=headers,
- timeout=30
- )
-
- # Fetch tools (async)
- tools = await mcp_server_tools(server_params)
-
- async def _activate_wrapper(customer_id: str, phone_number: str) -> str:
- # Call internal helper with conversation context
- return await _activate_new_line_impl(
- customer_id,
- phone_number,
- __session_id__=self.session_id,
- __state_store__=self.state_store,
- )
-
- # Copy metadata so AssistantAgent sees the right signature/docs
- _activate_wrapper.__name__ = activate_new_line.__name__
- _activate_wrapper.__doc__ = activate_new_line.__doc__
- _activate_wrapper.__annotations__ = activate_new_line.__annotations__
-
- tools.append(_activate_wrapper) # AFTER tools = await mcp_server_tools()
- # Set up the OpenAI/Azure model client
- model_client = AzureOpenAIChatCompletionClient(
- api_key=self.azure_openai_key,
- azure_endpoint=self.azure_openai_endpoint,
- api_version=self.api_version,
- azure_deployment=self.azure_deployment,
- model=self.openai_model_name,
- )
-
- # Set up the assistant agent
- model_context = BufferedChatCompletionContext(buffer_size=10)
- agent = AssistantAgent(
- name="ai_assistant",
- model_client=model_client,
- model_context=model_context,
- tools=tools,
- system_message=(
- "You are a helpful assistant. You can use multiple tools to find information and answer questions. "
- "Review the tools available to you and use them as needed. You can also ask clarifying questions if "
- "the user is not clear."
- )
- )
-
- # Set the termination condition: stop when agent answers as itself
- termination_condition = TextMessageTermination("ai_assistant")
-
- self.loop_agent = RoundRobinGroupChat(
- [agent],
- termination_condition=termination_condition,
- )
-
- if self.state:
- await self.loop_agent.load_state(self.state)
- self._initialized = True
-
- async def chat_async(self, prompt: str) -> str:
- """Ensure agent/tools are ready and process the prompt."""
- await self._setup_loop_agent()
-
- response = await self.loop_agent.run(task=prompt, cancellation_token=CancellationToken())
- assistant_response = response.messages[-1].content
-
- messages = [
- {"role": "user", "content": prompt},
- {"role": "assistant", "content": assistant_response}
- ]
- self.append_to_chat_history(messages)
-
- # Update/store latest agent state
- new_state = await self.loop_agent.save_state()
- print(f"Updated state for session {self.session_id}: {new_state}")
- self._setstate(new_state)
-
- return assistant_response
- async def chat_stream(self, prompt: str):
- """
- Async generator that yields Autogen streaming events while processing prompt.
- Backend will consume this and forward to frontend.
- """
- await self._setup_loop_agent()
- stream = self.loop_agent.run_stream(task=prompt, cancellation_token=CancellationToken())
-
- async for event in stream:
- yield event
-
- # After run finishes, persist state
- new_state = await self.loop_agent.save_state()
- self._setstate(new_state)
\ No newline at end of file
diff --git a/agentic_ai/scenarios/progress_update/chainlit.md b/agentic_ai/scenarios/progress_update/chainlit.md
deleted file mode 100644
index 4507ac467..000000000
--- a/agentic_ai/scenarios/progress_update/chainlit.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Welcome to Chainlit! 🚀🤖
-
-Hi there, Developer! 👋 We're excited to have you on board. Chainlit is a powerful tool designed to help you prototype, debug and share applications built on top of LLMs.
-
-## Useful Links 🔗
-
-- **Documentation:** Get started with our comprehensive [Chainlit Documentation](https://docs.chainlit.io) 📚
-- **Discord Community:** Join our friendly [Chainlit Discord](https://discord.gg/k73SQ3FyUh) to ask questions, share your projects, and connect with other developers! 💬
-
-We can't wait to see what you create with Chainlit! Happy coding! 💻😊
-
-## Welcome screen
-
-To modify the welcome screen, edit the `chainlit.md` file at the root of your project. If you do not want a welcome screen, just leave this file empty.
diff --git a/agentic_ai/scenarios/progress_update/frontend.py b/agentic_ai/scenarios/progress_update/frontend.py
deleted file mode 100644
index 9df4dfc16..000000000
--- a/agentic_ai/scenarios/progress_update/frontend.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# ui/chainlit_backend_stream.py
-import os
-import uuid
-import json
-import asyncio
-import chainlit as cl
-import websockets
-
-# Backend URL, e.g., ws://localhost:7000
-BACKEND_URL = os.getenv("BACKEND_URL", "http://localhost:7000")
-# Convert to ws:// if http:// given
-if BACKEND_URL.startswith("http://"):
- WS_BASE = "ws://" + BACKEND_URL[len("http://"):]
-elif BACKEND_URL.startswith("https://"):
- WS_BASE = "wss://" + BACKEND_URL[len("https://"):]
-else:
- WS_BASE = BACKEND_URL
-
-WS_CHAT_URL = f"{WS_BASE}/ws/chat"
-
-@cl.on_chat_start
-async def on_chat_start():
- # per-user session id
- session_id = str(uuid.uuid4())
- cl.user_session.set("session_id", session_id)
- await cl.Message(f"Session: {session_id}\nHow can I help you?").send()
-
-@cl.on_message
-async def on_message(message: cl.Message):
- session_id = cl.user_session.get("session_id")
-
- # Prepare messages
- progress_msg = cl.Message(content="")
- assistant_msg = cl.Message(content="")
- await progress_msg.send() # create placeholder
- await assistant_msg.send()
-
- # Optional: If your backend WS requires token, set it here
- # headers = [("Authorization", f"Bearer {YOUR_TOKEN}")]
- headers = None
-
- async def send_prompt():
- async with websockets.connect(WS_CHAT_URL, additional_headers=headers) as ws:
- payload = {
- "session_id": session_id,
- "prompt": message.content,
- # "access_token": "...", # include if your backend forwards to MCP
- }
- await ws.send(json.dumps(payload))
-
- while True:
- try:
- raw = await ws.recv()
- except websockets.ConnectionClosed:
- break
-
- try:
- data = json.loads(raw)
- except Exception:
- continue
-
- typ = data.get("type")
- if typ == "progress":
- # side-channel MCP tool progress
- line = f"[{data.get('percent', 0)}%] {data.get('message', '')}\n"
- await progress_msg.stream_token(line)
- elif typ == "token":
- await assistant_msg.stream_token(data.get("content", ""))
- elif typ == "message":
- await assistant_msg.stream_token("\n" + data.get("content", ""))
- elif typ == "final":
- # Final assistant message
- content = data.get("content", "")
- if content:
- await assistant_msg.stream_token("\n" + content)
- elif typ == "done":
- # finish
- break
- elif typ == "error":
- await assistant_msg.stream_token(f"\n[Error] {data.get('message','')}")
- break
-
- await send_prompt()
-
- # finalize messages
- await progress_msg.update()
- await assistant_msg.update()
\ No newline at end of file
diff --git a/agentic_ai/scenarios/progress_update/loop_agent_progress.py b/agentic_ai/scenarios/progress_update/loop_agent_progress.py
deleted file mode 100644
index 37824fa1e..000000000
--- a/agentic_ai/scenarios/progress_update/loop_agent_progress.py
+++ /dev/null
@@ -1,231 +0,0 @@
-# agents/autogen/single_agent/loop_agent.py
-import os
-import asyncio
-from typing import Any, Callable, Awaitable, Optional, Mapping, List
-
-from dotenv import load_dotenv
-
-from autogen_agentchat.agents import AssistantAgent
-from autogen_agentchat.teams import RoundRobinGroupChat
-from autogen_agentchat.conditions import TextMessageTermination
-from autogen_core import CancellationToken
-from autogen_core.tools import BaseTool
-from autogen_core.utils import schema_to_pydantic_model
-from pydantic import BaseModel
-
-from autogen_ext.models.openai import AzureOpenAIChatCompletionClient
-
-from fastmcp.client import Client
-from fastmcp.client.transports import StreamableHttpTransport
-
-from agents.base_agent import BaseAgent
-import mcp
-from fastmcp.exceptions import ToolError
-
-
-
-load_dotenv()
-
-
-# A simple Pydantic model for the return value (BaseTool requires a BaseModel return type)
-class ToolTextResult(BaseModel):
- text: str
-
-ProgressSink = Callable[[dict], Awaitable[None]]
-
-class MCPProgressTool(BaseTool[BaseModel, ToolTextResult]):
- """
- Wrap a remote MCP tool so Autogen sees it as a local tool, while forwarding progress updates.
- """
-
- def __init__(
- self,
- client: Client,
- mcp_tool: mcp.types.Tool,
- progress_sink: Optional[ProgressSink] = None,
- ) -> None:
- # Build a Pydantic args model from the MCP tool's JSON schema
- args_model = schema_to_pydantic_model(mcp_tool.inputSchema)
- super().__init__(
- args_type=args_model,
- return_type=ToolTextResult,
- name=mcp_tool.name,
- description=mcp_tool.description or "",
- strict=False, # set True if you want to enforce no extra args/defaults
- )
- self._client = client
- self._tool_name = mcp_tool.name
- self._progress_sink = progress_sink
-
- async def run(self, args: BaseModel, cancellation_token: CancellationToken) -> ToolTextResult:
- # Serialize args excluding unset values so we only send what's provided
- kwargs: Mapping[str, Any] = args.model_dump(exclude_unset=True)
-
- async def progress_cb(progress: float, total: float | None, message: str | None):
- if not self._progress_sink:
- return
- try:
- pct = int((progress / total) * 100) if total else int(progress)
- except Exception:
- pct = int(progress)
- await self._progress_sink({
- "type": "progress",
- "tool": self._tool_name,
- "percent": pct,
- "message": message or "",
- })
-
- # Use a fresh session for each tool call to avoid cross-call state pollution
- async with self._client.new() as c:
- call_coro = c.call_tool_mcp(
- name=self._tool_name,
- arguments=dict(kwargs),
- progress_handler=progress_cb,
- )
- task = asyncio.create_task(call_coro)
- # If CancellationToken exposes a way to bind, hook it here. Otherwise, just check state:
- if cancellation_token.is_cancelled():
- task.cancel()
- raise asyncio.CancelledError("Operation cancelled")
-
- try:
- result: mcp.types.CallToolResult = await task
- except asyncio.CancelledError:
- # Propagate cancellation
- raise
-
- if result.isError:
- # Bubble up MCP tool error for Autogen to surface
- msg = ""
- try:
- msg = (result.content[0].text if result.content else "Tool error")
- except Exception:
- msg = "Tool error"
- raise ToolError(msg)
-
- # Aggregate text contents; adjust as needed if you want images/resources
- texts: list[str] = []
- for content in result.content:
- if isinstance(content, mcp.types.TextContent):
- texts.append(content.text)
- final_text = "\n".join(texts) if texts else "(no text content)"
- return ToolTextResult(text=final_text)
-
- # Provide a readable string for the tool’s result (what the LLM “sees” in logs/streams)
- def return_value_as_string(self, value: Any) -> str:
- try:
- if isinstance(value, ToolTextResult):
- return value.text
- except Exception:
- pass
- return super().return_value_as_string(value)
-
-
-
-
-class Agent(BaseAgent):
- def __init__(self, state_store, session_id, access_token: str | None = None) -> None:
- super().__init__(state_store, session_id)
- self.loop_agent = None
- self._initialized = False
- self._access_token = access_token
- self._progress_sink: Optional[Callable[[dict], Awaitable[None]]] = None # side-channel sink
-
- def set_progress_sink(self, sink: Optional[Callable[[dict], Awaitable[None]]]) -> None:
- """Install (or remove) a per-call async sink to receive side-channel tool progress events."""
- self._progress_sink = sink
-
- async def _build_mcp_progress_tools(self,
- url: str,
- headers: Optional[dict[str, str]] = None,
- auth: Optional[str] = None, # "Bearer " or fastmcp.client.auth.BearerAuth
- progress_sink: Optional[ProgressSink] = None,
- ) -> List[MCPProgressTool]:
- """
- Create progress-aware Autogen tools for every remote MCP tool at the given endpoint.
- """
- transport = StreamableHttpTransport(url, headers=headers, auth=auth)
-
- client = Client(transport=transport)
- async with client:
- tools_resp = await client.list_tools_mcp()
- adapters: List[MCPProgressTool] = []
- for mcp_tool in tools_resp.tools:
- adapters.append(MCPProgressTool(client, mcp_tool, progress_sink))
- return adapters
-
-
- async def _setup_loop_agent(self) -> None:
- """Initialize the assistant and loop agent once, using our progress-aware tools."""
- if self._initialized:
- return
-
- # Build tools with progress support
- tools = await self._build_mcp_progress_tools(
- url=self.mcp_server_uri,
- headers={"Authorization": f"Bearer {self._access_token}"} if self._access_token else None,
- progress_sink=self._progress_sink,
- )
-
- # Set up the OpenAI/Azure model client
- model_client = AzureOpenAIChatCompletionClient(
- api_key=self.azure_openai_key,
- azure_endpoint=self.azure_openai_endpoint,
- api_version=self.api_version,
- azure_deployment=self.azure_deployment,
- model=self.openai_model_name,
- )
-
- # Set up the assistant agent
- agent = AssistantAgent(
- name="ai_assistant",
- model_client=model_client,
- tools=tools,
- system_message=(
- "You are a helpful assistant. You can use tools to get work done. "
- "Provide progress when running long operations."
- ),
- )
-
- termination_condition = TextMessageTermination("ai_assistant")
-
- self.loop_agent = RoundRobinGroupChat(
- [agent],
- termination_condition=termination_condition,
- )
-
- if self.state:
- await self.loop_agent.load_state(self.state)
- self._initialized = True
-
- async def chat_async(self, prompt: str) -> str:
- """Backwards-compatible single-shot call."""
- await self._setup_loop_agent()
- response = await self.loop_agent.run(task=prompt, cancellation_token=CancellationToken())
- assistant_response = response.messages[-1].content
-
- messages = [
- {"role": "user", "content": prompt},
- {"role": "assistant", "content": assistant_response}
- ]
- self.append_to_chat_history(messages)
-
- new_state = await self.loop_agent.save_state()
- self._setstate(new_state)
-
- return assistant_response
-
- async def chat_stream(self, prompt: str):
- """
- Async generator that yields Autogen streaming events while processing prompt.
- Backend will consume this and forward to frontend.
- """
- await self._setup_loop_agent()
- stream = self.loop_agent.run_stream(task=prompt, cancellation_token=CancellationToken())
-
- async for event in stream:
- yield event
-
- # After run finishes, persist state
- new_state = await self.loop_agent.save_state()
- self._setstate(new_state)
\ No newline at end of file
diff --git a/agentic_ai/workflow/README.md b/agentic_ai/workflow/README.md
deleted file mode 100644
index cca59f26b..000000000
--- a/agentic_ai/workflow/README.md
+++ /dev/null
@@ -1,195 +0,0 @@
-# Workflow Architecture
-
-The Agent Framework workflow system is a **directed-graph execution engine** modeled after Google's [Pregel](https://research.google/pubs/pub36726/) distributed graph computation model, adapted for orchestrating AI agents, tools, and arbitrary compute steps in a type-safe, checkpointable, and observable manner.
-
-## Core abstractions
-
-| Component | Purpose |
-|-----------|---------|
-| **Executor** (`_executor.py`) | A unit of work with typed handlers that process messages. Can be a class (subclassing `Executor`) or a decorated function (`@executor`). Executors define what input types they accept and what they emit. |
-| **Edge / EdgeGroup** (`_edge.py`) | Defines how messages flow between executors. Supports single, fan-out (1→N), fan-in (N→1 aggregation), and switch/case routing patterns. |
-| **WorkflowContext** (`_workflow_context.py`) | Injected into each executor handler; provides `send_message()`, `yield_output()`, state persistence APIs (`set_state`, `get_state`, `set_shared_state`). Enforces type safety through generic parameters. |
-| **Runner** (`_runner.py`) | Orchestrates execution in synchronized **supersteps**: delivers messages, invokes executors concurrently, drains events, creates checkpoints. Runs until the graph becomes idle (no pending messages). |
-| **Workflow** (`_workflow.py`) | The user-facing API that wraps the Runner and provides entry points (`run()`, `run_stream()`, `run_from_checkpoint()`). Built via `WorkflowBuilder`. |
-
----
-
-## Execution model: Pregel-style supersteps
-
-### 1. Initialization phase
-
-- User calls `workflow.run(initial_message)`.
-- The starting executor receives the message and runs its handler.
-- Handler can emit messages via `ctx.send_message()` or final outputs via `ctx.yield_output()`.
-- All emitted messages are queued in the `RunnerContext`.
-
-### 2. Superstep iteration
-
-- The Runner **drains** all pending messages from the queue.
-- Messages are routed through `EdgeRunner` implementations based on edge topology:
- - **SingleEdgeRunner**: Delivers to one target if type and condition match.
- - **FanOutEdgeRunner**: Broadcasts to multiple targets or selects a subset dynamically.
- - **FanInEdgeRunner**: Buffers messages from multiple sources; delivers aggregated list when all sources have sent.
- - **SwitchCaseEdgeRunner**: Evaluates predicates and routes to the first matching case.
-- All deliverable messages invoke their target executors **concurrently** (via `asyncio.gather`).
-- Each executor processes its messages and may emit new messages or outputs.
-- At the end of the superstep:
- - Events (outputs, custom events) are streamed to the caller.
- - A checkpoint is optionally created (if `CheckpointStorage` is configured).
- - The Runner checks if new messages are pending; if yes, starts the next superstep.
-
-### 3. Convergence / termination
-
-- The workflow runs until **no messages remain** or the **max iteration limit** is hit.
-- Final state is emitted as a `WorkflowStatusEvent`:
- - `IDLE`: Clean completion, no pending requests.
- - `IDLE_WITH_PENDING_REQUESTS`: Waiting for external input (via `RequestInfoExecutor`).
- - `FAILED`: An executor raised an exception.
-
----
-
-## Message routing and type safety
-
-- Each executor declares **input types** via handler parameter annotations (`text: str`, `data: MyModel`, etc.).
-- `WorkflowContext[T_Out]` declares the **output message type** the executor can emit.
-- `WorkflowContext[T_Out, T_W_Out]` adds workflow-level output types (for `yield_output`).
-- Edge runners use `executor.can_handle(message_data)` to enforce type compatibility at runtime.
-- Routing predicates (`edge.should_route(data)`) and selection functions (`selection_func(data, targets)`) allow dynamic control flow.
-
----
-
-## State and persistence
-
-| Layer | Mechanism |
-|-------|-----------|
-| **Executor-local state** | `ctx.set_state(key, value)` / `ctx.get_state(key)` stores per-executor JSON blobs in the `RunnerContext`. Executors can override `snapshot_state()` / `restore_state()` for custom serialization. |
-| **Shared state** | `WorkflowContext.set_shared_state(key, value)` writes to a `SharedState` dictionary visible to all executors. Protected by an async lock to prevent race conditions. |
-| **Checkpoints** | After each superstep, the Runner calls `_auto_snapshot_executor_states()`, then serializes:
- Pending messages per executor
- Shared state dictionary
- Executor state snapshots
- Iteration counter / metadata
`CheckpointStorage` (in-memory, file, Redis, Cosmos DB) persists `WorkflowCheckpoint` objects. |
-| **Restoration** | `workflow.run_from_checkpoint(checkpoint_id)` rehydrates the full runner context, re-injects shared state, restores iteration count, and validates graph topology (via a hash of the executor/edge structure). |
-
-Checkpoints are **delta-neutral**: the graph structure itself is not serialized, only the runtime state. You must rebuild the workflow with the same topology before restoring.
-
----
-
-## Observability and tracing
-
-- **OpenTelemetry integration**: The workflow creates a root span (`workflow_run`) that encompasses all supersteps. Each executor invocation and edge delivery gets nested spans.
-- **Trace context propagation**: Messages carry `trace_contexts` and `source_span_ids` to link spans across async boundaries (following W3C Trace Context).
-- **Event streaming**: The Runner emits `WorkflowEvent` subclasses:
- - `WorkflowStartedEvent`, `WorkflowStatusEvent` (lifecycle).
- - `WorkflowOutputEvent` (from `yield_output`).
- - `RequestInfoEvent` (external input requests).
- - Custom events via `ctx.add_event()`.
-- Events are streamed live via `run_stream()` or collected in `WorkflowRunResult` for batch runs.
-
----
-
-## Composition patterns
-
-1. **Nested workflows**: `WorkflowExecutor` wraps a child workflow as an executor. When invoked, it runs the child to completion and processes outputs.
-2. **Human-in-the-loop**: `RequestInfoExecutor` emits `RequestInfoEvent`, transitions the workflow to `IDLE_WITH_PENDING_REQUESTS`, and waits for external responses via `send_responses()`.
-3. **Multi-agent teams**: `MagenticOrchestratorExecutor` (in `_magentic.py`) wraps multiple agents, manages broadcast/targeted communication, and snapshots each participant's conversation history.
-
----
-
-## Key design decisions
-
-- **Type-driven routing**: Edge runners and executors use Python type annotations to enforce contracts at runtime, providing early feedback for wiring errors.
-- **Separation of data/control planes**: Executor invocations and message passing happen "under the hood"; only workflow-level events (outputs, requests) are exposed to callers. This keeps the event stream clean and hides internal coordination.
-- **Checkpointing by convention**: Executors opt into persistence by implementing `snapshot_state()` or exposing a `state` attribute. The framework handles serialization (including Pydantic models and dataclasses) transparently.
-- **Graph immutability**: Once built, workflows are immutable. This enables safe checkpoint restoration and parallel invocations (if you construct separate `Workflow` instances).
-- **Concurrency within supersteps**: All deliverable messages in a superstep execute concurrently. This parallelizes work but requires shared state to be protected (via `SharedState`'s async lock).
-
----
-
-## Validation and safety
-
-- **Graph validation**: `validate_workflow_graph()` (in `_validation.py`) checks for unreachable executors, missing start nodes, and cycles (for non-cyclic workflows).
-- **Concurrent execution guard**: The `Workflow` class prevents multiple `run()` calls on the same instance to avoid state corruption.
-- **Max iterations**: Prevents infinite loops by bounding superstep counts (default 100, configurable).
-- **Graph signature hashing**: Before restoring a checkpoint, the Runner compares a hash of the workflow topology to the checkpoint metadata to detect structural changes.
-
----
-
-## Sample execution trace
-
-```
-User calls workflow.run("hello world")
- ↓
-Workflow emits WorkflowStartedEvent, WorkflowStatusEvent(IN_PROGRESS)
- ↓
-Executor "upper_case_executor" receives "hello world"
- → Handler: to_upper_case(text: str, ctx: WorkflowContext[str])
- → Calls ctx.send_message("HELLO WORLD")
- → Message queued
- ↓
-Runner drains messages → SingleEdgeRunner delivers to "reverse_text_executor"
- ↓
-Executor "reverse_text_executor" receives "HELLO WORLD"
- → Handler: reverse_text(text: str, ctx: WorkflowContext[Never, str])
- → Calls ctx.yield_output("DLROW OLLEH")
- → WorkflowOutputEvent emitted
- ↓
-No more messages → Workflow emits WorkflowStatusEvent(IDLE)
- ↓
-workflow.run() returns WorkflowRunResult([WorkflowOutputEvent("DLROW OLLEH"), ...])
-```
-
----
-
-## Additional references
-
-- Full workflow builder API: `WorkflowBuilder` in `_workflow.py`.
-- Edge runner implementations: `_edge_runner.py`.
-- Checkpoint encoding: `_runner_context.py` (`_encode_checkpoint_value`, `_decode_checkpoint_value`).
-- Magentic multi-agent orchestration: `_magentic.py`.
-
-This architecture balances **expressiveness** (flexible routing, composition), **type safety** (runtime contract enforcement), **observability** (OpenTelemetry spans, event streams), and **durability** (checkpointing for long-running workflows), making it suitable for both simple pipelines and complex multi-agent systems.
-
----
-
-## Demo: Fraud Detection Workflow
-
-### 🎯 Production-Ready Implementation
-
-A comprehensive fraud detection system showcasing enterprise workflow patterns:
-
-| Feature | Description |
-|---------|-------------|
-| **Architecture** | Fan-out/fan-in pattern with parallel specialist agents |
-| **Human-in-the-Loop** | Analyst review for high-risk cases with checkpointing |
-| **Real-Time UI** | React + FastAPI dashboard with WebSocket streaming |
-| **MCP Integration** | Filtered tool access for domain-specific analysis |
-| **Persistence** | Checkpoint storage for pause/resume workflows |
-
-**[→ Explore the Fraud Detection Demo](fraud_detection/)**
-
----
-
-## Quick Start
-
-**Try the Fraud Detection Demo:**
-
-```bash
-# Terminal 1: Start MCP Server
-cd mcp
-uv run mcp_service.py
-
-# Terminal 2: Start Backend
-cd agentic_ai/workflow/fraud_detection
-uv run --prerelease allow backend.py
-
-# Terminal 3: Start Frontend
-cd agentic_ai/workflow/fraud_detection/ui
-npm install && npm run dev
-
-# Open browser: http://localhost:3000
-```
-
----
-
-## Additional Resources
-
-- **[Human-in-the-Loop Guide](human-in-the-loop.md)** - Comprehensive HITL patterns
-- **[Agent Framework GitHub](https://github.com/microsoft/agent-framework)** - Official framework repository
-- **[API Documentation](https://github.com/microsoft/agent-framework/tree/main/docs)** - Detailed API reference
diff --git a/agentic_ai/workflow/fraud_detection/IMPLEMENTATION.md b/agentic_ai/workflow/fraud_detection/IMPLEMENTATION.md
index 249c67964..fe2ae2649 100644
--- a/agentic_ai/workflow/fraud_detection/IMPLEMENTATION.md
+++ b/agentic_ai/workflow/fraud_detection/IMPLEMENTATION.md
@@ -14,7 +14,7 @@ Successfully implemented the Contoso Fraud Detection & Escalation Workflow as sp
- BillingChargeExecutor
- ✅ FraudRiskAggregatorExecutor (LLM-based fan-in)
- ✅ Switch/case routing based on risk score
-- ✅ RequestInfoExecutor for human-in-the-loop
+- ✅ ReviewGatewayExecutor for human-in-the-loop (uses ctx.request_info API)
- ✅ AutoClearExecutor for low-risk cases
- ✅ FraudActionExecutor for action execution
- ✅ FinalNotificationExecutor for completion
@@ -52,14 +52,16 @@ builder.add_switch_case_edge(
#### Human-in-the-Loop
```python
-# Workflow pauses for analyst review
-analyst_review = RequestInfoExecutor(
- name="analyst_review",
- request_info={
- "type": "fraud_analyst_review",
- "instructions": "Review the risk assessment..."
- },
-)
+# ReviewGatewayExecutor uses ctx.request_info() to pause workflow
+class ReviewGatewayExecutor(Executor):
+ async def handle_assessment(self, ctx, assessment: FraudRiskAssessment):
+ request = AnalystReviewRequest(...)
+ await ctx.request_info(request, AnalystDecision)
+
+ @response_handler
+ async def handle_analyst_response(self, ctx, decision: AnalystDecision):
+ # Process the analyst decision when workflow resumes
+ await ctx.send_message(decision)
```
### 3. **MCP Tool Integration**
@@ -113,15 +115,20 @@ The `FraudRiskAggregatorExecutor` uses an LLM agent to:
```python
# Workflow state saved at each superstep
-checkpoint_storage = InMemoryCheckpointStorage()
-workflow = await create_fraud_detection_workflow(
+# Using UTF8FileCheckpointStorage wrapper for Windows compatibility
+checkpoint_storage = UTF8FileCheckpointStorage("./checkpoints")
+workflow = create_fraud_detection_workflow(
...,
checkpoint_storage=checkpoint_storage
)
-# Can pause/resume indefinitely
-checkpoint_id = await workflow.create_checkpoint()
-await workflow.restore_from_checkpoint(checkpoint_id)
+# Resume from checkpoint after analyst decision
+async for event in workflow.run_stream(checkpoint_id=checkpoint_id):
+ process_event(event)
+
+# Send responses and continue workflow
+async for event in workflow.send_responses_streaming(responses):
+ process_event(event)
```
## 📁 Files Created
@@ -224,7 +231,7 @@ Learned how to wait for multiple inputs before proceeding (aggregation).
Learned conditional routing based on message content and business logic.
### ✅ Human-in-the-Loop
-Learned workflow pause/resume with external input using `RequestInfoExecutor`.
+Learned workflow pause/resume with external input using `ctx.request_info()` and `@response_handler` decorator.
### ✅ Checkpointing
Learned persistent workflow state for long-running processes.
@@ -346,16 +353,18 @@ class FraudRiskAggregatorExecutor(Executor):
### Workflow Pause/Resume
```python
-# Workflow automatically pauses at RequestInfoExecutor
+# Workflow automatically pauses when ctx.request_info() is called
async for event in workflow.run_stream(alert):
- if hasattr(event, "request_info"):
- # Wait for analyst decision
- decision = await get_analyst_decision()
+ if isinstance(event, RequestInfoEvent):
+ # Workflow is now paused, waiting for analyst decision
+ request_id = event.request_id
+
+ # ... Get analyst decision from UI ...
- # Resume workflow
- await workflow.send_responses({decision})
- async for event in workflow.run_stream(alert):
- # Continues from where it paused
+ # Resume workflow with the response
+ responses = {request_id: analyst_decision}
+ async for event in workflow.send_responses_streaming(responses):
+ # @response_handler processes the decision
process_event(event)
```
diff --git a/agentic_ai/workflow/fraud_detection/QUICKSTART.md b/agentic_ai/workflow/fraud_detection/QUICKSTART.md
index bf998a6bb..213ab2e11 100644
--- a/agentic_ai/workflow/fraud_detection/QUICKSTART.md
+++ b/agentic_ai/workflow/fraud_detection/QUICKSTART.md
@@ -188,16 +188,16 @@ Additional checks:
### Change Analyst Instructions
-**Location**: `create_fraud_detection_workflow()`
+**Location**: `ReviewGatewayExecutor` class in `fraud_detection_workflow.py`
```python
-analyst_review = RequestInfoExecutor(
- name="analyst_review",
- request_info={
- "type": "fraud_analyst_review",
- "instructions": "YOUR CUSTOM INSTRUCTIONS HERE"
- },
-)
+# The AnalystReviewRequest dataclass contains the request details
+@dataclass
+class AnalystReviewRequest:
+ alert_id: str
+ customer_id: int
+ risk_assessment: FraudRiskAssessment
+ instructions: str = "Review the risk assessment and decide on action"
```
### Modify Action Logic
diff --git a/agentic_ai/workflow/fraud_detection/README.md b/agentic_ai/workflow/fraud_detection/README.md
index abadfb4d4..9fa788cb0 100644
--- a/agentic_ai/workflow/fraud_detection/README.md
+++ b/agentic_ai/workflow/fraud_detection/README.md
@@ -37,9 +37,9 @@ This example demonstrates a comprehensive fraud detection system using the Agent
(High Risk ≥0.6) (Low Risk <0.6)
↓ ↓
┌──────────────┐ ┌──────────────┐
- │ Analyst │ │ Auto Clear │
- │ Review │ │ Executor │
- │ (Human Input)│ └──────┬───────┘
+ │ Review │ │ Auto Clear │
+ │ Gateway │ │ Executor │
+ │(Human Input) │ └──────┬───────┘
└──────┬───────┘ │
↓ │
┌──────────────┐ │
@@ -80,10 +80,10 @@ This example demonstrates a comprehensive fraud detection system using the Agent
- **Low risk (<0.6)**: Auto-clear
### 5. **Human-in-the-Loop**
-- Uses `RequestInfoExecutor` for analyst review
-- Workflow pauses and creates checkpoint
+- Uses `ctx.request_info()` API for analyst review within `ReviewGatewayExecutor`
+- Workflow pauses and creates checkpoint automatically
- Analyst provides decision (lock account, refund charges, clear, both)
-- Workflow resumes with analyst's decision
+- `@response_handler` decorator processes the response when workflow resumes
### 6. **Checkpointing**
- Workflow state saved at each superstep
@@ -103,8 +103,8 @@ This example demonstrates a comprehensive fraud detection system using the Agent
2. **SuspiciousActivityAlert** → [UsagePattern, Location, Billing] (fan-out)
3. **[UsageAnalysisResult, LocationAnalysisResult, BillingAnalysisResult]** → Aggregator (fan-in)
4. **FraudRiskAssessment** → Switch (risk score check)
-5. **FraudRiskAssessment** → AnalystReview OR AutoClear
-6. **AnalystDecision** → FraudAction (if high risk)
+5. **FraudRiskAssessment** → ReviewGateway OR AutoClear
+6. **AnalystDecision** → FraudAction (if high risk, after human review)
7. **ActionResult** → FinalNotification
8. **FinalNotification** → Workflow output
@@ -260,7 +260,7 @@ instructions=(
1. **Fan-Out Pattern**: One message → multiple executors
2. **Fan-In Pattern**: Multiple messages → one executor (waits for all)
3. **Switch/Case Routing**: Conditional routing based on message content
-4. **Human-in-the-Loop**: Workflow pause/resume with external input
+4. **Human-in-the-Loop**: Workflow pause/resume with `ctx.request_info()` and `@response_handler`
5. **Checkpointing**: Persistent workflow state across restarts
6. **MCP Tool Integration**: Domain-specific tool filtering
7. **LLM Agent Executors**: AI-powered decision making
@@ -390,6 +390,17 @@ http://localhost:3000
- Check WebSocket connection in DevTools
- Verify events are being sent from backend
+**Checkpoint Encoding Error (Windows):**
+
+- If you see `'charmap' codec can't encode character` errors, the backend uses `UTF8FileCheckpointStorage` wrapper to handle Unicode characters in LLM output
+- Ensure you're using the latest backend.py which includes this fix
+
+**Analyst Decision Not Resuming Workflow:**
+
+- Check that the checkpoint was created successfully (no encoding errors in logs)
+- Verify the `request_id` matches between the decision and the pending request
+- Check backend logs for `continue_workflow` messages
+
## License
diff --git a/agentic_ai/workflow/fraud_detection/backend.py b/agentic_ai/workflow/fraud_detection/backend.py
index e15446a07..d7dd5497c 100644
--- a/agentic_ai/workflow/fraud_detection/backend.py
+++ b/agentic_ai/workflow/fraud_detection/backend.py
@@ -28,12 +28,113 @@
WorkflowOutputEvent,
WorkflowStatusEvent,
RequestInfoEvent,
- RequestInfoExecutor,
+ SuperStepCompletedEvent,
+ WorkflowCheckpoint,
+ get_checkpoint_summary,
)
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
import os
+import json
+from pathlib import Path
+from dataclasses import asdict
+
+
+# ============================================================================
+# UTF-8 Checkpoint Storage Wrapper (fixes Windows encoding issues)
+# ============================================================================
+
+class UTF8FileCheckpointStorage:
+ """
+ Wrapper around FileCheckpointStorage that ensures UTF-8 encoding.
+
+ This fixes the Windows 'charmap' codec error when LLM output contains
+ Unicode characters (like combining diacritical marks) that can't be
+ encoded with the default cp1252 encoding.
+ """
+
+ def __init__(self, storage_path: str | Path):
+ """Initialize the file storage with UTF-8 encoding."""
+ self.storage_path = Path(storage_path)
+ self.storage_path.mkdir(parents=True, exist_ok=True)
+ logger.info(f"Initialized UTF-8 file checkpoint storage at {self.storage_path}")
+
+ async def save_checkpoint(self, checkpoint: WorkflowCheckpoint) -> str:
+ """Save a checkpoint with UTF-8 encoding and return its ID."""
+ file_path = self.storage_path / f"{checkpoint.checkpoint_id}.json"
+ checkpoint_dict = asdict(checkpoint)
+
+ def _write_atomic() -> None:
+ tmp_path = file_path.with_suffix(".json.tmp")
+ with open(tmp_path, "w", encoding="utf-8") as f:
+ json.dump(checkpoint_dict, f, indent=2, ensure_ascii=False)
+ os.replace(tmp_path, file_path)
+
+ await asyncio.to_thread(_write_atomic)
+ logger.info(f"Saved checkpoint {checkpoint.checkpoint_id} to {file_path}")
+ return checkpoint.checkpoint_id
+
+ async def load_checkpoint(self, checkpoint_id: str) -> WorkflowCheckpoint | None:
+ """Load a checkpoint by ID with UTF-8 encoding."""
+ file_path = self.storage_path / f"{checkpoint_id}.json"
+
+ if not file_path.exists():
+ return None
+
+ def _read() -> dict[str, Any]:
+ with open(file_path, encoding="utf-8") as f:
+ return json.load(f)
+
+ checkpoint_dict = await asyncio.to_thread(_read)
+ checkpoint = WorkflowCheckpoint(**checkpoint_dict)
+ logger.info(f"Loaded checkpoint {checkpoint_id} from {file_path}")
+ return checkpoint
+
+ async def list_checkpoint_ids(self, workflow_id: str | None = None) -> list[str]:
+ """List checkpoint IDs with UTF-8 encoding."""
+ def _list_ids() -> list[str]:
+ checkpoint_ids: list[str] = []
+ for file_path in self.storage_path.glob("*.json"):
+ try:
+ with open(file_path, encoding="utf-8") as f:
+ data = json.load(f)
+ if workflow_id is None or data.get("workflow_id") == workflow_id:
+ checkpoint_ids.append(data.get("checkpoint_id", file_path.stem))
+ except Exception as e:
+ logger.warning(f"Failed to read checkpoint file {file_path}: {e}")
+ return checkpoint_ids
+
+ return await asyncio.to_thread(_list_ids)
+
+ async def list_checkpoints(self, workflow_id: str | None = None) -> list[WorkflowCheckpoint]:
+ """List checkpoint objects with UTF-8 encoding."""
+ def _list_checkpoints() -> list[WorkflowCheckpoint]:
+ checkpoints: list[WorkflowCheckpoint] = []
+ for file_path in self.storage_path.glob("*.json"):
+ try:
+ with open(file_path, encoding="utf-8") as f:
+ data = json.load(f)
+ if workflow_id is None or data.get("workflow_id") == workflow_id:
+ checkpoints.append(WorkflowCheckpoint.from_dict(data))
+ except Exception as e:
+ logger.warning(f"Failed to read checkpoint file {file_path}: {e}")
+ return checkpoints
+
+ return await asyncio.to_thread(_list_checkpoints)
+
+ async def delete_checkpoint(self, checkpoint_id: str) -> bool:
+ """Delete a checkpoint by ID."""
+ file_path = self.storage_path / f"{checkpoint_id}.json"
+
+ def _delete() -> bool:
+ if file_path.exists():
+ file_path.unlink()
+ logger.info(f"Deleted checkpoint {checkpoint_id} from {file_path}")
+ return True
+ return False
+
+ return await asyncio.to_thread(_delete)
# Load environment variables
load_dotenv()
@@ -130,7 +231,7 @@ class WorkflowStatus(BaseModel):
# Pre-initialized resources (created once on startup)
mcp_tool: MCPStreamableHTTPTool | None = None
chat_client: AzureOpenAIChatClient | None = None
-checkpoint_storage: FileCheckpointStorage | None = None
+checkpoint_storage: UTF8FileCheckpointStorage | None = None
# ============================================================================
@@ -155,14 +256,18 @@ def disconnect(self, websocket: WebSocket):
async def broadcast(self, message: dict):
"""Broadcast message to all connected clients."""
+ msg_type = message.get('type', message.get('event_type', 'unknown'))
+ logger.info(f"[BROADCAST] Sending message type={msg_type} to {len(self.active_connections)} connections")
+
if not self.active_connections:
- logger.warning(f"No active WebSocket connections to broadcast to. Message type: {message.get('type', 'unknown')}")
+ logger.warning(f"No active WebSocket connections to broadcast to. Message type: {msg_type}")
return
disconnected = []
for connection in self.active_connections:
try:
await connection.send_json(message)
+ logger.info(f"[BROADCAST] Successfully sent {msg_type}")
except Exception as e:
logger.error(f"Error sending to WebSocket: {e}")
disconnected.append(connection)
@@ -314,7 +419,9 @@ async def _lookup() -> tuple[str | None, int | None]:
for checkpoint in checkpoints_sorted:
try:
- pending_requests = RequestInfoExecutor.pending_requests_from_checkpoint(checkpoint)
+ # Use the new get_checkpoint_summary API instead of RequestInfoExecutor
+ summary = get_checkpoint_summary(checkpoint)
+ pending_events = summary.pending_request_info_events
except Exception as exc: # pragma: no cover - defensive logging
logger.debug(
"Unable to inspect checkpoint %s for alert %s: %s",
@@ -324,9 +431,9 @@ async def _lookup() -> tuple[str | None, int | None]:
)
continue
- for pending in pending_requests:
+ for pending in pending_events:
if pending.request_id == request_id:
- return checkpoint.checkpoint_id, pending.iteration
+ return checkpoint.checkpoint_id, checkpoint.iteration_count
return None, None
@@ -626,52 +733,78 @@ async def send_progress_updates():
# Small delay to ensure WebSocket connection is fully established
await asyncio.sleep(0.1)
+ # Track if we've seen a RequestInfoEvent (workflow is awaiting decision)
+ seen_request_info = False
+ request_info_event_data = None
+
# Run workflow and stream events
async for event in workflow.run_stream(alert):
+ logger.info(f"[DEBUG] Received event: {type(event).__name__}")
await process_event(alert_id, event)
# Check for human-in-the-loop request
if isinstance(event, RequestInfoEvent):
+ logger.info(f"[DEBUG] RequestInfoEvent detected! request_id={event.request_id}, source={event.source_executor_id}")
request_payload = _serialize_analyst_request(event)
- checkpoint_id, checkpoint_iteration = await _resolve_checkpoint_for_request(
- alert_id,
- event.request_id,
- )
+ logger.info(f"[DEBUG] Serialized request payload keys: {list(request_payload.keys())}")
timestamp = datetime.now().isoformat()
+ # Store the pending decision immediately - checkpoint will be resolved later
pending_decisions[alert_id] = {
**request_payload,
"timestamp": timestamp,
"source_executor_id": event.source_executor_id,
- "checkpoint_id": checkpoint_id,
- "checkpoint_iteration": checkpoint_iteration,
+ "checkpoint_id": None, # Will be resolved after superstep completes
+ "checkpoint_iteration": None,
}
pending_request_events[alert_id] = event
active_workflows[alert_id]["status"] = "awaiting_decision"
- active_workflows[alert_id]["pending_checkpoint_id"] = checkpoint_id
- if checkpoint_id:
- active_workflows[alert_id]["last_checkpoint_id"] = checkpoint_id
- else:
- logger.warning(
- "No checkpoint recorded for alert %s request %s; resume will be unavailable until one is created.",
- alert_id,
- event.request_id,
- )
+ # Broadcast decision_required immediately - don't wait for checkpoint
await manager.broadcast(
{
"type": "decision_required",
"alert_id": alert_id,
"request_id": event.request_id,
"data": request_payload,
- "checkpoint_id": checkpoint_id,
- "checkpoint_iteration": checkpoint_iteration,
+ "checkpoint_id": None, # Will be resolved after superstep completes
+ "checkpoint_iteration": None,
"timestamp": timestamp,
}
)
+ logger.info(f"[DEBUG] Broadcast decision_required for {alert_id}")
logger.info(f"Workflow {alert_id} awaiting analyst decision")
+
+ # Mark that we've seen the request - continue processing to get checkpoint
+ seen_request_info = True
+ request_info_event_data = event
+ # DON'T return here - continue to let the superstep complete and checkpoint be created
+
+ # After a superstep completes following a RequestInfoEvent, resolve the checkpoint
+ elif seen_request_info and isinstance(event, SuperStepCompletedEvent):
+ logger.info(f"[DEBUG] SuperStepCompleted after RequestInfoEvent - resolving checkpoint")
+ checkpoint_id, checkpoint_iteration = await _resolve_checkpoint_for_request(
+ alert_id,
+ request_info_event_data.request_id,
+ )
+ logger.info(f"[DEBUG] Resolved checkpoint: id={checkpoint_id}, iteration={checkpoint_iteration}")
+
+ # Update stored values with resolved checkpoint
+ if checkpoint_id:
+ pending_decisions[alert_id]["checkpoint_id"] = checkpoint_id
+ pending_decisions[alert_id]["checkpoint_iteration"] = checkpoint_iteration
+ active_workflows[alert_id]["pending_checkpoint_id"] = checkpoint_id
+ active_workflows[alert_id]["last_checkpoint_id"] = checkpoint_id
+ else:
+ logger.warning(
+ "No checkpoint recorded for alert %s request %s; resume will be unavailable until one is created.",
+ alert_id,
+ request_info_event_data.request_id,
+ )
+
+ # Now we can exit - workflow is paused awaiting decision
return
# If we exit the loop naturally, mark as completed
@@ -759,44 +892,49 @@ async def continue_workflow(alert_id: str, responses: dict[str, Any], checkpoint
workflow_state.setdefault("workflow_id", workflow.id)
workflow_state["status"] = "running"
- # Debug: Check what's in the checkpoint
+ # Debug: Check what's in the checkpoint using get_checkpoint_summary
checkpoint = await checkpoint_storage.load_checkpoint(effective_checkpoint_id)
if checkpoint:
- logger.info(f"Checkpoint has {len(checkpoint.executor_states)} executor states")
- analyst_state = checkpoint.executor_states.get("analyst_review", {})
- logger.info(f"analyst_review state keys: {list(analyst_state.keys()) if isinstance(analyst_state, dict) else 'not a dict'}")
- if isinstance(analyst_state, dict):
- shared_state_key = RequestInfoExecutor._PENDING_SHARED_STATE_KEY
- pending_requests = checkpoint.shared_state.get(shared_state_key, {})
- logger.info(f"Pending requests in shared state: {list(pending_requests.keys())}")
+ summary = get_checkpoint_summary(checkpoint)
+ logger.info(f"Checkpoint status: {summary.status}")
+ logger.info(f"Pending request events: {len(summary.pending_request_info_events)}")
+ for pending in summary.pending_request_info_events:
+ logger.info(f" - Request ID: {pending.request_id}, Source: {pending.source_executor_id}")
- logger.info(f"Starting run_stream_from_checkpoint with responses keys: {list(responses.keys())}")
+ logger.info(f"Starting workflow resume with responses keys: {list(responses.keys())}")
logger.info(f"Checkpoint ID: {effective_checkpoint_id}")
- # Immediately broadcast analyst_review completion since the framework may not emit it
- # The RequestInfoExecutor was waiting, and when we provide the response, it completes
+ # Immediately broadcast review_gateway completion since the framework may not emit it
+ # The executor using ctx.request_info was waiting, and when we provide the response, it completes
# but this completion event is not always emitted in the event stream during resume
await manager.broadcast({
"alert_id": alert_id,
"type": "ExecutorCompletedEvent",
"event_type": "executor_completed",
- "executor_id": "analyst_review",
+ "executor_id": "review_gateway",
"timestamp": datetime.now().isoformat(),
})
- logger.info("Broadcast analyst_review completion event")
+ logger.info("Broadcast review_gateway completion event")
# Track request info executors that completed during resume
completed_request_executors = set()
- async for event in workflow.run_stream_from_checkpoint(
- effective_checkpoint_id,
+ # First, restore from checkpoint
+ logger.info(f"Restoring workflow from checkpoint {effective_checkpoint_id}")
+ async for event in workflow.run_stream(
+ checkpoint_id=effective_checkpoint_id,
checkpoint_storage=checkpoint_storage,
- responses=responses,
):
+ logger.info(f"Restore event received: {type(event).__name__}")
+ await process_event(alert_id, event)
+
+ # Now send the responses to continue the workflow
+ logger.info(f"Sending responses to continue workflow: {list(responses.keys())}")
+ async for event in workflow.send_responses_streaming(responses):
logger.info(f"Event received: {type(event).__name__}")
- # Track when analyst_review completes so we can re-broadcast at the end
- if isinstance(event, ExecutorCompletedEvent) and event.executor_id == "analyst_review":
+ # Track when review_gateway completes so we can re-broadcast at the end
+ if isinstance(event, ExecutorCompletedEvent) and event.executor_id == "review_gateway":
completed_request_executors.add(event.executor_id)
await process_event(alert_id, event)
@@ -979,11 +1117,11 @@ async def startup_event():
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential(), deployment_name=os.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT"))
logger.info("✓ Azure OpenAI client initialized")
- # Initialize checkpoint storage
+ # Initialize checkpoint storage with UTF-8 encoding wrapper
import pathlib
checkpoint_dir = pathlib.Path("./checkpoints")
checkpoint_dir.mkdir(parents=True, exist_ok=True)
- checkpoint_storage = FileCheckpointStorage(str(checkpoint_dir))
+ checkpoint_storage = UTF8FileCheckpointStorage(str(checkpoint_dir))
logger.info(f"✓ Checkpoint storage initialized at {checkpoint_dir.absolute()}")
logger.info("Backend ready! 🚀")
diff --git a/agentic_ai/workflow/fraud_detection/fraud_detection_workflow.py b/agentic_ai/workflow/fraud_detection/fraud_detection_workflow.py
index 8b6b8b0b1..da36abf1e 100644
--- a/agentic_ai/workflow/fraud_detection/fraud_detection_workflow.py
+++ b/agentic_ai/workflow/fraud_detection/fraud_detection_workflow.py
@@ -16,12 +16,13 @@
3. Fan-In to FraudRiskAggregatorExecutor (LLM-based agent):
- Produces FraudRiskScore and recommended action (lock account, refund charges, ignore)
4. SwitchCaseEdgeRunner:
- - If risk score ≥ threshold → route to RequestInfoExecutor for human fraud analyst review
+ - If risk score ≥ threshold → route to ReviewGatewayExecutor for human fraud analyst review
- Else → route to AutoClearExecutor
-5. RequestInfoExecutor → sends "Fraud Case Review Request" to analyst with full context
-6. Workflow pauses — checkpoint saved
+5. ReviewGatewayExecutor → calls ctx.request_info() to request analyst decision, workflow pauses
+6. Checkpoint saved — workflow awaits analyst response
7. Analyst decides (approve lock/refund or clear)
8. Workflow resumes:
+ - ReviewGatewayExecutor's @response_handler processes analyst decision
- FraudActionExecutor → performs chosen action (e.g., lock account, reverse charges)
9. FinalNotificationExecutor → informs customer and logs audit trail
@@ -30,7 +31,7 @@
- Fan-out pattern to multiple specialist agents with MCP tools
- Fan-in aggregation to produce single risk assessment
- LLM-based risk scoring
-- Human-in-the-loop for high-risk cases
+- Human-in-the-loop via ctx.request_info() and @response_handler
- Checkpointing for workflow pause/resume
"""
@@ -54,14 +55,12 @@
FileCheckpointStorage,
MCPStreamableHTTPTool,
RequestInfoEvent,
- RequestInfoExecutor,
- RequestInfoMessage,
- RequestResponse,
WorkflowBuilder,
WorkflowContext,
WorkflowOutputEvent,
WorkflowStatusEvent,
handler,
+ response_handler,
)
from agent_framework.azure import AzureOpenAIChatClient
from pydantic import BaseModel, Field
@@ -141,8 +140,8 @@ class FraudRiskAssessment:
@dataclass
-class AnalystReviewRequest(RequestInfoMessage):
- """Request for analyst review sent to RequestInfoExecutor."""
+class AnalystReviewRequest:
+ """Request for analyst review - used with ctx.request_info()."""
assessment: FraudRiskAssessment | None = None
prompt: str = ""
@@ -678,44 +677,64 @@ async def handle_analysis_results(
class ReviewGatewayExecutor(Executor):
- """Gateway that routes high-risk assessments to RequestInfoExecutor for human review."""
+ """
+ Gateway that handles high-risk assessments with human-in-the-loop review.
+
+ Uses the new request_info API:
+ 1. Receives high-risk assessment
+ 2. Calls ctx.request_info() to pause workflow and request analyst input
+ 3. @response_handler receives the analyst's decision
+ 4. Forwards decision to FraudActionExecutor
+ """
- def __init__(self, analyst_review_id: str, fraud_action_id: str, id: str = "review_gateway") -> None:
+ def __init__(self, fraud_action_id: str, id: str = "review_gateway") -> None:
super().__init__(id=id)
- self._analyst_review_id = analyst_review_id
self._fraud_action_id = fraud_action_id
+ # Store the assessment for use in the response handler
+ self._pending_assessment: FraudRiskAssessment | None = None
@handler
async def handle_assessment(
- self, assessment: FraudRiskAssessment, ctx: WorkflowContext[AnalystReviewRequest]
+ self, assessment: FraudRiskAssessment, ctx: WorkflowContext[AnalystDecision]
) -> None:
logger.info(f"[ReviewGateway] Routing high-risk assessment {assessment.alert_id} to analyst")
+ # Store assessment for response handler
+ self._pending_assessment = assessment
+
# Create analyst review request
request = AnalystReviewRequest(
assessment=assessment,
prompt=f"Review fraud case for alert {assessment.alert_id}. Risk score: {assessment.overall_risk_score:.2f}. Recommended action: {assessment.recommended_action}",
)
- # Send to RequestInfoExecutor
- await ctx.send_message(request, target_id=self._analyst_review_id)
+ # Request info from external analyst - workflow will pause here
+ # The response will be handled by the @response_handler below
+ await ctx.request_info(request, AnalystDecision)
+ logger.info(f"[ReviewGateway] Waiting for analyst decision on {assessment.alert_id}")
- @handler
+ @response_handler
async def handle_analyst_response(
- self, response: RequestResponse[AnalystReviewRequest, AnalystDecision], ctx: WorkflowContext[AnalystDecision]
+ self,
+ original_request: AnalystReviewRequest,
+ response: AnalystDecision,
+ ctx: WorkflowContext[AnalystDecision],
) -> None:
- logger.info(f"[ReviewGateway] Received analyst decision")
-
- assessment = response.original_request.assessment if response.original_request else None
- decision = response.data
+ """Handle the analyst's decision and forward to fraud action executor."""
+ logger.info(f"[ReviewGateway] Received analyst decision: {response.approved_action}")
- if assessment and getattr(decision, "customer_id", None) in (None, 0):
- # Now using dataclasses, use replace() to update fields
+ # Ensure decision has customer_id from original assessment
+ if original_request.assessment and getattr(response, "customer_id", None) in (None, 0):
from dataclasses import replace
- decision = replace(decision, customer_id=assessment.customer_id)
+ response = replace(
+ response,
+ customer_id=original_request.assessment.customer_id,
+ alert_id=original_request.assessment.alert_id,
+ )
# Forward the analyst decision to fraud action executor
- await ctx.send_message(decision, target_id=self._fraud_action_id)
+ await ctx.send_message(response, target_id=self._fraud_action_id)
+ logger.info(f"[ReviewGateway] Forwarded decision to fraud action executor")
class AutoClearExecutor(Executor):
@@ -822,7 +841,7 @@ async def create_fraud_detection_workflow(
"""
Build the fraud detection workflow.
- Topology:
+ Topology (updated for new request_info API):
AlertRouter → [UsagePattern, Location, Billing] → FraudRiskAggregator
↓
(Switch based on risk score)
@@ -830,10 +849,14 @@ async def create_fraud_detection_workflow(
┌───────────────────────┴──────────────────────┐
↓ ↓
(High Risk) (Low Risk)
- RequestInfoExecutor AutoClearExecutor
- ↓ ↓
- (Analyst Decision) ↓
+ ReviewGateway AutoClearExecutor
+ (uses ctx.request_info ↓
+ for human-in-the-loop) ↓
+ ↓ ↓
FraudActionExecutor ────────────────────→ FinalNotificationExecutor
+
+ Note: ReviewGateway now uses ctx.request_info() and @response_handler
+ instead of the deprecated RequestInfoExecutor.
"""
# Create executors
@@ -846,10 +869,8 @@ async def create_fraud_detection_workflow(
fraud_action = FraudActionExecutor()
final_notification = FinalNotificationExecutor()
- # Create human-in-the-loop executors
- analyst_review = RequestInfoExecutor(id="analyst_review")
+ # Create human-in-the-loop executor (now uses ctx.request_info internally)
review_gateway = ReviewGatewayExecutor(
- analyst_review_id=analyst_review.id,
fraud_action_id=fraud_action.id,
)
@@ -862,23 +883,21 @@ async def create_fraud_detection_workflow(
# Fan-in edge: 3 analysts → Aggregator (waits for all 3)
builder.add_fan_in_edges([usage_executor, location_executor, billing_executor], aggregator)
- # # Switch/case edges: Aggregator → High risk OR Low risk
+ # Switch/case edges: Aggregator → High risk OR Low risk
builder.add_switch_case_edge_group(
aggregator,
[
- # High risk → Review Gateway → Analyst review
+ # High risk → Review Gateway (will request human input via ctx.request_info)
Case(condition=lambda assessment: assessment.overall_risk_score >= 0.6, target=review_gateway),
# Low risk → Auto clear
Default(target=auto_clear),
],
)
- # # Review gateway routes to analyst review and back, then to fraud action
- builder.add_edge(review_gateway, analyst_review)
- builder.add_edge(analyst_review, review_gateway)
+ # Review gateway → Fraud action (after analyst decision via @response_handler)
builder.add_edge(review_gateway, fraud_action)
- # # Both paths → Final notification
+ # Both paths → Final notification
builder.add_edge(auto_clear, final_notification)
builder.add_edge(fraud_action, final_notification)
diff --git a/agentic_ai/workflow/fraud_detection/pyproject.toml b/agentic_ai/workflow/fraud_detection/pyproject.toml
index 5c297b7c7..4dcefddb8 100644
--- a/agentic_ai/workflow/fraud_detection/pyproject.toml
+++ b/agentic_ai/workflow/fraud_detection/pyproject.toml
@@ -6,7 +6,7 @@ readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"fastapi==0.115.12",
- "agent-framework==1.0.0b251007",
+ "agent-framework==1.0.0b260130",
"fastmcp==2.7.1",
"flasgger==0.9.7.1",
"flask==3.0.3",
@@ -15,7 +15,6 @@ dependencies = [
"openai>=1.90.0,<1.110.0",
"pydantic==2.11.4",
"requests==2.32.4",
- "streamlit==1.45.0",
"tenacity==8.5.0",
"uvicorn>=0.25.0",
"websockets>=15.0.1",
diff --git a/agentic_ai/workflow/fraud_detection/scenario.md b/agentic_ai/workflow/fraud_detection/scenario.md
index c7182eb79..70abc4f6b 100644
--- a/agentic_ai/workflow/fraud_detection/scenario.md
+++ b/agentic_ai/workflow/fraud_detection/scenario.md
@@ -13,10 +13,10 @@ Contoso’s automated systems flag suspicious account activity, but certain case
3. **Fan-In** to `FraudRiskAggregatorExecutor`:
- Produces `FraudRiskScore` and recommended action (lock account, refund charges, ignore).
4. **SwitchCaseEdgeRunner**:
- - If risk score ≥ threshold → route to `RequestInfoExecutor` for human fraud analyst review.
+ - If risk score ≥ threshold → route to `ReviewGatewayExecutor` for human fraud analyst review.
- Else → route to `AutoClearExecutor`.
-5. **RequestInfoExecutor** → sends “Fraud Case Review Request” to analyst with full context.
-6. **Workflow pauses** — checkpoint saved.
+5. **ReviewGatewayExecutor** → uses `ctx.request_info()` to request analyst review with full context.
+6. **Workflow pauses** — checkpoint saved automatically.
7. **Analyst decides** (approve lock/refund or clear).
8. Workflow resumes:
- `FraudActionExecutor` → performs chosen action (e.g., lock account, reverse charges).
diff --git a/agentic_ai/workflow/fraud_detection/ui/src/App.jsx b/agentic_ai/workflow/fraud_detection/ui/src/App.jsx
index 9cf0a9b66..67f692064 100644
--- a/agentic_ai/workflow/fraud_detection/ui/src/App.jsx
+++ b/agentic_ai/workflow/fraud_detection/ui/src/App.jsx
@@ -1,11 +1,13 @@
-import { useState, useCallback, useEffect } from 'react';
+import React, { useState, useCallback, useEffect } from 'react';
import {
Box,
ThemeProvider,
+ createTheme,
CssBaseline,
AppBar,
Toolbar,
Typography,
+ Container,
Paper,
Grid,
} from '@mui/material';
@@ -15,18 +17,32 @@ import ControlPanel from './components/ControlPanel';
import AnalystDecisionPanel from './components/AnalystDecisionPanel';
import EventLog from './components/EventLog';
import { useWebSocket } from './hooks/useWebSocket';
-import { fetchAlerts, startWorkflow, submitDecision } from './utils/api';
-import { isDuplicateEvent } from './utils/helpers';
-import { EVENT_TYPES } from './constants/workflow';
-import { API_CONFIG, APP_CONFIG } from './constants/config';
-import theme from './theme';
-
-/**
- * Main application component for the Fraud Detection Workflow Visualizer
- * Manages the state and orchestration of workflow visualization, controls, and event logging
- */
+
+const theme = createTheme({
+ palette: {
+ mode: 'light',
+ primary: {
+ main: '#1976d2',
+ },
+ secondary: {
+ main: '#dc004e',
+ },
+ success: {
+ main: '#4caf50',
+ },
+ warning: {
+ main: '#ff9800',
+ },
+ error: {
+ main: '#f44336',
+ },
+ },
+ typography: {
+ fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
+ },
+});
+
function App() {
- // State management
const [alerts, setAlerts] = useState([]);
const [selectedAlert, setSelectedAlert] = useState(null);
const [workflowRunning, setWorkflowRunning] = useState(false);
@@ -34,57 +50,50 @@ function App() {
const [pendingDecision, setPendingDecision] = useState(null);
const [executorStates, setExecutorStates] = useState({});
- // WebSocket connection for real-time updates
- const { lastMessage, sendMessage } = useWebSocket(API_CONFIG.WS_URL);
+ // WebSocket hook for real-time updates
+ const { lastMessage, sendMessage } = useWebSocket('ws://localhost:8001/ws');
- /**
- * Load sample alerts on component mount
- */
+ // Load sample alerts on mount
useEffect(() => {
- const loadAlerts = async () => {
- try {
- const alertsData = await fetchAlerts();
- setAlerts(alertsData);
- } catch (error) {
- console.error('Failed to load alerts:', error);
- }
- };
-
- loadAlerts();
+ fetch('/api/alerts')
+ .then((res) => res.json())
+ .then((data) => setAlerts(data.alerts))
+ .catch((err) => console.error('Error loading alerts:', err));
}, []);
- /**
- * Handle incoming WebSocket messages
- * Process different event types and update application state accordingly
- */
+ // Handle WebSocket messages
useEffect(() => {
if (!lastMessage) return;
try {
const event = lastMessage;
- // Add to event log - prevent duplicates
+ // Add to event log - prevent duplicates by checking timestamp + type + executor_id
setEvents((prev) => {
- return isDuplicateEvent(event, prev) ? prev : [...prev, event];
+ const eventKey = `${event.timestamp}-${event.type || event.event_type}-${event.executor_id || ''}`;
+ const isDuplicate = prev.some(
+ (e) => `${e.timestamp}-${e.type || e.event_type}-${e.executor_id || ''}` === eventKey
+ );
+ return isDuplicate ? prev : [...prev, event];
});
// Handle workflow initialization
- if (event.type === EVENT_TYPES.WORKFLOW_INITIALIZING) {
+ if (event.type === 'workflow_initializing') {
// Keep workflow running flag true, just show initialization message
}
// Handle workflow started
- if (event.type === EVENT_TYPES.WORKFLOW_STARTED) {
+ if (event.type === 'workflow_started') {
// Workflow is now running
}
// Update executor states based on event type
- if (event.event_type === EVENT_TYPES.EXECUTOR_INVOKED) {
+ if (event.event_type === 'executor_invoked') {
setExecutorStates((prev) => ({
...prev,
[event.executor_id]: 'running',
}));
- } else if (event.event_type === EVENT_TYPES.EXECUTOR_COMPLETED) {
+ } else if (event.event_type === 'executor_completed') {
setExecutorStates((prev) => ({
...prev,
[event.executor_id]: 'completed',
@@ -92,13 +101,13 @@ function App() {
}
// Handle decision required
- if (event.type === EVENT_TYPES.DECISION_REQUIRED) {
+ if (event.type === 'decision_required') {
setPendingDecision(event);
setWorkflowRunning(false);
}
// Handle workflow completion
- if (event.type === EVENT_TYPES.WORKFLOW_COMPLETED || event.type === EVENT_TYPES.WORKFLOW_ERROR) {
+ if (event.type === 'workflow_completed' || event.type === 'workflow_error') {
setWorkflowRunning(false);
// Keep all executor states as-is (they should already be 'completed')
}
@@ -107,10 +116,6 @@ function App() {
}
}, [lastMessage]);
- /**
- * Start a workflow for the selected alert
- * @param {Object} alert - The alert object to process
- */
const handleStartWorkflow = useCallback(async (alert) => {
console.log('Starting workflow for alert:', alert);
setSelectedAlert(alert);
@@ -120,7 +125,15 @@ function App() {
setPendingDecision(null);
try {
- const data = await startWorkflow(alert);
+ const response = await fetch('/api/workflow/start', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(alert),
+ });
+
+ const data = await response.json();
console.log('Workflow started:', data);
} catch (error) {
console.error('Error starting workflow:', error);
@@ -128,15 +141,19 @@ function App() {
}
}, []);
- /**
- * Submit analyst decision and resume workflow
- * @param {Object} decision - The decision object containing analyst's input
- */
const handleSubmitDecision = useCallback(async (decision) => {
console.log('Submitting decision:', decision);
try {
- const data = await submitDecision(decision);
+ const response = await fetch('/api/workflow/decision', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(decision),
+ });
+
+ const data = await response.json();
console.log('Decision submitted:', data);
setPendingDecision(null);
@@ -149,13 +166,13 @@ function App() {
return (
-
+
{/* App Bar */}
- {APP_CONFIG.TITLE}
+ Fraud Detection Workflow Visualizer
Real-time Multi-Agent Workflow Monitoring
@@ -164,29 +181,27 @@ function App() {
{/* Main Content */}
-
+
{/* Left Column - Controls and Decision Panel */}
-
-
-
+
+
+ {pendingDecision && (
+
-
- {pendingDecision && (
-
- )}
-
+ )}
{/* Center Column - Workflow Visualization */}
-
+
Workflow Graph
@@ -203,14 +218,14 @@ function App() {
{/* Right Column - Event Log */}
-
+
-
+
);
}
-export default App;
+export default App;
\ No newline at end of file
diff --git a/agentic_ai/workflow/fraud_detection/ui/src/components/AnalystDecisionPanel.jsx b/agentic_ai/workflow/fraud_detection/ui/src/components/AnalystDecisionPanel.jsx
index c633fd731..d2cea18f6 100644
--- a/agentic_ai/workflow/fraud_detection/ui/src/components/AnalystDecisionPanel.jsx
+++ b/agentic_ai/workflow/fraud_detection/ui/src/components/AnalystDecisionPanel.jsx
@@ -45,13 +45,15 @@ function AnalystDecisionPanel({ decision, onSubmit }) {
AI Analysis
-
+
- {decision.data.reasoning}
+ {decision.data.reasoning.length > 500
+ ? decision.data.reasoning.substring(0, 500) + '...'
+ : decision.data.reasoning}
diff --git a/agentic_ai/workflow/fraud_detection/ui/src/components/WorkflowVisualizer.jsx b/agentic_ai/workflow/fraud_detection/ui/src/components/WorkflowVisualizer.jsx
index c7b361301..1852c058f 100644
--- a/agentic_ai/workflow/fraud_detection/ui/src/components/WorkflowVisualizer.jsx
+++ b/agentic_ai/workflow/fraud_detection/ui/src/components/WorkflowVisualizer.jsx
@@ -51,7 +51,7 @@ const initialNodes = [
id: 'review_gateway',
type: 'custom',
position: { x: 550, y: 500 },
- data: { label: 'Review Gateway', status: 'idle', description: 'Routes to analyst review' },
+ data: { label: 'Review Gateway', status: 'idle', description: 'Human analyst review (pauses workflow)' },
},
{
id: 'auto_clear_executor',
@@ -59,22 +59,16 @@ const initialNodes = [
position: { x: 250, y: 500 },
data: { label: 'Auto Clear', status: 'idle', description: 'Auto-clears low risk' },
},
- {
- id: 'analyst_review',
- type: 'custom',
- position: { x: 550, y: 650 },
- data: { label: 'Analyst Review', status: 'idle', description: 'Human review required' },
- },
{
id: 'fraud_action_executor',
type: 'custom',
- position: { x: 550, y: 800 },
+ position: { x: 550, y: 650 },
data: { label: 'Fraud Action', status: 'idle', description: 'Execute fraud action' },
},
{
id: 'final_notification_executor',
type: 'custom',
- position: { x: 400, y: 950 },
+ position: { x: 400, y: 800 },
data: { label: 'Final Notification', status: 'idle', description: 'Send notifications' },
},
];
@@ -94,10 +88,8 @@ const initialEdges = [
{ id: 'e3-1', source: 'fraud_risk_aggregator', target: 'review_gateway', label: 'High Risk', style: { stroke: '#f44336' } },
{ id: 'e3-2', source: 'fraud_risk_aggregator', target: 'auto_clear_executor', label: 'Low Risk', style: { stroke: '#4caf50' } },
- // Review loop
- { id: 'e4-1', source: 'review_gateway', target: 'analyst_review' },
- { id: 'e4-2', source: 'analyst_review', target: 'review_gateway', animated: true, style: { stroke: '#ff9800' } },
- { id: 'e4-3', source: 'review_gateway', target: 'fraud_action_executor' },
+ // Review gateway to fraud action (human review happens via request_info, then proceeds)
+ { id: 'e4-1', source: 'review_gateway', target: 'fraud_action_executor', animated: true, style: { stroke: '#ff9800' } },
// Final paths
{ id: 'e5-1', source: 'auto_clear_executor', target: 'final_notification_executor' },
diff --git a/agentic_ai/workflow/fraud_detection/ui/src/hooks/useWebSocket.js b/agentic_ai/workflow/fraud_detection/ui/src/hooks/useWebSocket.js
index 531588e32..dd9a4f13d 100644
--- a/agentic_ai/workflow/fraud_detection/ui/src/hooks/useWebSocket.js
+++ b/agentic_ai/workflow/fraud_detection/ui/src/hooks/useWebSocket.js
@@ -3,6 +3,7 @@ import { WS_CONFIG } from '../constants/config';
/**
* Custom hook for managing WebSocket connections with automatic reconnection
+ * Uses a message queue to ensure no messages are lost when they arrive quickly
* @param {string} url - WebSocket URL to connect to
* @returns {Object} Object containing lastMessage, readyState, and sendMessage function
*/
@@ -12,6 +13,29 @@ export function useWebSocket(url) {
const ws = useRef(null);
const reconnectTimeout = useRef(null);
const reconnectAttempts = useRef(0);
+
+ // Message queue to prevent lost messages during rapid updates
+ const messageQueue = useRef([]);
+ const processingQueue = useRef(false);
+
+ // Process messages from queue one at a time
+ const processQueue = useCallback(() => {
+ if (processingQueue.current || messageQueue.current.length === 0) {
+ return;
+ }
+
+ processingQueue.current = true;
+ const message = messageQueue.current.shift();
+
+ // Use setTimeout to ensure React processes each state update
+ setLastMessage(message);
+
+ // Schedule next message processing
+ setTimeout(() => {
+ processingQueue.current = false;
+ processQueue();
+ }, 0);
+ }, []);
const connect = useCallback(() => {
try {
@@ -26,7 +50,9 @@ export function useWebSocket(url) {
ws.current.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
- setLastMessage(data);
+ // Add to queue instead of directly setting state
+ messageQueue.current.push(data);
+ processQueue();
} catch (error) {
console.error('Error parsing WebSocket message:', error);
}
@@ -56,7 +82,7 @@ export function useWebSocket(url) {
} catch (error) {
console.error('Error creating WebSocket:', error);
}
- }, [url]);
+ }, [url, processQueue]);
useEffect(() => {
connect();
diff --git a/agentic_ai/workflow/fraud_detection/uv.lock b/agentic_ai/workflow/fraud_detection/uv.lock
index 306cb8415..0576fe255 100644
--- a/agentic_ai/workflow/fraud_detection/uv.lock
+++ b/agentic_ai/workflow/fraud_detection/uv.lock
@@ -25,22 +25,28 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/73/23/bf86f1d3a04a6d967176e20939a98207f8a8ed49480622ad6cf5ce232083/a2a_sdk-0.3.8-py3-none-any.whl", hash = "sha256:21254dd47d89a958b9d15576a69fe3d44aaef558858d148e187d1f1e26b320e7", size = 138098 },
]
+[[package]]
+name = "ag-ui-protocol"
+version = "0.1.10"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/67/bb/5a5ec893eea5805fb9a3db76a9888c3429710dfb6f24bbb37568f2cf7320/ag_ui_protocol-0.1.10.tar.gz", hash = "sha256:3213991c6b2eb24bb1a8c362ee270c16705a07a4c5962267a083d0959ed894f4", size = 6945 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8f/78/eb55fabaab41abc53f52c0918a9a8c0f747807e5306273f51120fd695957/ag_ui_protocol-0.1.10-py3-none-any.whl", hash = "sha256:c81e6981f30aabdf97a7ee312bfd4df0cd38e718d9fc10019c7d438128b93ab5", size = 7889 },
+]
+
[[package]]
name = "agent-framework"
-version = "1.0.0b251007"
+version = "1.0.0b260130"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "agent-framework-a2a" },
- { name = "agent-framework-azure-ai" },
- { name = "agent-framework-copilotstudio" },
- { name = "agent-framework-core" },
- { name = "agent-framework-devui" },
- { name = "agent-framework-mem0" },
- { name = "agent-framework-redis" },
+ { name = "agent-framework-core", extra = ["all"] },
]
-sdist = { url = "https://files.pythonhosted.org/packages/6b/a6/d21b9666b738b7398ab1b58c31fcd17f0b4da04e822f261b097058451ee5/agent_framework-1.0.0b251007.tar.gz", hash = "sha256:c17e1286471d22f60304a36a03e38f87bf7a720dee6fce5c35bc738eb5227abc", size = 1689664 }
+sdist = { url = "https://files.pythonhosted.org/packages/93/10/ba51bf04ea2900897a221664e4e673dcc7a7a58a6658eeb85115e920d9b4/agent_framework-1.0.0b260130.tar.gz", hash = "sha256:50e13b74366b8092cb81769f07b3b42d6ddc8888a51244933c3214df591b7108", size = 3506765 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e6/aa/7c4b12e59040d2cb7c4d19d34ceb0de2a126c9850d954d08cc02acb760d4/agent_framework-1.0.0b251007-py3-none-any.whl", hash = "sha256:c582b2f7d1659cc5c543c6a6e90cfd05c87547b0dadbce38cd46aa75f883474f", size = 5551 },
+ { url = "https://files.pythonhosted.org/packages/bb/3d/2a8efa9085c7fec503a64038f986faf0cdf7f5de853c4ae30724e2e2bda6/agent_framework-1.0.0b260130-py3-none-any.whl", hash = "sha256:b9ba1487f91ab22031e01b5c09e5649181fd717f807d94f22ec43a409c43cde1", size = 5552 },
]
[[package]]
@@ -56,6 +62,34 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/af/87/09806e5b4a3c95ba7aca5be9bda853b518a174754530343f909f53beca76/agent_framework_a2a-1.0.0b251007-py3-none-any.whl", hash = "sha256:b26b9a43056783dd8b11926d4dd005a008e3a52ed885d9e9303f862542f019d1", size = 6766 },
]
+[[package]]
+name = "agent-framework-ag-ui"
+version = "1.0.0b260130"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "ag-ui-protocol" },
+ { name = "agent-framework-core" },
+ { name = "fastapi" },
+ { name = "uvicorn" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/69/2f/ae316dec3d27b484d5e11dd6469d5dee660416b38d51179a8712d987617c/agent_framework_ag_ui-1.0.0b260130.tar.gz", hash = "sha256:0ebf489fe43050b6e63f3188be13449389735e3e82905c3479c9fba8e73568a9", size = 93211 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/5e/ccfe94370e26928bea78e5fd89efbe4254e2263e1718b49f38a24108abf9/agent_framework_ag_ui-1.0.0b260130-py3-none-any.whl", hash = "sha256:68cab476436a6bf7d3b1ac6341b3debdf45cd516e311522b8b84ebfafa0f4be5", size = 67897 },
+]
+
+[[package]]
+name = "agent-framework-anthropic"
+version = "1.0.0b260130"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "agent-framework-core" },
+ { name = "anthropic" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8c/50/523d45d86768301cfd853f0c54d5dcbc1df81b50d9e6a89a8acfa2f533f7/agent_framework_anthropic-1.0.0b260130.tar.gz", hash = "sha256:d8ac99cdc9e82f91e8a8f749965523b49e2341c51ce8dc1dd9a1c7c2df567a4e", size = 12252 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e7/4a/da59aa7ca5b3441ab74807ed3861ec4f6ae9f301421c50944859e111a169/agent_framework_anthropic-1.0.0b260130-py3-none-any.whl", hash = "sha256:7272dd56a09c6d3e33652c5031b01224d5333fee176f2129382fcc6729714261", size = 12316 },
+]
+
[[package]]
name = "agent-framework-azure-ai"
version = "1.0.0b251007"
@@ -71,6 +105,47 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/11/2ea07e06c695371cdc2fdc8f4bf9117f1b4e19f3a128d4da07e522afc8ed/agent_framework_azure_ai-1.0.0b251007-py3-none-any.whl", hash = "sha256:ce15f01ad04d534e42f6a678472d44a3814406e24d96b2770f857170019e0cc4", size = 12810 },
]
+[[package]]
+name = "agent-framework-azure-ai-search"
+version = "1.0.0b260130"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "agent-framework-core" },
+ { name = "azure-search-documents" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/64/63/81c7853aa526f3c3667871cea14667af73323c6c53d31c34be34926a9de4/agent_framework_azure_ai_search-1.0.0b260130.tar.gz", hash = "sha256:0a622fdddd7dc0287de693f2aa6f770ec52ea8d1eaca817c4276daa08001c10b", size = 13312 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f5/ec/ac8143dbb1af2ec510f7772d712803193a6a0ad5f36b06e7ec7121df5c80/agent_framework_azure_ai_search-1.0.0b260130-py3-none-any.whl", hash = "sha256:0278c948696d7a00193a0271074c6057b57589ff98eda5544f2eafeac051d6e9", size = 13449 },
+]
+
+[[package]]
+name = "agent-framework-azurefunctions"
+version = "1.0.0b260130"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "agent-framework-core" },
+ { name = "agent-framework-durabletask" },
+ { name = "azure-functions" },
+ { name = "azure-functions-durable" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c4/0e/59c4c45c380b4d0dcfb71be45ec60a8d52b271979b5cf9e5be1f9e974653/agent_framework_azurefunctions-1.0.0b260130.tar.gz", hash = "sha256:b6a971036c7088a61e5079549f11e0c7972b955452bdb6d576769ed8da27b920", size = 16340 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0d/fa/200b40db670f79f561ff1e69e9626729ceb6486af970e3489f6c3a295d76/agent_framework_azurefunctions-1.0.0b260130-py3-none-any.whl", hash = "sha256:7d529a0bad67caa38d8823462c439e97de5e1cf364c0e9a0895df5fb44996f64", size = 17788 },
+]
+
+[[package]]
+name = "agent-framework-chatkit"
+version = "1.0.0b260130"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "agent-framework-core" },
+ { name = "openai-chatkit" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/be/9e/3f2d6440ad2a16308c26d894995895d131225c5284328190b1c5ae7f769a/agent_framework_chatkit-1.0.0b260130.tar.gz", hash = "sha256:e5953337a5d8dd7930c2692ba1b23cf771002a9797b5c2306e3f3b256db200cc", size = 12415 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9f/f1/68496e52aa36e66cf2962b8a8c6937053e2e57ad5f135b6983d705172554/agent_framework_chatkit-1.0.0b260130-py3-none-any.whl", hash = "sha256:a7814a5b222de7a0ac57fb89f4a6e534521c7e58bdc86a6465885fb9d57e63f1", size = 11712 },
+]
+
[[package]]
name = "agent-framework-copilotstudio"
version = "1.0.0b251007"
@@ -86,26 +161,58 @@ wheels = [
[[package]]
name = "agent-framework-core"
-version = "1.0.0b251007"
+version = "1.0.0b260130"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "aiofiles" },
{ name = "azure-identity" },
- { name = "azure-monitor-opentelemetry" },
- { name = "azure-monitor-opentelemetry-exporter" },
{ name = "mcp", extra = ["ws"] },
{ name = "openai" },
{ name = "opentelemetry-api" },
- { name = "opentelemetry-exporter-otlp-proto-grpc" },
{ name = "opentelemetry-sdk" },
{ name = "opentelemetry-semantic-conventions-ai" },
+ { name = "packaging" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/0f/ba/c4d706aa716a37f1f3400e7489ad464c3be86f94cd062d130d8ae3671121/agent_framework_core-1.0.0b251007.tar.gz", hash = "sha256:56ac1705b43e0ebe49ab7ec890625db3c619af63f455b87e41c631449e8b5de3", size = 384807 }
+sdist = { url = "https://files.pythonhosted.org/packages/4d/39/e508e778219bd6d20e023a6f48235861a639e3cf888776f9e873bbad3c6b/agent_framework_core-1.0.0b260130.tar.gz", hash = "sha256:030a5b2ced796eec6839c2dabad90b4bd1ea33d1026f3ed1813050a56ccfa4ec", size = 301823 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/36/68/afe66c72951a279e0fe048fd5af1e775528cde40dbdab8ec03b42c545df4/agent_framework_core-1.0.0b260130-py3-none-any.whl", hash = "sha256:75b4dd0ca2ae52574d406cf5c9ed7adf63e187379f72fce891743254d83dfd56", size = 348724 },
+]
+
+[package.optional-dependencies]
+all = [
+ { name = "agent-framework-a2a" },
+ { name = "agent-framework-ag-ui" },
+ { name = "agent-framework-anthropic" },
+ { name = "agent-framework-azure-ai" },
+ { name = "agent-framework-azure-ai-search" },
+ { name = "agent-framework-azurefunctions" },
+ { name = "agent-framework-chatkit" },
+ { name = "agent-framework-copilotstudio" },
+ { name = "agent-framework-declarative" },
+ { name = "agent-framework-devui" },
+ { name = "agent-framework-durabletask" },
+ { name = "agent-framework-github-copilot" },
+ { name = "agent-framework-lab" },
+ { name = "agent-framework-mem0" },
+ { name = "agent-framework-ollama" },
+ { name = "agent-framework-purview" },
+ { name = "agent-framework-redis" },
+]
+
+[[package]]
+name = "agent-framework-declarative"
+version = "1.0.0b260130"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "agent-framework-core" },
+ { name = "powerfx", marker = "python_full_version < '3.14'" },
+ { name = "pyyaml" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/55/a4/7821524773b40366db789ba620e23e086b3d205cf0e21f2a94b19026b4a3/agent_framework_declarative-1.0.0b260130.tar.gz", hash = "sha256:30171a7cdd4f140cc66f17084b2fa5296a45d6b5ce59a2deed95b008a672c98d", size = 78227 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/34/fc/e122b9f7e63b0e3ec425edc22aaff4649ef7dec5214fc93309a1331b7d37/agent_framework_core-1.0.0b251007-py3-none-any.whl", hash = "sha256:5042c37fa53370089fb7db07d7be9f1dfa4ea3491754878b04504e07cc9993c3", size = 261210 },
+ { url = "https://files.pythonhosted.org/packages/da/1c/e85fb11e3e1922e6442073e1ac7a0042a04d6f645393227c2b498575d187/agent_framework_declarative-1.0.0b260130-py3-none-any.whl", hash = "sha256:9ccfa1ed846c2e414ace1f9320e6e7fbbddf3ea9dafdeed138e2bfcb481c2bef", size = 89331 },
]
[[package]]
@@ -123,6 +230,46 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5d/b6/ab40dc4f768104bfaa43523e003c88aded8f79428f6c73a22f9547ddc752/agent_framework_devui-1.0.0b251007-py3-none-any.whl", hash = "sha256:350952d2c6442702b0281dfb4736cf9d94e1a7ea30de7f1c4419ad9408b42b2e", size = 268535 },
]
+[[package]]
+name = "agent-framework-durabletask"
+version = "1.0.0b260130"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "agent-framework-core" },
+ { name = "durabletask" },
+ { name = "durabletask-azuremanaged" },
+ { name = "python-dateutil" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e3/95/9d5ee7fd1fdcd52c10aa1b2902964701d1d62b9d35cc7d05115b90db6329/agent_framework_durabletask-1.0.0b260130.tar.gz", hash = "sha256:63a2c8e0968a51d8e132892e9d385d2b82ccb95263d2c0316dc46b0eaa4dd7a4", size = 30285 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/22/122ed515935926137cc3c6ca795ef01b30feb82160cfc0f29a34f9d603de/agent_framework_durabletask-1.0.0b260130-py3-none-any.whl", hash = "sha256:a46e292800d10a62ce0923efe753594ddbf0bd6d1bb6e1258380f0dbf7d0302f", size = 36357 },
+]
+
+[[package]]
+name = "agent-framework-github-copilot"
+version = "1.0.0b260130"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "agent-framework-core" },
+ { name = "github-copilot-sdk" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ae/00/f69d731db02e256b8d18d6d8cd20d3d0684245df876f22b836743403a9c1/agent_framework_github_copilot-1.0.0b260130.tar.gz", hash = "sha256:3f5f231785bc8e663da2d1db65a5e4ee49a0f6266e31cccbf3ef05a79ab6c90d", size = 7929 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/13/b8/0a09396682e915dc25dc39c69fc06cc199b9901ccb0fdbb5e9e2886d2cb0/agent_framework_github_copilot-1.0.0b260130-py3-none-any.whl", hash = "sha256:b8844bacbf666ff1ea7f27d34a42c11be4ade1c4d57e7545341bb74462d82703", size = 8752 },
+]
+
+[[package]]
+name = "agent-framework-lab"
+version = "1.0.0b251024"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "agent-framework-core" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/05/c5/be86273cb3545651d0c8112ff9f38ae8fe13b740ce9b65b9be83ff2d70ee/agent_framework_lab-1.0.0b251024.tar.gz", hash = "sha256:4261cb595b6edfd4f30db613c1885c71b3dcfa2088cf29224d4f17b3ff956b2a", size = 23397 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/26/0f/3974b2b1f6bf523ee3ced0886b6afd5ca8bbebd24aa5278ef77db0d3d765/agent_framework_lab-1.0.0b251024-py3-none-any.whl", hash = "sha256:1596408991a92fcacef4bb939305d2b59159517b707f48114105fc0dd46bfee7", size = 26589 },
+]
+
[[package]]
name = "agent-framework-mem0"
version = "1.0.0b251007"
@@ -136,6 +283,33 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/34/93/11905c94b4980b61f4910c7ce26988ff9bfd3a0fa864e2b83c066108363d/agent_framework_mem0-1.0.0b251007-py3-none-any.whl", hash = "sha256:eb9e2e3ae63284f30e4874afd7383e8ad0258a1e1acbc5bdf5e352911d8bb9b3", size = 5297 },
]
+[[package]]
+name = "agent-framework-ollama"
+version = "1.0.0b260130"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "agent-framework-core" },
+ { name = "ollama" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e9/a3/2c18ad3f7878415148a118a6695eb2bf02d2ba99a4138992bad3ad7a194f/agent_framework_ollama-1.0.0b260130.tar.gz", hash = "sha256:312b5d7eaf6894307c57844bf7cd7172f0592cf28f7c253d0a6460992dd87392", size = 8096 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b8/27/23e23a1919592dcf2aaf25aa9950a7dbda77c4ba03cba8843491b9f12024/agent_framework_ollama-1.0.0b260130-py3-none-any.whl", hash = "sha256:55e4e17f226ad61e8a9dcbbcc24ab006a3480043ecb4d32c12d2444f628054d6", size = 9167 },
+]
+
+[[package]]
+name = "agent-framework-purview"
+version = "1.0.0b260130"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "agent-framework-core" },
+ { name = "azure-core" },
+ { name = "httpx" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/60/09/02ecaddb6c647f1f0b6f399b902bbb99282e1547c4ef169a44f40684696d/agent_framework_purview-1.0.0b260130.tar.gz", hash = "sha256:80c9641f7ab33a8c366dc74b5cf55f91a6bc3095a4d6e5f7cf08a430d4357795", size = 26785 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/8f/c1a53f11fa80fb5dddf550104f9b81f321f23cff224606a19ecc92b4b483/agent_framework_purview-1.0.0b260130-py3-none-any.whl", hash = "sha256:4bd1d0ed320ab04358b662df945b1d59797a4dab497bb3a12cda33136466b8fc", size = 26139 },
+]
+
[[package]]
name = "agent-framework-redis"
version = "1.0.0b251007"
@@ -151,15 +325,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c3/1b/4aec0bb3976f47396fdc59c2ebdba759078f49f41b4e52a0a2c289d04a1e/agent_framework_redis-1.0.0b251007-py3-none-any.whl", hash = "sha256:4633f2e94d68ea9b4374f564bd622a1766913010b151fc04e01a751343f04804", size = 15606 },
]
-[[package]]
-name = "aiofiles"
-version = "24.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896 },
-]
-
[[package]]
name = "aiohappyeyeballs"
version = "2.6.1"
@@ -268,28 +433,31 @@ wheels = [
]
[[package]]
-name = "altair"
-version = "5.5.0"
+name = "annotated-types"
+version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "jinja2" },
- { name = "jsonschema" },
- { name = "narwhals" },
- { name = "packaging" },
- { name = "typing-extensions", marker = "python_full_version < '3.14'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/16/b1/f2969c7bdb8ad8bbdda031687defdce2c19afba2aa2c8e1d2a17f78376d8/altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d", size = 705305 }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/aa/f3/0b6ced594e51cc95d8c1fc1640d3623770d01e4969d29c0bd09945fafefa/altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c", size = 731200 },
+ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
]
[[package]]
-name = "annotated-types"
-version = "0.7.0"
+name = "anthropic"
+version = "0.77.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
+dependencies = [
+ { name = "anyio" },
+ { name = "distro" },
+ { name = "docstring-parser" },
+ { name = "httpx" },
+ { name = "jiter" },
+ { name = "pydantic" },
+ { name = "sniffio" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/88/61/50aef0587acd9dd8bf1b8b7fd7fbb25ba4c6ec5387a6ffc195a697951fcc/anthropic-0.77.1.tar.gz", hash = "sha256:a19d78ff6fff9e05d211e3a936051cd5b9462f0eac043d2d45b2372f455d11cd", size = 504691 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
+ { url = "https://files.pythonhosted.org/packages/2b/54/e83babf9833547c5548b4e25230ef3d62492e45925b0d104a43e501918a0/anthropic-0.77.1-py3-none-any.whl", hash = "sha256:76fd6f2ab36033a5294d58182a5f712dab9573c3a54413a275ecdf29e727c1e0", size = 397856 },
]
[[package]]
@@ -321,7 +489,6 @@ dependencies = [
{ name = "openai" },
{ name = "pydantic" },
{ name = "requests" },
- { name = "streamlit" },
{ name = "tenacity" },
{ name = "uvicorn" },
{ name = "websockets" },
@@ -329,7 +496,7 @@ dependencies = [
[package.metadata]
requires-dist = [
- { name = "agent-framework", specifier = "==1.0.0b251007" },
+ { name = "agent-framework", specifier = "==1.0.0b260130" },
{ name = "fastapi", specifier = "==0.115.12" },
{ name = "fastmcp", specifier = "==2.7.1" },
{ name = "flasgger", specifier = "==0.9.7.1" },
@@ -339,19 +506,18 @@ requires-dist = [
{ name = "openai", specifier = ">=1.90.0,<1.110.0" },
{ name = "pydantic", specifier = "==2.11.4" },
{ name = "requests", specifier = "==2.32.4" },
- { name = "streamlit", specifier = "==1.45.0" },
{ name = "tenacity", specifier = "==8.5.0" },
{ name = "uvicorn", specifier = ">=0.25.0" },
{ name = "websockets", specifier = ">=15.0.1" },
]
[[package]]
-name = "asgiref"
-version = "3.10.0"
+name = "asyncio"
+version = "4.0.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/46/08/4dfec9b90758a59acc6be32ac82e98d1fbfc321cb5cfa410436dbacf821c/asgiref-3.10.0.tar.gz", hash = "sha256:d89f2d8cd8b56dada7d52fa7dc8075baa08fb836560710d38c292a7a3f78c04e", size = 37483 }
+sdist = { url = "https://files.pythonhosted.org/packages/71/ea/26c489a11f7ca862d5705db67683a7361ce11c23a7b98fc6c2deaeccede2/asyncio-4.0.0.tar.gz", hash = "sha256:570cd9e50db83bc1629152d4d0b7558d6451bb1bfd5dfc2e935d96fc2f40329b", size = 5371 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/17/9c/fc2331f538fbf7eedba64b2052e99ccf9ba9d6888e2f41441ee28847004b/asgiref-3.10.0-py3-none-any.whl", hash = "sha256:aef8a81283a34d0ab31630c9b7dfe70c812c95eba78171367ca8745e88124734", size = 24050 },
+ { url = "https://files.pythonhosted.org/packages/57/64/eff2564783bd650ca25e15938d1c5b459cda997574a510f7de69688cb0b4/asyncio-4.0.0-py3-none-any.whl", hash = "sha256:c1eddb0659231837046809e68103969b2bef8b0400d59cfa6363f6b5ed8cc88b", size = 5555 },
]
[[package]]
@@ -405,6 +571,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/db/10/8b7bd070e3cc804343dab124ce66a3b7999a72d5be0e49232cbcd1d36e18/azure_ai_projects-1.1.0b4-py3-none-any.whl", hash = "sha256:d8aab84fd7cd7c5937e78141e37ca4473dc5ed6cce2c0490c634418abe14afea", size = 126670 },
]
+[[package]]
+name = "azure-common"
+version = "1.1.28"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3e/71/f6f71a276e2e69264a97ad39ef850dca0a04fce67b12570730cb38d0ccac/azure-common-1.1.28.zip", hash = "sha256:4ac0cd3214e36b6a1b6a442686722a5d8cc449603aa833f3f0f40bda836704a3", size = 20914 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/62/55/7f118b9c1b23ec15ca05d15a578d8207aa1706bc6f7c87218efffbbf875d/azure_common-1.1.28-py2.py3-none-any.whl", hash = "sha256:5c12d3dcf4ec20599ca6b0d3e09e86e146353d443e7fcc050c9a19c1f9df20ad", size = 14462 },
+]
+
[[package]]
name = "azure-core"
version = "1.35.1"
@@ -420,16 +595,33 @@ wheels = [
]
[[package]]
-name = "azure-core-tracing-opentelemetry"
-version = "1.0.0b12"
+name = "azure-functions"
+version = "1.25.0b3.dev3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "azure-core" },
+ { name = "werkzeug" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/70/09/a402b424bf7f489661fdf59126ebc8b9644626033a5f88b9dfe7c5fe1658/azure_functions-1.25.0b3.dev3.tar.gz", hash = "sha256:cee70ab55a87051da5c5ecca4ba747705e64f2c7f76f0f59f9072001059abf32", size = 141925 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9d/af/c67c849ce87416eae78ddf424e8222f5b0980404e60616a87fb041170344/azure_functions-1.25.0b3.dev3-py3-none-any.whl", hash = "sha256:64ef99a7baf053242394a20fc820458fbf0c207ee5635adfcbb3bb8f0d297716", size = 114054 },
+]
+
+[[package]]
+name = "azure-functions-durable"
+version = "1.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "azure-functions" },
+ { name = "furl" },
{ name = "opentelemetry-api" },
+ { name = "opentelemetry-sdk" },
+ { name = "python-dateutil" },
+ { name = "requests" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/5a/7f/5de13a331a5f2919417819cc37dcf7c897018f02f83aa82b733e6629a6a6/azure_core_tracing_opentelemetry-1.0.0b12.tar.gz", hash = "sha256:bb454142440bae11fd9d68c7c1d67ae38a1756ce808c5e4d736730a7b4b04144", size = 26010 }
+sdist = { url = "https://files.pythonhosted.org/packages/51/3a/f168b434fa69eaaf5d14b54d88239b851eceb7e10f666b55289dd0933ccb/azure-functions-durable-1.4.0.tar.gz", hash = "sha256:945488ef28917dae4295a4dd6e6f6601ffabe32e3fbb94ceb261c9b65b6e6c0f", size = 176584 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/76/5e/97a471f66935e7f89f521d0e11ae49c7f0871ca38f5c319dccae2155c8d8/azure_core_tracing_opentelemetry-1.0.0b12-py3-none-any.whl", hash = "sha256:38fd42709f1cc4bbc4f2797008b1c30a6a01617e49910c05daa3a0d0c65053ac", size = 11962 },
+ { url = "https://files.pythonhosted.org/packages/74/01/7f03229fa5c05a5cc7e41172aef80c5242d28aeea0825f592f93141a4b91/azure_functions_durable-1.4.0-py3-none-any.whl", hash = "sha256:0efe919cdda96924791feabe192a37c7d872414b4c6ce348417a02ee53d8cc31", size = 143159 },
]
[[package]]
@@ -449,44 +641,18 @@ wheels = [
]
[[package]]
-name = "azure-monitor-opentelemetry"
-version = "1.8.1"
+name = "azure-search-documents"
+version = "11.7.0b2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
+ { name = "azure-common" },
{ name = "azure-core" },
- { name = "azure-core-tracing-opentelemetry" },
- { name = "azure-monitor-opentelemetry-exporter" },
- { name = "opentelemetry-instrumentation-django" },
- { name = "opentelemetry-instrumentation-fastapi" },
- { name = "opentelemetry-instrumentation-flask" },
- { name = "opentelemetry-instrumentation-psycopg2" },
- { name = "opentelemetry-instrumentation-requests" },
- { name = "opentelemetry-instrumentation-urllib" },
- { name = "opentelemetry-instrumentation-urllib3" },
- { name = "opentelemetry-resource-detector-azure" },
- { name = "opentelemetry-sdk" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/55/ae/eae89705498c975b1cfcc2ce0e5bfbe784a47ffd54cef6fbebe31fdb2295/azure_monitor_opentelemetry-1.8.1.tar.gz", hash = "sha256:9b93b62868775d74db60d9e997cfccc5898260c5de23278d7e99cce3764e9fda", size = 53471 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/85/ab/d063f5d0debbb01ef716789f5b4b315d58f657dd5dbf15e47ca6648a557b/azure_monitor_opentelemetry-1.8.1-py3-none-any.whl", hash = "sha256:bebca6af9d81ddc52df59b281a5acc84182bbf1cbccd6f843a2074f6e283947e", size = 27169 },
-]
-
-[[package]]
-name = "azure-monitor-opentelemetry-exporter"
-version = "1.0.0b42"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "azure-core" },
- { name = "azure-identity" },
- { name = "fixedint" },
- { name = "msrest" },
- { name = "opentelemetry-api" },
- { name = "opentelemetry-sdk" },
- { name = "psutil" },
+ { name = "isodate" },
+ { name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/d6/d9/ed8c3a4ad8ae2ab8745d940305ca8ec41208dfdefa6a7a232ab62ffe40a3/azure_monitor_opentelemetry_exporter-1.0.0b42.tar.gz", hash = "sha256:d35b60e0404446932e31e3a0b1c2202e47e9b78f91bb24110bd164aa62c5bb87", size = 245626 }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/ba/bde0f03e0a742ba3bbcc929f91ed2f3b1420c2bb84c9a7f878f3b87ebfce/azure_search_documents-11.7.0b2.tar.gz", hash = "sha256:b6e039f8038ff2210d2057e704e867c6e29bb46bfcd400da4383e45e4b8bb189", size = 423956 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/86/c9/d6050a53565f3653205fad79d53982411530b80a72ee1352110c0c0cd0fe/azure_monitor_opentelemetry_exporter-1.0.0b42-py2.py3-none-any.whl", hash = "sha256:649d8a634c119ae942d2dd20ff3006dda88050d3c7044b09bd5111e66439833e", size = 183324 },
+ { url = "https://files.pythonhosted.org/packages/e5/26/ed4498374f9088818278ac225f2bea688b4ec979d81bf83a5355c8c366af/azure_search_documents-11.7.0b2-py3-none-any.whl", hash = "sha256:f82117b321344a84474269ed26df194c24cca619adc024d981b1b86aee3c6f05", size = 432037 },
]
[[package]]
@@ -651,6 +817,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295 },
]
+[[package]]
+name = "clr-loader"
+version = "0.2.10"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/18/24/c12faf3f61614b3131b5c98d3bf0d376b49c7feaa73edca559aeb2aee080/clr_loader-0.2.10.tar.gz", hash = "sha256:81f114afbc5005bafc5efe5af1341d400e22137e275b042a8979f3feb9fc9446", size = 83605 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c8/61/cf819f8e8bb4d4c74661acf2498ba8d4a296714be3478d21eaabf64f5b9b/clr_loader-0.2.10-py3-none-any.whl", hash = "sha256:ebbbf9d511a7fe95fa28a95a4e04cd195b097881dfe66158dc2c281d3536f282", size = 56483 },
+]
+
[[package]]
name = "colorama"
version = "0.4.6"
@@ -704,6 +882,43 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 },
]
+[[package]]
+name = "docstring-parser"
+version = "0.17.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896 },
+]
+
+[[package]]
+name = "durabletask"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "asyncio" },
+ { name = "grpcio" },
+ { name = "packaging" },
+ { name = "protobuf" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c9/27/3d021e6b36fc1aab6099fafc56dfc8059b4e8968615a26c1a0418601e50a/durabletask-1.3.0.tar.gz", hash = "sha256:11e38dda6df4737fadca0c71fc0a0f769955877c8a8bdb25ccbf90cf45afbf63", size = 57830 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/44/87/31ea460dbfaf50d9877f143e2ce9829cac2fb106747d9900cc353356ea77/durabletask-1.3.0-py3-none-any.whl", hash = "sha256:411f23e13391b8845edca010873dd7a87ee7cfc1fe05753ab28a7cd7c3c1bd77", size = 64112 },
+]
+
+[[package]]
+name = "durabletask-azuremanaged"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "azure-identity" },
+ { name = "durabletask" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/29/29/6bb0b5fe51aa92e117adcdc93efe97cf5476d86c1496e5c5ab35d99a8d07/durabletask_azuremanaged-1.3.0.tar.gz", hash = "sha256:55172588e075afa80d46dcc2e5ddbd84be0a20cc78c74f687040c3720677d34c", size = 4343 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/08/11/4d34fec302c4813e626080f1532d189767eb31d6d80e8f3698c230512f14/durabletask_azuremanaged-1.3.0-py3-none-any.whl", hash = "sha256:9da914f569da1597c858d494a95eda37e4372726c0ee65f30080dcafab262d60", size = 6366 },
+]
+
[[package]]
name = "exceptiongroup"
version = "1.3.0"
@@ -749,15 +964,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/b8/af0bb06d1388b680c64ec7b9767d3718e51e65d91e425c1296446f10a9fc/fastmcp-2.7.1-py3-none-any.whl", hash = "sha256:e75b4c7088338f2532d79f37a2ae654f47bfd7d3d15340233fda25bc168231b6", size = 127618 },
]
-[[package]]
-name = "fixedint"
-version = "0.1.6"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/32/c6/b1b9b3f69915d51909ef6ebe6352e286ec3d6f2077278af83ec6e3cc569c/fixedint-0.1.6.tar.gz", hash = "sha256:703005d090499d41ce7ce2ee7eae8f7a5589a81acdc6b79f1728a56495f2c799", size = 12750 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c8/6d/8f5307d26ce700a89e5a67d1e1ad15eff977211f9ed3ae90d7b0d67f4e66/fixedint-0.1.6-py3-none-any.whl", hash = "sha256:b8cf9f913735d2904deadda7a6daa9f57100599da1de57a7448ea1be75ae8c9c", size = 12702 },
-]
-
[[package]]
name = "flasgger"
version = "0.9.7.1"
@@ -878,27 +1084,30 @@ wheels = [
]
[[package]]
-name = "gitdb"
-version = "4.0.12"
+name = "furl"
+version = "2.1.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "smmap" },
+ { name = "orderedmultidict" },
+ { name = "six" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 }
+sdist = { url = "https://files.pythonhosted.org/packages/53/e4/203a76fa2ef46cdb0a618295cc115220cbb874229d4d8721068335eb87f0/furl-2.1.4.tar.gz", hash = "sha256:877657501266c929269739fb5f5980534a41abd6bbabcb367c136d1d3b2a6015", size = 57526 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 },
+ { url = "https://files.pythonhosted.org/packages/61/8c/dce3b1b7593858eba995b2dfdb833f872c7f863e3da92aab7128a6b11af4/furl-2.1.4-py2.py3-none-any.whl", hash = "sha256:da34d0b34e53ffe2d2e6851a7085a05d96922b5b578620a37377ff1dbeeb11c8", size = 27550 },
]
[[package]]
-name = "gitpython"
-version = "3.1.45"
+name = "github-copilot-sdk"
+version = "0.1.20"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "gitdb" },
+ { name = "pydantic" },
+ { name = "python-dateutil" },
+ { name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/9a/c8/dd58967d119baab745caec2f9d853297cec1989ec1d63f677d3880632b88/gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c", size = 215076 }
+sdist = { url = "https://files.pythonhosted.org/packages/02/7d/afde0ec85815a558612130dc5ff79536299f411e672410c3edc0c1edeb2a/github_copilot_sdk-0.1.20.tar.gz", hash = "sha256:9e89cd46577fd18dd808d7113b7e20e021c4f944121a0a4891945460fb26c53c", size = 92207 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168 },
+ { url = "https://files.pythonhosted.org/packages/55/91/f8cfa809184988a273af58824b312d31a532ee3ee70875100b5061540178/github_copilot_sdk-0.1.20-py3-none-any.whl", hash = "sha256:e7fa1bb843e2494930126551b80f3a035f36c47a05f9173ad0cdfb4151ad9346", size = 40306 },
]
[[package]]
@@ -957,6 +1166,8 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586 },
{ url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281 },
{ url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142 },
+ { url = "https://files.pythonhosted.org/packages/27/45/80935968b53cfd3f33cf99ea5f08227f2646e044568c9b1555b58ffd61c2/greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0", size = 1564846 },
+ { url = "https://files.pythonhosted.org/packages/69/02/b7c30e5e04752cb4db6202a3858b149c0710e5453b71a3b2aec5d78a1aab/greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d", size = 1633814 },
{ url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899 },
{ url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814 },
{ url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073 },
@@ -966,6 +1177,8 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497 },
{ url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662 },
{ url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210 },
+ { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759 },
+ { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288 },
{ url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685 },
{ url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586 },
{ url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346 },
@@ -973,9 +1186,23 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659 },
{ url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355 },
{ url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512 },
+ { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508 },
+ { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760 },
{ url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425 },
]
+[[package]]
+name = "griffe"
+version = "1.15.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0d/0c/3a471b6e31951dce2360477420d0a8d1e00dea6cf33b70f3e8c3ab6e28e1/griffe-1.15.0.tar.gz", hash = "sha256:7726e3afd6f298fbc3696e67958803e7ac843c1cfe59734b6251a40cdbfb5eea", size = 424112 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.0-py3-none-any.whl", hash = "sha256:6f6762661949411031f5fcda9593f586e6ce8340f0ba88921a0f2ef7a81eb9a3", size = 150705 },
+]
+
[[package]]
name = "grpcio"
version = "1.76.0rc1"
@@ -1336,7 +1563,7 @@ wheels = [
[[package]]
name = "mcp"
-version = "1.16.0"
+version = "1.26.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
@@ -1345,15 +1572,18 @@ dependencies = [
{ name = "jsonschema" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
+ { name = "pyjwt", extra = ["crypto"] },
{ name = "python-multipart" },
{ name = "pywin32", marker = "sys_platform == 'win32'" },
{ name = "sse-starlette" },
{ name = "starlette" },
+ { name = "typing-extensions" },
+ { name = "typing-inspection" },
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/3d/a1/b1f328da3b153683d2ec34f849b4b6eac2790fb240e3aef06ff2fab3df9d/mcp-1.16.0.tar.gz", hash = "sha256:39b8ca25460c578ee2cdad33feeea122694cfdf73eef58bee76c42f6ef0589df", size = 472918 }
+sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c9/0e/7cebc88e17daf94ebe28c95633af595ccb2864dc2ee7abd75542d98495cc/mcp-1.16.0-py3-none-any.whl", hash = "sha256:ec917be9a5d31b09ba331e1768aa576e0af45470d657a0319996a20a57d7d633", size = 167266 },
+ { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615 },
]
[package.optional-dependencies]
@@ -1495,22 +1725,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583 },
]
-[[package]]
-name = "msrest"
-version = "0.7.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "azure-core" },
- { name = "certifi" },
- { name = "isodate" },
- { name = "requests" },
- { name = "requests-oauthlib" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/68/77/8397c8fb8fc257d8ea0fa66f8068e073278c65f05acb17dcb22a02bfdc42/msrest-0.7.1.zip", hash = "sha256:6e7661f46f3afd88b75667b7187a92829924446c7ea1d169be8c4bb7eeb788b9", size = 175332 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/15/cf/f2966a2638144491f8696c27320d5219f48a072715075d168b31d3237720/msrest-0.7.1-py3-none-any.whl", hash = "sha256:21120a810e1233e5e6cc7fe40b474eeb4ec6f757a15d7cf86702c369f9567c32", size = 85384 },
-]
-
[[package]]
name = "multidict"
version = "6.7.0"
@@ -1610,15 +1824,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317 },
]
-[[package]]
-name = "narwhals"
-version = "2.7.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/87/76/9ca8f4d03f02b8289807d0c91eeb01fa6b7fdd6273769d5bd1f94773b40b/narwhals-2.7.0.tar.gz", hash = "sha256:e3fff7f1610fd3318ede78c969bc5954ce710d585eefdb689586fb69da3da43c", size = 569315 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/74/0d/bc630dfd34ad2150d40f9392e94d3803980e71a47e10a709ce9bfcd40ffe/narwhals-2.7.0-py3-none-any.whl", hash = "sha256:010791aa0cee86d90bf2b658264aaec3eeea34fb4ddf2e83746ea4940bcffae3", size = 412767 },
-]
-
[[package]]
name = "numpy"
version = "2.3.3"
@@ -1683,12 +1888,16 @@ wheels = [
]
[[package]]
-name = "oauthlib"
-version = "3.3.1"
+name = "ollama"
+version = "0.6.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918 }
+dependencies = [
+ { name = "httpx" },
+ { name = "pydantic" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9d/5a/652dac4b7affc2b37b95386f8ae78f22808af09d720689e3d7a86b6ed98e/ollama-0.6.1.tar.gz", hash = "sha256:478c67546836430034b415ed64fa890fd3d1ff91781a9d548b3325274e69d7c6", size = 51620 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065 },
+ { url = "https://files.pythonhosted.org/packages/47/4f/4a617ee93d8208d2bcf26b2d8b9402ceaed03e3853c754940e2290fed063/ollama-0.6.1-py3-none-any.whl", hash = "sha256:fc4c984b345735c5486faeee67d8a265214a31cbb828167782dc642ce0a2bf8c", size = 14354 },
]
[[package]]
@@ -1711,279 +1920,89 @@ wheels = [
]
[[package]]
-name = "openapi-pydantic"
-version = "0.5.1"
+name = "openai-agents"
+version = "0.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
+ { name = "griffe" },
+ { name = "mcp" },
+ { name = "openai" },
{ name = "pydantic" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381 },
-]
-
-[[package]]
-name = "opentelemetry-api"
-version = "1.37.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "importlib-metadata" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/63/04/05040d7ce33a907a2a02257e601992f0cdf11c73b33f13c4492bf6c3d6d5/opentelemetry_api-1.37.0.tar.gz", hash = "sha256:540735b120355bd5112738ea53621f8d5edb35ebcd6fe21ada3ab1c61d1cd9a7", size = 64923 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/91/48/28ed9e55dcf2f453128df738210a980e09f4e468a456fa3c763dbc8be70a/opentelemetry_api-1.37.0-py3-none-any.whl", hash = "sha256:accf2024d3e89faec14302213bc39550ec0f4095d1cf5ca688e1bfb1c8612f47", size = 65732 },
-]
-
-[[package]]
-name = "opentelemetry-exporter-otlp-proto-common"
-version = "1.37.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "opentelemetry-proto" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/dc/6c/10018cbcc1e6fff23aac67d7fd977c3d692dbe5f9ef9bb4db5c1268726cc/opentelemetry_exporter_otlp_proto_common-1.37.0.tar.gz", hash = "sha256:c87a1bdd9f41fdc408d9cc9367bb53f8d2602829659f2b90be9f9d79d0bfe62c", size = 20430 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/08/13/b4ef09837409a777f3c0af2a5b4ba9b7af34872bc43609dda0c209e4060d/opentelemetry_exporter_otlp_proto_common-1.37.0-py3-none-any.whl", hash = "sha256:53038428449c559b0c564b8d718df3314da387109c4d36bd1b94c9a641b0292e", size = 18359 },
-]
-
-[[package]]
-name = "opentelemetry-exporter-otlp-proto-grpc"
-version = "1.37.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "googleapis-common-protos" },
- { name = "grpcio" },
- { name = "opentelemetry-api" },
- { name = "opentelemetry-exporter-otlp-proto-common" },
- { name = "opentelemetry-proto" },
- { name = "opentelemetry-sdk" },
+ { name = "requests" },
+ { name = "types-requests" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/d1/11/4ad0979d0bb13ae5a845214e97c8d42da43980034c30d6f72d8e0ebe580e/opentelemetry_exporter_otlp_proto_grpc-1.37.0.tar.gz", hash = "sha256:f55bcb9fc848ce05ad3dd954058bc7b126624d22c4d9e958da24d8537763bec5", size = 24465 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/39/17/46630b74751031a658706bef23ac99cdc2953cd3b2d28ec90590a0766b3e/opentelemetry_exporter_otlp_proto_grpc-1.37.0-py3-none-any.whl", hash = "sha256:aee5104835bf7993b7ddaaf380b6467472abaedb1f1dbfcc54a52a7d781a3890", size = 19305 },
-]
-
-[[package]]
-name = "opentelemetry-instrumentation"
-version = "0.58b0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "opentelemetry-api" },
- { name = "opentelemetry-semantic-conventions" },
- { name = "packaging" },
- { name = "wrapt" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/f6/36/7c307d9be8ce4ee7beb86d7f1d31027f2a6a89228240405a858d6e4d64f9/opentelemetry_instrumentation-0.58b0.tar.gz", hash = "sha256:df640f3ac715a3e05af145c18f527f4422c6ab6c467e40bd24d2ad75a00cb705", size = 31549 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d4/db/5ff1cd6c5ca1d12ecf1b73be16fbb2a8af2114ee46d4b0e6d4b23f4f4db7/opentelemetry_instrumentation-0.58b0-py3-none-any.whl", hash = "sha256:50f97ac03100676c9f7fc28197f8240c7290ca1baa12da8bfbb9a1de4f34cc45", size = 33019 },
-]
-
-[[package]]
-name = "opentelemetry-instrumentation-asgi"
-version = "0.58b0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "asgiref" },
- { name = "opentelemetry-api" },
- { name = "opentelemetry-instrumentation" },
- { name = "opentelemetry-semantic-conventions" },
- { name = "opentelemetry-util-http" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/7b/e2/03ff707d881d590c7adaed5e9d1979aed7e5e53fc1ed89035e5ed9f304af/opentelemetry_instrumentation_asgi-0.58b0.tar.gz", hash = "sha256:3ccc0c9c1c8c71e8d9da5945c6dcd9c0c8d147839f208536b7042c6dd98e65c9", size = 25116 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8c/71/a00884c6655387c70070138acbf79a6616ad5d4489680f40708d75b598a7/opentelemetry_instrumentation_asgi-0.58b0-py3-none-any.whl", hash = "sha256:508a6d79e333d648d2afee0e140b6e80eb5d443be183be58e81d9ff88373168a", size = 16798 },
-]
-
-[[package]]
-name = "opentelemetry-instrumentation-dbapi"
-version = "0.58b0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "opentelemetry-api" },
- { name = "opentelemetry-instrumentation" },
- { name = "opentelemetry-semantic-conventions" },
- { name = "wrapt" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e5/8b/bf6c72d54f77eb0e4445e3b0415e69b3ea5fa40b9372c586db91ffc59b17/opentelemetry_instrumentation_dbapi-0.58b0.tar.gz", hash = "sha256:34ca7e7bf942d5ebf1ea3838e34154b3900bd00d17115a99b83c4ee280e658ac", size = 14223 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b4/18/bf51a3913da9659d17aa1f44860bcc5d721ddcdcdbfbca80b596139d8811/opentelemetry_instrumentation_dbapi-0.58b0-py3-none-any.whl", hash = "sha256:49283687dfc47f05484d4b186fecca8a96b70e18fd406e34c13eb5f09eabb67c", size = 12522 },
-]
-
-[[package]]
-name = "opentelemetry-instrumentation-django"
-version = "0.58b0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "opentelemetry-api" },
- { name = "opentelemetry-instrumentation" },
- { name = "opentelemetry-instrumentation-wsgi" },
- { name = "opentelemetry-semantic-conventions" },
- { name = "opentelemetry-util-http" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/69/e8/6055cb9129a5a80b8814b770b79ce559977ff3b3b11dfd4067566ff25c1d/opentelemetry_instrumentation_django-0.58b0.tar.gz", hash = "sha256:24f45706a9dc3c47b9214ed5422fd0d35a850f3f40b04112a91fc10561cfd3f5", size = 25009 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/83/1a/396e941a7aae4e81793931a60669378c64a147a6dca75344b4c26cc8811b/opentelemetry_instrumentation_django-0.58b0-py3-none-any.whl", hash = "sha256:6e3ada766ce965e9486d193e10cb32749bd51fe1adb5ec6b9310e33fe89fad6d", size = 19596 },
-]
-
-[[package]]
-name = "opentelemetry-instrumentation-fastapi"
-version = "0.58b0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "opentelemetry-api" },
- { name = "opentelemetry-instrumentation" },
- { name = "opentelemetry-instrumentation-asgi" },
- { name = "opentelemetry-semantic-conventions" },
- { name = "opentelemetry-util-http" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/64/09/4f8fcab834af6b403e5e2d94bdfb2d0835ba8cd1049bcc156995f47b65fb/opentelemetry_instrumentation_fastapi-0.58b0.tar.gz", hash = "sha256:03da470d694116a0a40f4e76319e42f3ff9efc49abf804b2acc2c07f96661497", size = 24598 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/45/fb/82de06eba54e5cb979274f073065ebc374794853502d342b5155073d1194/opentelemetry_instrumentation_fastapi-0.58b0-py3-none-any.whl", hash = "sha256:d89bfec69c9ffc5d9f3fe58655d6660a66b2bca863b9132712c06edcde68b6fa", size = 13460 },
-]
-
-[[package]]
-name = "opentelemetry-instrumentation-flask"
-version = "0.58b0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "opentelemetry-api" },
- { name = "opentelemetry-instrumentation" },
- { name = "opentelemetry-instrumentation-wsgi" },
- { name = "opentelemetry-semantic-conventions" },
- { name = "opentelemetry-util-http" },
- { name = "packaging" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/8b/df/68aa2dea7e04401f9ce669e6a7a46cc25eb4bb7a14004bf7d535bb27c122/opentelemetry_instrumentation_flask-0.58b0.tar.gz", hash = "sha256:ea2e06f448cef263c21f86401984906f68a5c766c7359000afb5621ae528d9c5", size = 19420 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/07/7f/88079bc3e4aa188d78692328453f906dca35fa9f286623af13df0b0a1ead/opentelemetry_instrumentation_flask-0.58b0-py3-none-any.whl", hash = "sha256:b0d57ad4db7bd0177ddf8c7ae3adf8bd90e2ebfa2dd30884c6a97c97197e4ac5", size = 14685 },
-]
-
-[[package]]
-name = "opentelemetry-instrumentation-psycopg2"
-version = "0.58b0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "opentelemetry-api" },
- { name = "opentelemetry-instrumentation" },
- { name = "opentelemetry-instrumentation-dbapi" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/36/3e/eee2fafdd5c7f141cffc588859659e3e0808872c7f8f28d321921d6cc08d/opentelemetry_instrumentation_psycopg2-0.58b0.tar.gz", hash = "sha256:e08e2336926a920bc01788d7ff08315c7d995bd62bc9588c316ebb46f05ae95c", size = 10735 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/20/d8/5e92e7e1be63076a23309523c827437ab7e4f77c20fa2077c49aa055cf56/opentelemetry_instrumentation_psycopg2-0.58b0-py3-none-any.whl", hash = "sha256:6afa483f4d1f6d94702082c96e5a0e14bad32195e0d3c10ab5cda92a8f523e36", size = 10732 },
-]
-
-[[package]]
-name = "opentelemetry-instrumentation-requests"
-version = "0.58b0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "opentelemetry-api" },
- { name = "opentelemetry-instrumentation" },
- { name = "opentelemetry-semantic-conventions" },
- { name = "opentelemetry-util-http" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/36/42/83ee32de763b919779aaa595b60c5a7b9c0a4b33952bbe432c5f6a783085/opentelemetry_instrumentation_requests-0.58b0.tar.gz", hash = "sha256:ae9495e6ff64e27bdb839fce91dbb4be56e325139828e8005f875baf41951a2e", size = 15188 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/90/4d/f3476b28ea167d1762134352d01ae9693940a42c78994d9f1b32a4477816/opentelemetry_instrumentation_requests-0.58b0-py3-none-any.whl", hash = "sha256:672a0be0bb5b52bea0c11820b35e27edcf4cd22d34abe4afc59a92a80519f8a8", size = 12966 },
-]
-
-[[package]]
-name = "opentelemetry-instrumentation-urllib"
-version = "0.58b0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "opentelemetry-api" },
- { name = "opentelemetry-instrumentation" },
- { name = "opentelemetry-semantic-conventions" },
- { name = "opentelemetry-util-http" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/84/94/48171907cb9ced5bdc5be18f8cc8a8234bb2ec695f20c69f1330b336f2fb/opentelemetry_instrumentation_urllib-0.58b0.tar.gz", hash = "sha256:071e5a28a1c4198cfa33937484f4b0b1068aab26d75e71e55f598a717f268d0a", size = 13932 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/57/66/37edce21a14b6a983577d7aea95fee3581e80f9b4a272f514726e5041104/opentelemetry_instrumentation_urllib-0.58b0-py3-none-any.whl", hash = "sha256:63ad8a304a299bcb39224ecedc718a391404c8f2d4cc5755edfb5e49904e7b27", size = 12674 },
-]
-
-[[package]]
-name = "opentelemetry-instrumentation-urllib3"
-version = "0.58b0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "opentelemetry-api" },
- { name = "opentelemetry-instrumentation" },
- { name = "opentelemetry-semantic-conventions" },
- { name = "opentelemetry-util-http" },
- { name = "wrapt" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/9f/e7/affaeadd974587c6eaab1c8af2dfba776dcc083493c97cb193173570d335/opentelemetry_instrumentation_urllib3-0.58b0.tar.gz", hash = "sha256:978b8e3daa076437b1f7ed7509d8156108aee0679556fd355e532c4065dd7635", size = 15791 }
+sdist = { url = "https://files.pythonhosted.org/packages/a4/37/2b4f828840d3ff32d82b813c3371ec9ee26b3b8dc6b4acbb7a4a579f617a/openai_agents-0.3.3.tar.gz", hash = "sha256:b016381a6890e1cb6879eb23c53c35f8c2312be1117f1cd4e4b5e2463150839f", size = 1816230 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/1a/5f/d960be00ec15a722f2bfff9d226b320cc6c4191b737c98b937541fdecf83/opentelemetry_instrumentation_urllib3-0.58b0-py3-none-any.whl", hash = "sha256:9e698785afe311edfab772152cb4851f1aaffde5110bb83e4e45d8c4e97277ee", size = 13188 },
+ { url = "https://files.pythonhosted.org/packages/65/59/fd49fd2c3184c0d5fedb8c9c456ae9852154828bca7ee69dce004ea83188/openai_agents-0.3.3-py3-none-any.whl", hash = "sha256:aa2c74e010b923c09f166e63a51fae8c850c62df8581b84bafcbe5bd208d1505", size = 210893 },
]
[[package]]
-name = "opentelemetry-instrumentation-wsgi"
-version = "0.58b0"
+name = "openai-chatkit"
+version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "opentelemetry-api" },
- { name = "opentelemetry-instrumentation" },
- { name = "opentelemetry-semantic-conventions" },
- { name = "opentelemetry-util-http" },
+ { name = "jinja2" },
+ { name = "openai" },
+ { name = "openai-agents" },
+ { name = "pydantic" },
+ { name = "uvicorn" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/3e/76/c33bb3f219dc2266f98b8f927e913e93b31e94af7aa6430a9a9f167f9ab2/opentelemetry_instrumentation_wsgi-0.58b0.tar.gz", hash = "sha256:0ea27d44c83b48e6b182a904c801ca62b2999642647f32ef33c8a9c8bbf6a245", size = 18377 }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/8d/80d05af592b4c9484014de5cb5fd095916ac32f077232f1e62b85452cf07/openai_chatkit-1.6.0.tar.gz", hash = "sha256:01d029f4ddbb2035a84a484cecb254e6848601ae76a466bc8f8ce8b61c62efa6", size = 60890 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/81/05/a168ba97831823e170937b4f1a0f95657c8bd9cc573c81629f464c3cc32b/opentelemetry_instrumentation_wsgi-0.58b0-py3-none-any.whl", hash = "sha256:cef5bdf1cb7a5162fdb1c1a2f95e4a08e02b1ca67ce828a1efdf81e9f23273b7", size = 14449 },
+ { url = "https://files.pythonhosted.org/packages/1a/9d/6830850971dcd89f0461801be0cab7affce8d584799fc1397077bd082c3f/openai_chatkit-1.6.0-py3-none-any.whl", hash = "sha256:241887f65dd129d0af7cc6e30c46c99c4a477317c1862d8620d3a579b0511dcd", size = 42271 },
]
[[package]]
-name = "opentelemetry-proto"
-version = "1.37.0"
+name = "openapi-pydantic"
+version = "0.5.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "protobuf" },
+ { name = "pydantic" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/dd/ea/a75f36b463a36f3c5a10c0b5292c58b31dbdde74f6f905d3d0ab2313987b/opentelemetry_proto-1.37.0.tar.gz", hash = "sha256:30f5c494faf66f77faeaefa35ed4443c5edb3b0aa46dad073ed7210e1a789538", size = 46151 }
+sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c4/25/f89ea66c59bd7687e218361826c969443c4fa15dfe89733f3bf1e2a9e971/opentelemetry_proto-1.37.0-py3-none-any.whl", hash = "sha256:8ed8c066ae8828bbf0c39229979bdf583a126981142378a9cbe9d6fd5701c6e2", size = 72534 },
+ { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381 },
]
[[package]]
-name = "opentelemetry-resource-detector-azure"
-version = "0.1.5"
+name = "opentelemetry-api"
+version = "1.39.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "opentelemetry-sdk" },
+ { name = "importlib-metadata" },
+ { name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/67/e4/0d359d48d03d447225b30c3dd889d5d454e3b413763ff721f9b0e4ac2e59/opentelemetry_resource_detector_azure-0.1.5.tar.gz", hash = "sha256:e0ba658a87c69eebc806e75398cd0e9f68a8898ea62de99bc1b7083136403710", size = 11503 }
+sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c3/ae/c26d8da88ba2e438e9653a408b0c2ad6f17267801250a8f3cc6405a93a72/opentelemetry_resource_detector_azure-0.1.5-py3-none-any.whl", hash = "sha256:4dcc5d54ab5c3b11226af39509bc98979a8b9e0f8a24c1b888783755d3bf00eb", size = 14252 },
+ { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356 },
]
[[package]]
name = "opentelemetry-sdk"
-version = "1.37.0"
+version = "1.39.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-api" },
{ name = "opentelemetry-semantic-conventions" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/f4/62/2e0ca80d7fe94f0b193135375da92c640d15fe81f636658d2acf373086bc/opentelemetry_sdk-1.37.0.tar.gz", hash = "sha256:cc8e089c10953ded765b5ab5669b198bbe0af1b3f89f1007d19acd32dc46dda5", size = 170404 }
+sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/9f/62/9f4ad6a54126fb00f7ed4bb5034964c6e4f00fcd5a905e115bd22707e20d/opentelemetry_sdk-1.37.0-py3-none-any.whl", hash = "sha256:8f3c3c22063e52475c5dbced7209495c2c16723d016d39287dfc215d1771257c", size = 131941 },
+ { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565 },
]
[[package]]
name = "opentelemetry-semantic-conventions"
-version = "0.58b0"
+version = "0.60b1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "opentelemetry-api" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/aa/1b/90701d91e6300d9f2fb352153fb1721ed99ed1f6ea14fa992c756016e63a/opentelemetry_semantic_conventions-0.58b0.tar.gz", hash = "sha256:6bd46f51264279c433755767bb44ad00f1c9e2367e1b42af563372c5a6fa0c25", size = 129867 }
+sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/07/90/68152b7465f50285d3ce2481b3aec2f82822e3f52e5152eeeaf516bab841/opentelemetry_semantic_conventions-0.58b0-py3-none-any.whl", hash = "sha256:5564905ab1458b96684db1340232729fce3b5375a06e140e8904c78e4f815b28", size = 207954 },
+ { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982 },
]
[[package]]
@@ -1996,12 +2015,15 @@ wheels = [
]
[[package]]
-name = "opentelemetry-util-http"
-version = "0.58b0"
+name = "orderedmultidict"
+version = "1.0.2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c6/5f/02f31530faf50ef8a41ab34901c05cbbf8e9d76963ba2fb852b0b4065f4e/opentelemetry_util_http-0.58b0.tar.gz", hash = "sha256:de0154896c3472c6599311c83e0ecee856c4da1b17808d39fdc5cce5312e4d89", size = 9411 }
+dependencies = [
+ { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5c/62/61ad51f6c19d495970230a7747147ce7ed3c3a63c2af4ebfdb1f6d738703/orderedmultidict-1.0.2.tar.gz", hash = "sha256:16a7ae8432e02cc987d2d6d5af2df5938258f87c870675c73ee77a0920e6f4a6", size = 13973 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a5/a3/0a1430c42c6d34d8372a16c104e7408028f0c30270d8f3eb6cccf2e82934/opentelemetry_util_http-0.58b0-py3-none-any.whl", hash = "sha256:6c6b86762ed43025fbd593dc5f700ba0aa3e09711aedc36fd48a13b23d8cb1e7", size = 7652 },
+ { url = "https://files.pythonhosted.org/packages/b2/6c/d8a02ffb24876b5f51fbd781f479fc6525a518553a4196bd0433dae9ff8e/orderedmultidict-1.0.2-py2.py3-none-any.whl", hash = "sha256:ab5044c1dca4226ae4c28524cfc5cc4c939f0b49e978efa46a6ad6468049f79b", size = 11897 },
]
[[package]]
@@ -2013,119 +2035,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
]
-[[package]]
-name = "pandas"
-version = "2.3.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "numpy" },
- { name = "python-dateutil" },
- { name = "pytz" },
- { name = "tzdata" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846 },
- { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618 },
- { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212 },
- { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693 },
- { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002 },
- { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971 },
- { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722 },
- { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671 },
- { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807 },
- { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872 },
- { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371 },
- { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333 },
- { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120 },
- { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991 },
- { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227 },
- { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056 },
- { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189 },
- { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912 },
- { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160 },
- { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233 },
- { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635 },
- { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079 },
- { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049 },
- { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638 },
- { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834 },
- { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925 },
- { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071 },
- { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504 },
- { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702 },
- { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535 },
- { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582 },
- { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963 },
- { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175 },
-]
-
-[[package]]
-name = "pillow"
-version = "11.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800 },
- { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296 },
- { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726 },
- { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652 },
- { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787 },
- { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236 },
- { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950 },
- { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358 },
- { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079 },
- { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324 },
- { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067 },
- { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328 },
- { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652 },
- { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443 },
- { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474 },
- { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038 },
- { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407 },
- { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094 },
- { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503 },
- { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574 },
- { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060 },
- { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407 },
- { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841 },
- { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450 },
- { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055 },
- { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110 },
- { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547 },
- { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554 },
- { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132 },
- { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001 },
- { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814 },
- { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124 },
- { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186 },
- { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546 },
- { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102 },
- { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803 },
- { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520 },
- { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116 },
- { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597 },
- { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246 },
- { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336 },
- { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699 },
- { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789 },
- { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386 },
- { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911 },
- { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383 },
- { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385 },
- { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129 },
- { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580 },
- { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860 },
- { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694 },
- { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888 },
- { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330 },
- { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089 },
- { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206 },
- { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370 },
- { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500 },
- { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835 },
-]
-
[[package]]
name = "ply"
version = "3.11"
@@ -2164,6 +2073,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/de/84/586422d8861b5391c8414360b10f603c0b7859bb09ad688e64430ed0df7b/posthog-6.7.6-py3-none-any.whl", hash = "sha256:b09a7e65a042ec416c28874b397d3accae412a80a8b0ef3fa686fbffc99e4d4b", size = 137348 },
]
+[[package]]
+name = "powerfx"
+version = "0.0.34"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+ { name = "pythonnet" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9f/fb/6c4bf87e0c74ca1c563921ce89ca1c5785b7576bca932f7255cdf81082a7/powerfx-0.0.34.tar.gz", hash = "sha256:956992e7afd272657ed16d80f4cad24ec95d9e4a79fb9dfa4a068a09e136af32", size = 3237555 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6f/96/0f8a1f86485b3ec0315e3e8403326884a0334b3dcd699df2482669cca4be/powerfx-0.0.34-py3-none-any.whl", hash = "sha256:f2dc1c42ba8bfa4c72a7fcff2a00755b95394547388ca0b3e36579c49ee7ed75", size = 3483089 },
+]
+
[[package]]
name = "propcache"
version = "0.4.1"
@@ -2274,51 +2196,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/cc/7e77861000a0691aeea8f4566e5d3aa716f2b1dece4a24439437e41d3d25/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5", size = 172823 },
]
-[[package]]
-name = "psutil"
-version = "7.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b3/31/4723d756b59344b643542936e37a31d1d3204bcdc42a7daa8ee9eb06fb50/psutil-7.1.0.tar.gz", hash = "sha256:655708b3c069387c8b77b072fc429a57d0e214221d01c0a772df7dfedcb3bcd2", size = 497660 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/46/62/ce4051019ee20ce0ed74432dd73a5bb087a6704284a470bb8adff69a0932/psutil-7.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76168cef4397494250e9f4e73eb3752b146de1dd950040b29186d0cce1d5ca13", size = 245242 },
- { url = "https://files.pythonhosted.org/packages/38/61/f76959fba841bf5b61123fbf4b650886dc4094c6858008b5bf73d9057216/psutil-7.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:5d007560c8c372efdff9e4579c2846d71de737e4605f611437255e81efcca2c5", size = 246682 },
- { url = "https://files.pythonhosted.org/packages/88/7a/37c99d2e77ec30d63398ffa6a660450b8a62517cabe44b3e9bae97696e8d/psutil-7.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22e4454970b32472ce7deaa45d045b34d3648ce478e26a04c7e858a0a6e75ff3", size = 287994 },
- { url = "https://files.pythonhosted.org/packages/9d/de/04c8c61232f7244aa0a4b9a9fbd63a89d5aeaf94b2fc9d1d16e2faa5cbb0/psutil-7.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c70e113920d51e89f212dd7be06219a9b88014e63a4cec69b684c327bc474e3", size = 291163 },
- { url = "https://files.pythonhosted.org/packages/f4/58/c4f976234bf6d4737bc8c02a81192f045c307b72cf39c9e5c5a2d78927f6/psutil-7.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d4a113425c037300de3ac8b331637293da9be9713855c4fc9d2d97436d7259d", size = 293625 },
- { url = "https://files.pythonhosted.org/packages/79/87/157c8e7959ec39ced1b11cc93c730c4fb7f9d408569a6c59dbd92ceb35db/psutil-7.1.0-cp37-abi3-win32.whl", hash = "sha256:09ad740870c8d219ed8daae0ad3b726d3bf9a028a198e7f3080f6a1888b99bca", size = 244812 },
- { url = "https://files.pythonhosted.org/packages/bf/e9/b44c4f697276a7a95b8e94d0e320a7bf7f3318521b23de69035540b39838/psutil-7.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:57f5e987c36d3146c0dd2528cd42151cf96cd359b9d67cfff836995cc5df9a3d", size = 247965 },
- { url = "https://files.pythonhosted.org/packages/26/65/1070a6e3c036f39142c2820c4b52e9243246fcfc3f96239ac84472ba361e/psutil-7.1.0-cp37-abi3-win_arm64.whl", hash = "sha256:6937cb68133e7c97b6cc9649a570c9a18ba0efebed46d8c5dae4c07fa1b67a07", size = 244971 },
-]
-
-[[package]]
-name = "pyarrow"
-version = "21.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ef/c2/ea068b8f00905c06329a3dfcd40d0fcc2b7d0f2e355bdb25b65e0a0e4cd4/pyarrow-21.0.0.tar.gz", hash = "sha256:5051f2dccf0e283ff56335760cbc8622cf52264d67e359d5569541ac11b6d5bc", size = 1133487 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ca/d4/d4f817b21aacc30195cf6a46ba041dd1be827efa4a623cc8bf39a1c2a0c0/pyarrow-21.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3a302f0e0963db37e0a24a70c56cf91a4faa0bca51c23812279ca2e23481fccd", size = 31160305 },
- { url = "https://files.pythonhosted.org/packages/a2/9c/dcd38ce6e4b4d9a19e1d36914cb8e2b1da4e6003dd075474c4cfcdfe0601/pyarrow-21.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:b6b27cf01e243871390474a211a7922bfbe3bda21e39bc9160daf0da3fe48876", size = 32684264 },
- { url = "https://files.pythonhosted.org/packages/4f/74/2a2d9f8d7a59b639523454bec12dba35ae3d0a07d8ab529dc0809f74b23c/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e72a8ec6b868e258a2cd2672d91f2860ad532d590ce94cdf7d5e7ec674ccf03d", size = 41108099 },
- { url = "https://files.pythonhosted.org/packages/ad/90/2660332eeb31303c13b653ea566a9918484b6e4d6b9d2d46879a33ab0622/pyarrow-21.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b7ae0bbdc8c6674259b25bef5d2a1d6af5d39d7200c819cf99e07f7dfef1c51e", size = 42829529 },
- { url = "https://files.pythonhosted.org/packages/33/27/1a93a25c92717f6aa0fca06eb4700860577d016cd3ae51aad0e0488ac899/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:58c30a1729f82d201627c173d91bd431db88ea74dcaa3885855bc6203e433b82", size = 43367883 },
- { url = "https://files.pythonhosted.org/packages/05/d9/4d09d919f35d599bc05c6950095e358c3e15148ead26292dfca1fb659b0c/pyarrow-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:072116f65604b822a7f22945a7a6e581cfa28e3454fdcc6939d4ff6090126623", size = 45133802 },
- { url = "https://files.pythonhosted.org/packages/71/30/f3795b6e192c3ab881325ffe172e526499eb3780e306a15103a2764916a2/pyarrow-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf56ec8b0a5c8c9d7021d6fd754e688104f9ebebf1bf4449613c9531f5346a18", size = 26203175 },
- { url = "https://files.pythonhosted.org/packages/16/ca/c7eaa8e62db8fb37ce942b1ea0c6d7abfe3786ca193957afa25e71b81b66/pyarrow-21.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e99310a4ebd4479bcd1964dff9e14af33746300cb014aa4a3781738ac63baf4a", size = 31154306 },
- { url = "https://files.pythonhosted.org/packages/ce/e8/e87d9e3b2489302b3a1aea709aaca4b781c5252fcb812a17ab6275a9a484/pyarrow-21.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d2fe8e7f3ce329a71b7ddd7498b3cfac0eeb200c2789bd840234f0dc271a8efe", size = 32680622 },
- { url = "https://files.pythonhosted.org/packages/84/52/79095d73a742aa0aba370c7942b1b655f598069489ab387fe47261a849e1/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f522e5709379d72fb3da7785aa489ff0bb87448a9dc5a75f45763a795a089ebd", size = 41104094 },
- { url = "https://files.pythonhosted.org/packages/89/4b/7782438b551dbb0468892a276b8c789b8bbdb25ea5c5eb27faadd753e037/pyarrow-21.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:69cbbdf0631396e9925e048cfa5bce4e8c3d3b41562bbd70c685a8eb53a91e61", size = 42825576 },
- { url = "https://files.pythonhosted.org/packages/b3/62/0f29de6e0a1e33518dec92c65be0351d32d7ca351e51ec5f4f837a9aab91/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:731c7022587006b755d0bdb27626a1a3bb004bb56b11fb30d98b6c1b4718579d", size = 43368342 },
- { url = "https://files.pythonhosted.org/packages/90/c7/0fa1f3f29cf75f339768cc698c8ad4ddd2481c1742e9741459911c9ac477/pyarrow-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc56bc708f2d8ac71bd1dcb927e458c93cec10b98eb4120206a4091db7b67b99", size = 45131218 },
- { url = "https://files.pythonhosted.org/packages/01/63/581f2076465e67b23bc5a37d4a2abff8362d389d29d8105832e82c9c811c/pyarrow-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:186aa00bca62139f75b7de8420f745f2af12941595bbbfa7ed3870ff63e25636", size = 26087551 },
- { url = "https://files.pythonhosted.org/packages/c9/ab/357d0d9648bb8241ee7348e564f2479d206ebe6e1c47ac5027c2e31ecd39/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:a7a102574faa3f421141a64c10216e078df467ab9576684d5cd696952546e2da", size = 31290064 },
- { url = "https://files.pythonhosted.org/packages/3f/8a/5685d62a990e4cac2043fc76b4661bf38d06efed55cf45a334b455bd2759/pyarrow-21.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:1e005378c4a2c6db3ada3ad4c217b381f6c886f0a80d6a316fe586b90f77efd7", size = 32727837 },
- { url = "https://files.pythonhosted.org/packages/fc/de/c0828ee09525c2bafefd3e736a248ebe764d07d0fd762d4f0929dbc516c9/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:65f8e85f79031449ec8706b74504a316805217b35b6099155dd7e227eef0d4b6", size = 41014158 },
- { url = "https://files.pythonhosted.org/packages/6e/26/a2865c420c50b7a3748320b614f3484bfcde8347b2639b2b903b21ce6a72/pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3a81486adc665c7eb1a2bde0224cfca6ceaba344a82a971ef059678417880eb8", size = 42667885 },
- { url = "https://files.pythonhosted.org/packages/0a/f9/4ee798dc902533159250fb4321267730bc0a107d8c6889e07c3add4fe3a5/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fc0d2f88b81dcf3ccf9a6ae17f89183762c8a94a5bdcfa09e05cfe413acf0503", size = 43276625 },
- { url = "https://files.pythonhosted.org/packages/5a/da/e02544d6997037a4b0d22d8e5f66bc9315c3671371a8b18c79ade1cefe14/pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6299449adf89df38537837487a4f8d3bd91ec94354fdd2a7d30bc11c48ef6e79", size = 44951890 },
- { url = "https://files.pythonhosted.org/packages/e5/4e/519c1bc1876625fe6b71e9a28287c43ec2f20f73c658b9ae1d485c0c206e/pyarrow-21.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:222c39e2c70113543982c6b34f3077962b44fca38c0bd9e68bb6781534425c10", size = 26371006 },
-]
-
[[package]]
name = "pyasn1"
version = "0.6.1"
@@ -2420,19 +2297,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608 },
]
-[[package]]
-name = "pydeck"
-version = "0.9.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "jinja2" },
- { name = "numpy" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 },
-]
-
[[package]]
name = "pygments"
version = "2.19.2"
@@ -2495,6 +2359,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/6c/a0/4ed6632b70a52de845df056654162acdebaf97c20e3212c559ac43e7216e/python_ulid-3.1.0-py3-none-any.whl", hash = "sha256:e2cdc979c8c877029b4b7a38a6fba3bc4578e4f109a308419ff4d3ccf0a46619", size = 11577 },
]
+[[package]]
+name = "pythonnet"
+version = "3.0.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "clr-loader" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9a/d6/1afd75edd932306ae9bd2c2d961d603dc2b52fcec51b04afea464f1f6646/pythonnet-3.0.5.tar.gz", hash = "sha256:48e43ca463941b3608b32b4e236db92d8d40db4c58a75ace902985f76dac21cf", size = 239212 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cd/f1/bfb6811df4745f92f14c47a29e50e89a36b1533130fcc56452d4660bd2d6/pythonnet-3.0.5-py3-none-any.whl", hash = "sha256:f6702d694d5d5b163c9f3f5cc34e0bed8d6857150237fae411fefb883a656d20", size = 297506 },
+]
+
[[package]]
name = "pytz"
version = "2025.2"
@@ -2641,19 +2517,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847 },
]
-[[package]]
-name = "requests-oauthlib"
-version = "2.0.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "oauthlib" },
- { name = "requests" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179 },
-]
-
[[package]]
name = "rich"
version = "14.1.0"
@@ -2778,15 +2641,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
]
-[[package]]
-name = "smmap"
-version = "5.0.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 },
-]
-
[[package]]
name = "sniffio"
version = "1.3.1"
@@ -2849,35 +2703,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037 },
]
-[[package]]
-name = "streamlit"
-version = "1.45.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "altair" },
- { name = "blinker" },
- { name = "cachetools" },
- { name = "click" },
- { name = "gitpython" },
- { name = "numpy" },
- { name = "packaging" },
- { name = "pandas" },
- { name = "pillow" },
- { name = "protobuf" },
- { name = "pyarrow" },
- { name = "pydeck" },
- { name = "requests" },
- { name = "tenacity" },
- { name = "toml" },
- { name = "tornado" },
- { name = "typing-extensions" },
- { name = "watchdog", marker = "sys_platform != 'darwin'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c0/52/83d46b4c5044477e9c6dee779704a9d4041610439533af9c0a5084921098/streamlit-1.45.0.tar.gz", hash = "sha256:4e99014e113a11a7163b9da5ac079efb1ae5f8575a09c5a6a9c43cd6877a2a88", size = 9462166 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/43/ff/f41cfaf1bb58223fe77ff87213a689f6c9c82f7363f9d7c879d294dbe985/streamlit-1.45.0-py3-none-any.whl", hash = "sha256:b7d03ec68a23de0f1922ec9a28fbe3fe37d9fb31ad31d6c429d262c3631c2943", size = 9856265 },
-]
-
[[package]]
name = "tenacity"
version = "8.5.0"
@@ -2887,34 +2712,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/3f/8ba87d9e287b9d385a02a7114ddcef61b26f86411e121c9003eb509a1773/tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687", size = 28165 },
]
-[[package]]
-name = "toml"
-version = "0.10.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 },
-]
-
-[[package]]
-name = "tornado"
-version = "6.5.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563 },
- { url = "https://files.pythonhosted.org/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729 },
- { url = "https://files.pythonhosted.org/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295 },
- { url = "https://files.pythonhosted.org/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644 },
- { url = "https://files.pythonhosted.org/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878 },
- { url = "https://files.pythonhosted.org/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549 },
- { url = "https://files.pythonhosted.org/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973 },
- { url = "https://files.pythonhosted.org/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954 },
- { url = "https://files.pythonhosted.org/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023 },
- { url = "https://files.pythonhosted.org/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427 },
- { url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456 },
-]
-
[[package]]
name = "tqdm"
version = "4.67.1"
@@ -2942,6 +2739,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/00/22/35617eee79080a5d071d0f14ad698d325ee6b3bf824fc0467c03b30e7fa8/typer-0.19.2-py3-none-any.whl", hash = "sha256:755e7e19670ffad8283db353267cb81ef252f595aa6834a0d1ca9312d9326cb9", size = 46748 },
]
+[[package]]
+name = "types-requests"
+version = "2.32.4.20260107"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0f/f3/a0663907082280664d745929205a89d41dffb29e89a50f753af7d57d0a96/types_requests-2.32.4.20260107.tar.gz", hash = "sha256:018a11ac158f801bfa84857ddec1650750e393df8a004a8a9ae2a9bec6fcb24f", size = 23165 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1c/12/709ea261f2bf91ef0a26a9eed20f2623227a8ed85610c1e54c5805692ecb/types_requests-2.32.4.20260107-py3-none-any.whl", hash = "sha256:b703fe72f8ce5b31ef031264fe9395cac8f46a04661a79f7ed31a80fb308730d", size = 20676 },
+]
+
[[package]]
name = "typing-extensions"
version = "4.15.0"
@@ -2963,15 +2772,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 },
]
-[[package]]
-name = "tzdata"
-version = "2025.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 },
-]
-
[[package]]
name = "urllib3"
version = "2.5.0"
@@ -3025,24 +2825,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018 },
]
-[[package]]
-name = "watchdog"
-version = "6.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 },
- { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 },
- { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 },
- { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 },
- { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 },
- { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 },
- { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 },
- { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 },
- { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 },
- { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 },
-]
-
[[package]]
name = "watchfiles"
version = "1.1.0"
@@ -3153,55 +2935,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498 },
]
-[[package]]
-name = "wrapt"
-version = "1.17.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998 },
- { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020 },
- { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098 },
- { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036 },
- { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156 },
- { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102 },
- { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732 },
- { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705 },
- { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877 },
- { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885 },
- { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003 },
- { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025 },
- { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108 },
- { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072 },
- { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214 },
- { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105 },
- { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766 },
- { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711 },
- { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885 },
- { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896 },
- { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132 },
- { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091 },
- { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172 },
- { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163 },
- { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963 },
- { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945 },
- { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857 },
- { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178 },
- { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310 },
- { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266 },
- { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544 },
- { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283 },
- { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366 },
- { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571 },
- { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094 },
- { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659 },
- { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946 },
- { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717 },
- { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334 },
- { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471 },
- { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591 },
-]
-
[[package]]
name = "yarl"
version = "1.22.0"
diff --git a/agentic_ai/workflow/fraud_detection_durable/.env.sample b/agentic_ai/workflow/fraud_detection_durable/.env.sample
new file mode 100644
index 000000000..2b838dc77
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/.env.sample
@@ -0,0 +1,22 @@
+# Azure OpenAI Configuration
+AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
+AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-4o
+AZURE_OPENAI_API_VERSION=2024-10-01-preview
+# Optional: Use API key instead of Azure CLI auth
+# AZURE_OPENAI_API_KEY=your-api-key
+
+# MCP Server
+MCP_SERVER_URI=http://localhost:8000/mcp
+
+# Durable Task Scheduler
+# Local emulator (default)
+DTS_ENDPOINT=http://localhost:8080
+DTS_TASKHUB=fraud-detection
+
+# Azure-hosted (production)
+# DTS_ENDPOINT=https://your-dts-endpoint.azure.com
+# DTS_TASKHUB=fraud-detection-prod
+
+# Human-in-the-loop Configuration
+ANALYST_APPROVAL_TIMEOUT_HOURS=72
+MAX_REVIEW_ATTEMPTS=3
diff --git a/agentic_ai/workflow/fraud_detection_durable/README.md b/agentic_ai/workflow/fraud_detection_durable/README.md
new file mode 100644
index 000000000..5aa5c7753
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/README.md
@@ -0,0 +1,360 @@
+# Durable Fraud Detection Workflow
+
+A hybrid architecture combining **Workflow** (complex topology) and **Durable Task** (durability, HITL) for enterprise-grade fraud detection.
+
+## 🏗️ Architecture
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ DURABLE TASK ORCHESTRATION (Outer Layer) │
+│ Handles: Durability, Long Waits, Crash Recovery │
+│ │
+│ ┌─────────────────────────────────────────────────────────────────┐ │
+│ │ 1. Receive Alert │ │
+│ │ 2. Call "run_fraud_analysis" Activity ──────────────────────┐ │ │
+│ │ │ │ │
+│ │ ┌─────────────────────────────────────────────────────┐ │ │ │
+│ │ │ WORKFLOW (Inner Layer - Activity) │ │ │ │
+│ │ │ Handles: Complex Topology, Fast Execution │ │ │ │
+│ │ │ │ │ │ │
+│ │ │ AlertRouter │ │ │ │
+│ │ │ ↓ │ │ │ │
+│ │ │ ┌───┴───┬───────┐ (fan-out) │ │ │ │
+│ │ │ ↓ ↓ ↓ │ │ │ │
+│ │ │ Usage Location Billing │ │ │ │
+│ │ │ └───────┼───────┘ (fan-in) │ │ │ │
+│ │ │ ↓ │ │ │ │
+│ │ │ Aggregator (LLM) │ │ │ │
+│ │ │ ↓ │ │ │ │
+│ │ │ Returns: FraudRiskAssessment │ │ │ │
+│ │ └─────────────────────────────────────────────────────┘ │ │ │
+│ │ ◄─────┘ │ │
+│ │ 3. Check Risk Score (simple if/else) │ │
+│ │ │ │
+│ │ IF risk >= 0.6: │ │
+│ │ → notify_analyst Activity │ │
+│ │ → wait_for_external_event("AnalystDecision") ⏸️ │ │
+│ │ → execute_fraud_action Activity │ │
+│ │ │ │
+│ │ ELSE: │ │
+│ │ → auto_clear Activity │ │
+│ │ │ │
+│ │ 4. send_notification Activity │ │
+│ └─────────────────────────────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────────────────────┘
+```
+
+## 🎯 Why Hybrid Architecture?
+
+| Feature | Workflow Only | Durable Task Only | **Hybrid (This)** |
+|---------|--------------|-------------------|-------------------|
+| Complex topology (fan-out/fan-in) | ✅ Easy | ❌ Manual | ✅ Easy |
+| Crash recovery | ❌ Lost state | ✅ Automatic | ✅ Automatic |
+| Human-in-the-loop | ⚠️ Manual checkpoints | ✅ Built-in events | ✅ Built-in events |
+| Timeout handling | ❌ Not built-in | ✅ Native timers | ✅ Native timers |
+| Long waits (hours/days) | ❌ Memory-bound | ✅ Persistent | ✅ Persistent |
+| Visibility/Dashboard | ❌ Custom logging | ✅ DTS Dashboard | ✅ DTS Dashboard |
+
+## 📁 Project Structure
+
+```
+fraud_detection_durable/
+├── pyproject.toml # Dependencies
+├── .env.sample # Environment template
+├── fraud_analysis_workflow.py # Inner workflow (fan-out → aggregate)
+├── worker.py # DTS Worker with orchestration
+├── client.py # CLI client for testing
+├── backend.py # FastAPI backend for UI
+├── README.md # This file
+└── ui/ # React UI
+ ├── src/
+ │ ├── App.jsx # Main app with WebSocket connection
+ │ └── components/
+ │ └── WorkflowVisualizer.jsx # Interactive workflow diagram
+ └── package.json
+```
+
+## 🚀 Quick Start
+
+### Prerequisites
+
+1. **Docker** - For DTS emulator
+2. **Python 3.12+**
+3. **Azure OpenAI** - With a deployed model
+4. **MCP Server** - Running on port 8000
+
+### Step 1: Start Durable Task Scheduler
+
+```bash
+docker run -d --name dts-emulator \
+ -p 8080:8080 -p 8082:8082 \
+ mcr.microsoft.com/dts/dts-emulator:latest
+```
+
+Dashboard: http://localhost:8082
+
+### Step 2: Start MCP Server
+
+```bash
+cd mcp
+uv run mcp_service.py
+```
+
+### Step 3: Configure Environment
+
+```bash
+cd agentic_ai/workflow/fraud_detection_durable
+cp .env.sample .env
+# Edit .env with your Azure OpenAI credentials
+```
+
+### Step 4: Install Dependencies
+
+```bash
+uv sync
+```
+
+### Step 5: Start Worker
+
+```bash
+uv run worker.py
+```
+
+### Step 6: Run Tests
+
+**Option A: CLI Client**
+```bash
+uv run client.py
+```
+
+**Option B: FastAPI Backend + React UI**
+```bash
+# Terminal 1: Backend
+uv run backend.py
+
+# Terminal 2: React UI
+cd ui
+npm install
+npm run dev
+```
+
+Open http://localhost:5173 to view the interactive workflow UI.
+
+## 🧪 Test Scenarios
+
+### 1. High-Risk Alert with Analyst Approval
+
+```
+Alert: ALERT-001 (multi_country_login, high severity)
+ ↓
+Workflow: Fan-out to 3 specialists
+ ↓
+Risk Score: 0.75 (HIGH RISK)
+ ↓
+⏸️ Waiting for analyst decision...
+ ↓
+Analyst: "lock_account"
+ ↓
+✅ Account locked, notification sent
+```
+
+### 2. Low-Risk Alert with Auto-Clear
+
+```
+Alert: ALERT-002 (data_spike, low severity)
+ ↓
+Workflow: Fan-out to 3 specialists
+ ↓
+Risk Score: 0.35 (LOW RISK)
+ ↓
+✅ Auto-cleared, notification sent
+```
+
+### 3. Timeout Escalation
+
+```
+Alert: ALERT-003 (unusual_charges, high severity)
+ ↓
+Workflow: Fan-out to 3 specialists
+ ↓
+Risk Score: 0.80 (CRITICAL)
+ ↓
+⏸️ Waiting for analyst decision...
+ ↓
+⏰ Timeout (72 hours)
+ ↓
+⚠️ Escalated to manager
+```
+
+## �️ React UI Features
+
+The interactive React UI provides real-time visualization of the fraud detection workflow:
+
+### Interactive Workflow Diagram
+
+- **Real-time Status Updates**: Nodes change color based on execution state
+ - Gray: Pending
+ - Blue: Running (with pulse animation)
+ - Green: Completed
+ - Red: Failed
+
+- **Clickable Nodes**: Click any workflow step to see detailed execution info:
+ - **Tool Calls**: Actual MCP tool calls made (e.g., `get_billing_summary`, `get_data_usage`)
+ - **Arguments**: Parameters passed to each tool
+ - **Results**: Output returned from each tool call
+ - **Step Output**: Final output from the agent step
+
+### Human-in-the-Loop Panel
+
+When the workflow reaches the Review Gateway (risk ≥ 0.6):
+- Shows analyst decision options: `lock_account`, `flag_review`, `dismiss`
+- Displays risk assessment details
+- Allows analyst to submit decision via UI
+
+### Example: Viewing Step Details
+
+```
+1. Click on "Usage Analyst" node
+2. Popover shows:
+ - Tool Calls (Real):
+ • get_data_usage(subscription_id=5, start_date="2025-12-01", ...)
+ → Result: {"total_gb": 45.2, "daily_avg": 1.5, ...}
+ • get_billing_summary(customer_id=3)
+ → Result: {"current_balance": 150.00, ...}
+ - Output: "Usage analysis indicates 300% spike in data..."
+```
+
+## �📊 DTS Dashboard
+
+Open http://localhost:8082 to see:
+
+- All orchestration instances
+- Pending external events (analyst decisions)
+- Activity execution logs
+- Orchestration timeline and status
+
+
+
+## 🔧 Configuration
+
+### Environment Variables
+
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `AZURE_OPENAI_ENDPOINT` | Azure OpenAI endpoint | Required |
+| `AZURE_OPENAI_CHAT_DEPLOYMENT` | Deployment name | `gpt-4o` |
+| `MCP_SERVER_URI` | MCP server URL | `http://localhost:8000/mcp` |
+| `DTS_ENDPOINT` | DTS scheduler URL | `http://localhost:8080` |
+| `DTS_TASKHUB` | DTS task hub name | `fraud-detection` |
+| `ANALYST_APPROVAL_TIMEOUT_HOURS` | Timeout for analyst review | `72` |
+
+### Risk Threshold
+
+Edit `worker.py` to change the risk threshold:
+
+```python
+# Current: 0.6 (60%)
+if risk_score >= 0.6:
+ # High risk path
+else:
+ # Low risk path
+```
+
+## 🏛️ Key Components
+
+### 1. Inner Workflow (`fraud_analysis_workflow.py`)
+
+The workflow handles complex multi-agent topology:
+
+```python
+# Fan-out: Alert → 3 Specialists
+builder.add_edge(alert_router, usage_executor)
+builder.add_edge(alert_router, location_executor)
+builder.add_edge(alert_router, billing_executor)
+
+# Fan-in: 3 Specialists → Aggregator
+builder.add_fan_in_edge(
+ [usage_executor, location_executor, billing_executor],
+ aggregator
+)
+```
+
+### 2. DTS Orchestration (`worker.py`)
+
+The orchestration handles durability and HITL:
+
+```python
+def fraud_detection_orchestration(context, payload):
+ # Run inner workflow as activity
+ assessment = yield context.call_activity("run_fraud_analysis", alert)
+
+ if assessment["risk_score"] >= 0.6:
+ # Wait for analyst with timeout
+ approval_task = context.wait_for_external_event("AnalystDecision")
+ timeout_task = context.create_timer(timedelta(hours=72))
+
+ winner = yield when_any([approval_task, timeout_task])
+
+ if winner == approval_task:
+ yield context.call_activity("execute_fraud_action", decision)
+ else:
+ yield context.call_activity("escalate_timeout", assessment)
+ else:
+ yield context.call_activity("auto_clear_alert", assessment)
+```
+
+### 3. Activities
+
+| Activity | Purpose |
+|----------|---------|
+| `run_fraud_analysis` | Runs inner workflow, returns assessment |
+| `notify_analyst` | Sends notification for review |
+| `execute_fraud_action` | Executes approved action |
+| `auto_clear_alert` | Auto-clears low-risk alerts |
+| `escalate_timeout` | Escalates on timeout |
+| `send_notification` | Sends final notification |
+
+## 🔄 Comparison with Original Implementation
+
+| Aspect | Original (`fraud_detection/`) | Durable (`fraud_detection_durable/`) |
+|--------|-------------------------------|-------------------------------------|
+| HITL Pattern | `ctx.request_info()` + `@response_handler` | `wait_for_external_event()` |
+| Checkpointing | `FileCheckpointStorage` (manual) | DTS (automatic) |
+| Timeout | Not built-in | Native `create_timer()` |
+| Recovery | Load checkpoint manually | Automatic replay |
+| Dashboard | Custom logging | DTS Dashboard |
+| Topology | Full workflow | Workflow as activity |
+
+## 🐛 Troubleshooting
+
+### "Cannot connect to DTS"
+
+```bash
+# Check if DTS is running
+docker ps | grep dts
+
+# Restart if needed
+docker restart dts-emulator
+```
+
+### "Worker not processing"
+
+1. Check worker is running: `uv run worker.py`
+2. Check logs for errors
+3. Verify DTS endpoint in `.env`
+
+### "Analyst decision not received"
+
+1. Check instance ID matches
+2. Verify event name is `AnalystDecision`
+3. Check DTS dashboard for pending events
+
+## 📚 Related Documentation
+
+- [Agent Framework Workflow](../human-in-the-loop.md)
+- [Durable Task Samples](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/durabletask)
+- [Original Fraud Detection](../fraud_detection/README.md)
+
+## 📜 License
+
+Copyright (c) Microsoft. All rights reserved.
diff --git a/agentic_ai/workflow/fraud_detection_durable/backend.py b/agentic_ai/workflow/fraud_detection_durable/backend.py
new file mode 100644
index 000000000..0b7fd926d
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/backend.py
@@ -0,0 +1,507 @@
+"""
+FastAPI Backend for Durable Fraud Detection.
+
+This backend provides:
+1. REST API to start orchestrations and submit decisions
+2. WebSocket for real-time status updates
+3. Integration with DTS client
+
+The UI connects here instead of managing workflow directly.
+"""
+
+import asyncio
+import json
+import logging
+import os
+import time
+from datetime import datetime
+from typing import Any
+
+from azure.identity import DefaultAzureCredential
+from dotenv import load_dotenv
+from durabletask.azuremanaged.client import DurableTaskSchedulerClient
+from durabletask.client import OrchestrationState
+from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException
+from fastapi.middleware.cors import CORSMiddleware
+from pydantic import BaseModel
+
+# Load environment
+load_dotenv()
+
+# Configure logging
+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+logger = logging.getLogger(__name__)
+
+# FastAPI app
+app = FastAPI(
+ title="Durable Fraud Detection API",
+ description="Hybrid Workflow + Durable Task architecture for fraud detection",
+ version="1.0.0",
+)
+
+# CORS
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["http://localhost:3000", "http://localhost:5173"],
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+# Constants
+ANALYST_APPROVAL_EVENT = "AnalystDecision"
+ORCHESTRATION_NAME = "fraud_detection_orchestration"
+
+
+# ============================================================================
+# Request/Response Models
+# ============================================================================
+
+
+class StartWorkflowRequest(BaseModel):
+ """Request to start a fraud detection workflow."""
+ alert_id: str
+ customer_id: int
+ alert_type: str
+ description: str = ""
+ severity: str = "medium"
+ approval_timeout_hours: float = 72.0
+
+
+class StartWorkflowResponse(BaseModel):
+ """Response after starting a workflow."""
+ instance_id: str
+ alert_id: str
+ status: str
+
+
+class AnalystDecisionRequest(BaseModel):
+ """Analyst decision submitted from UI."""
+ instance_id: str
+ alert_id: str
+ approved_action: str
+ analyst_notes: str = ""
+ analyst_id: str = "analyst_ui"
+
+
+class WorkflowStatusResponse(BaseModel):
+ """Workflow status response."""
+ instance_id: str
+ status: str
+ custom_status: str | None
+ result: dict | None
+
+
+class AlertInfo(BaseModel):
+ """Sample alert info."""
+ alert_id: str
+ customer_id: int
+ alert_type: str
+ description: str
+ severity: str
+
+
+# ============================================================================
+# Sample Alerts
+# ============================================================================
+
+SAMPLE_ALERTS = [
+ AlertInfo(
+ alert_id="ALERT-001",
+ customer_id=1,
+ alert_type="multi_country_login",
+ description="Login attempts from USA and Russia within 2 hours",
+ severity="high",
+ ),
+ AlertInfo(
+ alert_id="ALERT-002",
+ customer_id=2,
+ alert_type="data_spike",
+ description="Data usage increased by 500% in last 24 hours",
+ severity="medium",
+ ),
+ AlertInfo(
+ alert_id="ALERT-003",
+ customer_id=3,
+ alert_type="unusual_charges",
+ description="Three large purchases totaling $5,000 in 10 minutes",
+ severity="high",
+ ),
+]
+
+
+# ============================================================================
+# DTS Client
+# ============================================================================
+
+_dts_client: DurableTaskSchedulerClient | None = None
+
+
+def get_dts_client() -> DurableTaskSchedulerClient:
+ """Get or create DTS client."""
+ global _dts_client
+
+ if _dts_client is None:
+ taskhub = os.getenv("DTS_TASKHUB", "default")
+ endpoint = os.getenv("DTS_ENDPOINT", "http://localhost:8080")
+
+ credential = None if endpoint.startswith("http://localhost") else DefaultAzureCredential()
+
+ _dts_client = DurableTaskSchedulerClient(
+ host_address=endpoint,
+ secure_channel=not endpoint.startswith("http://localhost"),
+ taskhub=taskhub,
+ token_credential=credential,
+ )
+ logger.info(f"DTS client initialized: {endpoint}/{taskhub}")
+
+ return _dts_client
+
+
+# ============================================================================
+# WebSocket Manager
+# ============================================================================
+
+
+class ConnectionManager:
+ """Manages WebSocket connections for real-time updates."""
+
+ def __init__(self):
+ self.active_connections: dict[str, list[WebSocket]] = {} # instance_id -> connections
+
+ async def connect(self, websocket: WebSocket, instance_id: str):
+ await websocket.accept()
+ if instance_id not in self.active_connections:
+ self.active_connections[instance_id] = []
+ self.active_connections[instance_id].append(websocket)
+ logger.info(f"WebSocket connected for instance {instance_id}")
+
+ def disconnect(self, websocket: WebSocket, instance_id: str):
+ if instance_id in self.active_connections:
+ self.active_connections[instance_id].remove(websocket)
+ if not self.active_connections[instance_id]:
+ del self.active_connections[instance_id]
+ logger.info(f"WebSocket disconnected for instance {instance_id}")
+
+ async def broadcast(self, instance_id: str, message: dict):
+ """Broadcast message to all connections watching this instance."""
+ if instance_id in self.active_connections:
+ disconnected = []
+ for connection in self.active_connections[instance_id]:
+ try:
+ await connection.send_json(message)
+ except Exception:
+ disconnected.append(connection)
+
+ for conn in disconnected:
+ self.disconnect(conn, instance_id)
+
+
+manager = ConnectionManager()
+
+
+# ============================================================================
+# Background Task: Poll DTS for Status Updates
+# ============================================================================
+
+_polling_tasks: dict[str, asyncio.Task] = {}
+
+
+async def poll_orchestration_status(instance_id: str):
+ """Background task to poll DTS and broadcast status updates."""
+ client = get_dts_client()
+ last_status = None
+ last_custom_status = None
+
+ while instance_id in manager.active_connections:
+ try:
+ state = client.get_orchestration_state(instance_id)
+
+ if state:
+ status = state.runtime_status.name
+ custom_status_raw = state.serialized_custom_status
+
+ # Debug: log raw custom_status
+ # logger.info(f"[Backend] Raw custom_status: {custom_status_raw[:200] if custom_status_raw else 'None'}")
+
+ # Parse custom_status JSON if present
+ custom_status = custom_status_raw
+ step_details = None
+ status_message = custom_status_raw
+
+ if custom_status_raw:
+ try:
+ parsed = json.loads(custom_status_raw)
+ # Handle double-encoding: if parsed is still a string, parse again
+ if isinstance(parsed, str):
+ try:
+ parsed = json.loads(parsed)
+ except json.JSONDecodeError:
+ pass # Keep as string
+
+ if isinstance(parsed, dict):
+ status_message = parsed.get("message", custom_status_raw)
+ step_details = parsed.get("step_details")
+ custom_status = status_message
+ except json.JSONDecodeError:
+ pass # Keep original string
+
+ # Only broadcast if status changed
+ if status != last_status or custom_status_raw != last_custom_status:
+ message = {
+ "type": "status_update",
+ "instance_id": instance_id,
+ "status": status,
+ "custom_status": custom_status,
+ "step_details": step_details,
+ "timestamp": datetime.now().isoformat(),
+ }
+
+ # Check if waiting for analyst
+ if custom_status and "Awaiting analyst" in custom_status:
+ message["decision_required"] = True
+
+ # Check if completed
+ if status in ("COMPLETED", "FAILED", "TERMINATED"):
+ if state.serialized_output:
+ try:
+ message["result"] = json.loads(state.serialized_output)
+ except json.JSONDecodeError:
+ message["result"] = {"raw": state.serialized_output}
+
+ await manager.broadcast(instance_id, message)
+
+ last_status = status
+ last_custom_status = custom_status_raw
+
+ # Stop polling if completed
+ if status in ("COMPLETED", "FAILED", "TERMINATED"):
+ logger.info(f"Orchestration {instance_id} completed, stopping poll")
+ break
+
+ except Exception as e:
+ logger.error(f"Error polling status for {instance_id}: {e}")
+
+ await asyncio.sleep(0.1) # Poll every 100ms for responsive UI
+
+ # Cleanup
+ if instance_id in _polling_tasks:
+ del _polling_tasks[instance_id]
+
+
+def start_status_polling(instance_id: str):
+ """Start background polling for an instance."""
+ if instance_id not in _polling_tasks:
+ task = asyncio.create_task(poll_orchestration_status(instance_id))
+ _polling_tasks[instance_id] = task
+
+
+# ============================================================================
+# REST API Endpoints
+# ============================================================================
+
+
+@app.get("/health")
+async def health():
+ """Health check endpoint."""
+ return {"status": "healthy", "timestamp": datetime.now().isoformat()}
+
+
+@app.get("/api/alerts", response_model=list[AlertInfo])
+async def get_alerts():
+ """Get sample alerts."""
+ return SAMPLE_ALERTS
+
+
+@app.post("/api/workflow/start", response_model=StartWorkflowResponse)
+async def start_workflow(request: StartWorkflowRequest):
+ """Start a new fraud detection orchestration."""
+ client = get_dts_client()
+
+ alert = {
+ "alert_id": request.alert_id,
+ "customer_id": request.customer_id,
+ "alert_type": request.alert_type,
+ "description": request.description,
+ "timestamp": datetime.now().isoformat(),
+ "severity": request.severity,
+ "approval_timeout_hours": request.approval_timeout_hours,
+ }
+
+ instance_id = client.schedule_new_orchestration(
+ ORCHESTRATION_NAME,
+ input=alert,
+ instance_id=f"fraud-{request.alert_id}-{int(time.time())}",
+ )
+
+ logger.info(f"Started orchestration {instance_id} for alert {request.alert_id}")
+
+ return StartWorkflowResponse(
+ instance_id=instance_id,
+ alert_id=request.alert_id,
+ status="started",
+ )
+
+
+@app.get("/api/workflow/status/{instance_id}", response_model=WorkflowStatusResponse)
+async def get_workflow_status(instance_id: str):
+ """Get current status of an orchestration."""
+ client = get_dts_client()
+
+ state = client.get_orchestration_state(instance_id)
+
+ if not state:
+ raise HTTPException(status_code=404, detail="Orchestration not found")
+
+ result = None
+ if state.serialized_output:
+ try:
+ result = json.loads(state.serialized_output)
+ except json.JSONDecodeError:
+ result = {"raw": state.serialized_output}
+
+ return WorkflowStatusResponse(
+ instance_id=instance_id,
+ status=state.runtime_status.name,
+ custom_status=state.serialized_custom_status,
+ result=result,
+ )
+
+
+@app.post("/api/workflow/decision")
+async def submit_decision(request: AnalystDecisionRequest):
+ """Submit analyst decision for a pending orchestration."""
+ client = get_dts_client()
+
+ decision = {
+ "alert_id": request.alert_id,
+ "approved_action": request.approved_action,
+ "analyst_notes": request.analyst_notes,
+ "analyst_id": request.analyst_id,
+ }
+
+ client.raise_orchestration_event(
+ instance_id=request.instance_id,
+ event_name=ANALYST_APPROVAL_EVENT,
+ data=decision,
+ )
+
+ logger.info(f"Submitted decision for {request.instance_id}: {request.approved_action}")
+
+ # Broadcast decision event
+ await manager.broadcast(request.instance_id, {
+ "type": "decision_submitted",
+ "instance_id": request.instance_id,
+ "action": request.approved_action,
+ "timestamp": datetime.now().isoformat(),
+ })
+
+ return {"status": "submitted", "instance_id": request.instance_id}
+
+
+# ============================================================================
+# WebSocket Endpoint
+# ============================================================================
+
+
+@app.websocket("/ws/{instance_id}")
+async def websocket_endpoint(websocket: WebSocket, instance_id: str):
+ """
+ WebSocket endpoint for real-time status updates.
+
+ Connect to /ws/{instance_id} to receive updates for a specific orchestration.
+ """
+ await manager.connect(websocket, instance_id)
+
+ # Start polling for this instance
+ start_status_polling(instance_id)
+
+ try:
+ # Send initial status
+ client = get_dts_client()
+ state = client.get_orchestration_state(instance_id)
+
+ if state:
+ await websocket.send_json({
+ "type": "initial_status",
+ "instance_id": instance_id,
+ "status": state.runtime_status.name,
+ "custom_status": state.serialized_custom_status,
+ })
+
+ # Keep connection alive
+ while True:
+ try:
+ # Wait for client messages (ping/pong, commands, etc.)
+ data = await asyncio.wait_for(websocket.receive_text(), timeout=30)
+
+ # Handle client commands
+ try:
+ message = json.loads(data)
+ if message.get("type") == "ping":
+ await websocket.send_json({"type": "pong"})
+ except json.JSONDecodeError:
+ pass
+
+ except asyncio.TimeoutError:
+ # Send ping to keep connection alive
+ try:
+ await websocket.send_json({"type": "ping"})
+ except Exception:
+ break
+
+ except WebSocketDisconnect:
+ logger.info(f"WebSocket disconnected for {instance_id}")
+ except Exception as e:
+ logger.error(f"WebSocket error for {instance_id}: {e}")
+ finally:
+ manager.disconnect(websocket, instance_id)
+
+
+# ============================================================================
+# Startup/Shutdown
+# ============================================================================
+
+
+@app.on_event("startup")
+async def startup():
+ """Initialize on startup."""
+ logger.info("="*60)
+ logger.info("Starting Durable Fraud Detection Backend")
+ logger.info("="*60)
+
+ # Pre-initialize DTS client
+ try:
+ get_dts_client()
+ logger.info("✓ DTS client initialized")
+ except Exception as e:
+ logger.error(f"Failed to initialize DTS client: {e}")
+ logger.error("Make sure DTS emulator is running")
+
+ logger.info("Backend ready! 🚀")
+
+
+@app.on_event("shutdown")
+async def shutdown():
+ """Cleanup on shutdown."""
+ logger.info("Shutting down backend...")
+
+ # Cancel polling tasks
+ for task in _polling_tasks.values():
+ task.cancel()
+
+
+# ============================================================================
+# Main
+# ============================================================================
+
+
+if __name__ == "__main__":
+ import uvicorn
+
+ uvicorn.run(
+ app,
+ host="0.0.0.0",
+ port=8001,
+ log_level="info",
+ )
diff --git a/agentic_ai/workflow/fraud_detection_durable/client.py b/agentic_ai/workflow/fraud_detection_durable/client.py
new file mode 100644
index 000000000..5f4993949
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/client.py
@@ -0,0 +1,398 @@
+"""
+Durable Task Client for Fraud Detection Testing.
+
+This client demonstrates:
+1. Starting a fraud detection orchestration
+2. Monitoring orchestration status
+3. Sending analyst decisions (external events)
+4. Viewing results
+
+Prerequisites:
+- Worker must be running (python worker.py)
+- DTS emulator running on port 8080
+"""
+
+import asyncio
+import json
+import logging
+import os
+import time
+from datetime import datetime
+
+from azure.identity import DefaultAzureCredential
+from dotenv import load_dotenv
+from durabletask.azuremanaged.client import DurableTaskSchedulerClient
+from durabletask.client import OrchestrationState
+
+# Load environment
+load_dotenv()
+
+# Configure logging
+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
+logger = logging.getLogger(__name__)
+
+# Constants
+ANALYST_APPROVAL_EVENT = "AnalystDecision"
+ORCHESTRATION_NAME = "fraud_detection_orchestration"
+
+
+# ============================================================================
+# Sample Alerts
+# ============================================================================
+
+SAMPLE_ALERTS = {
+ "ALERT-001": {
+ "alert_id": "ALERT-001",
+ "customer_id": 1,
+ "alert_type": "multi_country_login",
+ "description": "Login attempts from USA and Russia within 2 hours",
+ "timestamp": datetime.now().isoformat(),
+ "severity": "high",
+ "approval_timeout_hours": 0.05, # 3 minutes for demo
+ },
+ "ALERT-002": {
+ "alert_id": "ALERT-002",
+ "customer_id": 2,
+ "alert_type": "data_spike",
+ "description": "Data usage increased by 500% in last 24 hours",
+ "timestamp": datetime.now().isoformat(),
+ "severity": "medium",
+ "approval_timeout_hours": 0.05,
+ },
+ "ALERT-003": {
+ "alert_id": "ALERT-003",
+ "customer_id": 3,
+ "alert_type": "unusual_charges",
+ "description": "Three large purchases totaling $5,000 in 10 minutes",
+ "timestamp": datetime.now().isoformat(),
+ "severity": "high",
+ "approval_timeout_hours": 0.05,
+ },
+}
+
+
+# ============================================================================
+# Client Helpers
+# ============================================================================
+
+
+def get_client() -> DurableTaskSchedulerClient:
+ """Create a configured DurableTaskSchedulerClient."""
+ taskhub_name = os.getenv("DTS_TASKHUB", "default")
+ endpoint_url = os.getenv("DTS_ENDPOINT", "http://localhost:8080")
+
+ logger.debug(f"Using DTS endpoint: {endpoint_url}")
+ logger.debug(f"Using taskhub: {taskhub_name}")
+
+ credential = None if endpoint_url.startswith("http://localhost") else DefaultAzureCredential()
+
+ return DurableTaskSchedulerClient(
+ host_address=endpoint_url,
+ secure_channel=not endpoint_url.startswith("http://localhost"),
+ taskhub=taskhub_name,
+ token_credential=credential,
+ )
+
+
+def start_orchestration(client: DurableTaskSchedulerClient, alert: dict) -> str:
+ """Start a fraud detection orchestration."""
+ instance_id = client.schedule_new_orchestration(
+ ORCHESTRATION_NAME,
+ input=alert,
+ instance_id=f"fraud-{alert['alert_id']}-{int(time.time())}",
+ )
+ logger.info(f"Started orchestration with instance ID: {instance_id}")
+ return instance_id
+
+
+def get_status(client: DurableTaskSchedulerClient, instance_id: str) -> OrchestrationState | None:
+ """Get orchestration status."""
+ return client.get_orchestration_state(instance_id)
+
+
+def wait_for_status(
+ client: DurableTaskSchedulerClient,
+ instance_id: str,
+ target_status: str = "RUNNING",
+ timeout: int = 60,
+ poll_interval: float = 1.0,
+) -> OrchestrationState | None:
+ """Wait for orchestration to reach a specific status."""
+ start = time.time()
+ while time.time() - start < timeout:
+ state = get_status(client, instance_id)
+ if state:
+ current_status = state.runtime_status.name
+ custom_status = state.custom_status or ""
+ logger.debug(f"Status: {current_status}, Custom: {custom_status}")
+
+ if current_status == target_status:
+ return state
+
+ if current_status in ("COMPLETED", "FAILED", "TERMINATED"):
+ return state
+
+ time.sleep(poll_interval)
+
+ return None
+
+
+def send_analyst_decision(
+ client: DurableTaskSchedulerClient,
+ instance_id: str,
+ alert_id: str,
+ action: str,
+ notes: str = "",
+ analyst_id: str = "analyst_cli",
+) -> None:
+ """Send analyst decision as external event."""
+ decision = {
+ "alert_id": alert_id,
+ "approved_action": action,
+ "analyst_notes": notes,
+ "analyst_id": analyst_id,
+ }
+
+ logger.info(f"Sending analyst decision: {action}")
+ client.raise_orchestration_event(
+ instance_id=instance_id,
+ event_name=ANALYST_APPROVAL_EVENT,
+ data=decision,
+ )
+ logger.info("Decision sent successfully")
+
+
+def print_result(state: OrchestrationState | None) -> None:
+ """Print orchestration result."""
+ if not state:
+ logger.error("No state available")
+ return
+
+ print(f"\n{'='*60}")
+ print("ORCHESTRATION RESULT")
+ print(f"{'='*60}")
+ print(f"Status: {state.runtime_status.name}")
+ print(f"Custom Status: {state.serialized_custom_status or 'N/A'}")
+
+ if state.serialized_output:
+ try:
+ result = json.loads(state.serialized_output)
+ print(f"Alert ID: {result.get('alert_id')}")
+ print(f"Risk Score: {result.get('risk_score', 'N/A')}")
+ print(f"Action Taken: {result.get('action_taken', 'N/A')}")
+ print(f"Success: {result.get('success', 'N/A')}")
+ except json.JSONDecodeError:
+ print(f"Output: {state.serialized_output}")
+
+ print(f"{'='*60}\n")
+
+
+# ============================================================================
+# Test Scenarios
+# ============================================================================
+
+
+def test_high_risk_with_approval(client: DurableTaskSchedulerClient) -> None:
+ """
+ Test: High-risk alert → Analyst approves → Action executed
+ """
+ print("\n" + "="*70)
+ print("TEST: High-Risk Alert with Analyst Approval")
+ print("="*70)
+
+ alert = SAMPLE_ALERTS["ALERT-001"]
+ print(f"\nAlert: {alert['alert_id']} - {alert['alert_type']}")
+ print(f"Description: {alert['description']}")
+
+ # Start orchestration
+ instance_id = start_orchestration(client, alert)
+
+ # Wait for orchestration to request analyst review
+ print("\n⏳ Waiting for analysis to complete and request analyst review...")
+
+ for _ in range(120): # Wait up to 2 minutes
+ state = get_status(client, instance_id)
+ if state:
+ custom_status = state.serialized_custom_status or ""
+ if "Awaiting analyst review" in custom_status:
+ print(f"✓ Orchestration is waiting for analyst: {custom_status}")
+ break
+ if state.runtime_status.name in ("COMPLETED", "FAILED"):
+ print(f"Orchestration ended: {state.runtime_status.name}")
+ print_result(state)
+ return
+ time.sleep(1)
+ else:
+ print("⚠️ Timeout waiting for analyst review")
+ return
+
+ # Simulate analyst decision
+ print("\n👤 Simulating analyst decision: lock_account")
+ time.sleep(2) # Simulate analyst thinking
+
+ send_analyst_decision(
+ client=client,
+ instance_id=instance_id,
+ alert_id=alert["alert_id"],
+ action="lock_account",
+ notes="Confirmed fraudulent activity - locking account",
+ analyst_id="analyst_001",
+ )
+
+ # Wait for completion
+ print("\n⏳ Waiting for orchestration to complete...")
+ state = client.wait_for_orchestration_completion(instance_id, timeout=60)
+ print_result(state)
+
+
+def test_low_risk_auto_clear(client: DurableTaskSchedulerClient) -> None:
+ """
+ Test: Low-risk alert → Auto-cleared (no human review)
+ """
+ print("\n" + "="*70)
+ print("TEST: Low-Risk Alert with Auto-Clear")
+ print("="*70)
+
+ # Modify alert to be low severity
+ alert = {
+ **SAMPLE_ALERTS["ALERT-002"],
+ "severity": "low",
+ "description": "Minor data usage increase - likely legitimate",
+ }
+ print(f"\nAlert: {alert['alert_id']} - {alert['alert_type']}")
+ print(f"Description: {alert['description']}")
+
+ # Start orchestration
+ instance_id = start_orchestration(client, alert)
+
+ # Wait for completion (should auto-clear)
+ print("\n⏳ Waiting for orchestration to complete (should auto-clear)...")
+ state = client.wait_for_orchestration_completion(instance_id, timeout=120)
+ print_result(state)
+
+
+def test_timeout_escalation(client: DurableTaskSchedulerClient) -> None:
+ """
+ Test: High-risk alert → Analyst doesn't respond → Timeout escalation
+ """
+ print("\n" + "="*70)
+ print("TEST: Analyst Timeout Escalation")
+ print("="*70)
+
+ # Use very short timeout for demo
+ alert = {
+ **SAMPLE_ALERTS["ALERT-003"],
+ "approval_timeout_hours": 0.001, # ~3.6 seconds
+ }
+ print(f"\nAlert: {alert['alert_id']} - {alert['alert_type']}")
+ print(f"Description: {alert['description']}")
+ print(f"Timeout: {alert['approval_timeout_hours']*3600:.1f} seconds")
+
+ # Start orchestration
+ instance_id = start_orchestration(client, alert)
+
+ # Wait for completion (will timeout and escalate)
+ print("\n⏳ Waiting for timeout and escalation...")
+ state = client.wait_for_orchestration_completion(instance_id, timeout=180)
+ print_result(state)
+
+
+def interactive_mode(client: DurableTaskSchedulerClient) -> None:
+ """
+ Interactive mode for manual testing.
+ """
+ print("\n" + "="*70)
+ print("INTERACTIVE MODE")
+ print("="*70)
+
+ while True:
+ print("\nOptions:")
+ print("1. Start new orchestration (ALERT-001)")
+ print("2. Start new orchestration (ALERT-002)")
+ print("3. Start new orchestration (ALERT-003)")
+ print("4. Check status of instance")
+ print("5. Send analyst decision")
+ print("6. Exit")
+
+ choice = input("\nChoice: ").strip()
+
+ if choice == "1":
+ instance_id = start_orchestration(client, SAMPLE_ALERTS["ALERT-001"])
+ print(f"Instance ID: {instance_id}")
+
+ elif choice == "2":
+ instance_id = start_orchestration(client, SAMPLE_ALERTS["ALERT-002"])
+ print(f"Instance ID: {instance_id}")
+
+ elif choice == "3":
+ instance_id = start_orchestration(client, SAMPLE_ALERTS["ALERT-003"])
+ print(f"Instance ID: {instance_id}")
+
+ elif choice == "4":
+ instance_id = input("Instance ID: ").strip()
+ state = get_status(client, instance_id)
+ if state:
+ print(f"Status: {state.runtime_status.name}")
+ print(f"Custom: {state.custom_status}")
+ if state.serialized_output:
+ print(f"Output: {state.serialized_output}")
+ else:
+ print("Not found")
+
+ elif choice == "5":
+ instance_id = input("Instance ID: ").strip()
+ alert_id = input("Alert ID: ").strip()
+ action = input("Action (lock_account/refund_charges/clear/both): ").strip()
+ send_analyst_decision(client, instance_id, alert_id, action)
+
+ elif choice == "6":
+ break
+
+
+# ============================================================================
+# Main
+# ============================================================================
+
+
+def main():
+ """Main entry point."""
+ print("\n" + "="*70)
+ print("Durable Fraud Detection Client")
+ print("="*70)
+ print("\nMake sure:")
+ print("1. DTS emulator is running (docker)")
+ print("2. Worker is running (python worker.py)")
+ print("3. MCP server is running")
+ print("")
+
+ client = get_client()
+
+ print("Select test mode:")
+ print("1. Run automated tests")
+ print("2. Interactive mode")
+
+ choice = input("\nChoice: ").strip()
+
+ if choice == "1":
+ print("\nRunning automated tests...")
+
+ # Test 1: High risk with approval
+ test_high_risk_with_approval(client)
+
+ # Test 2: Low risk auto-clear
+ # test_low_risk_auto_clear(client)
+
+ # Test 3: Timeout escalation
+ # test_timeout_escalation(client)
+
+ print("\n✅ All tests completed!")
+
+ elif choice == "2":
+ interactive_mode(client)
+
+ else:
+ print("Invalid choice")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/agentic_ai/workflow/fraud_detection_durable/fraud_analysis_workflow.py b/agentic_ai/workflow/fraud_detection_durable/fraud_analysis_workflow.py
new file mode 100644
index 000000000..7557707da
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/fraud_analysis_workflow.py
@@ -0,0 +1,667 @@
+"""
+Fraud Analysis Workflow - Inner Workflow for Fan-Out → Aggregate Pattern.
+
+This workflow handles the complex multi-agent topology:
+1. AlertRouter fans out to 3 specialist agents
+2. UsagePatternExecutor, LocationAnalysisExecutor, BillingChargeExecutor run in parallel
+3. FraudRiskAggregator collects all results and produces FraudRiskAssessment
+
+This is called as an ACTIVITY from the Durable Task orchestration.
+No human-in-the-loop here - that's handled by the outer Durable Task layer.
+"""
+
+import logging
+from dataclasses import dataclass, field
+from datetime import datetime
+from typing import Any
+
+from pydantic import BaseModel
+
+from agent_framework import (
+ ChatAgent,
+ Executor,
+ handler,
+ WorkflowBuilder,
+ WorkflowContext,
+ MCPStreamableHTTPTool,
+)
+from agent_framework.azure import AzureOpenAIChatClient
+
+logger = logging.getLogger(__name__)
+
+
+# ============================================================================
+# Helper Functions for Tool Call Extraction
+# ============================================================================
+
+
+def extract_tool_calls(response) -> list[dict]:
+ """Extract tool calls from an AgentResponse."""
+ tool_calls = []
+ call_id_to_index = {} # Map call_id to tool_calls index
+
+ for msg in response.messages:
+ for content in msg.contents:
+ if content.type == "function_call":
+ # Parse arguments if they're a string
+ args = getattr(content, "arguments", {})
+ if isinstance(args, str):
+ try:
+ import json
+ args = json.loads(args)
+ except:
+ args = {"raw": args}
+
+ call_id = getattr(content, "call_id", None)
+ idx = len(tool_calls)
+ tool_calls.append({
+ "name": getattr(content, "name", "unknown"),
+ "arguments": args,
+ "result": "",
+ "call_id": call_id,
+ })
+ if call_id:
+ call_id_to_index[call_id] = idx
+
+ elif content.type == "function_result":
+ # Match result to existing call by call_id
+ call_id = getattr(content, "call_id", None)
+ result = getattr(content, "result", None)
+ if call_id and call_id in call_id_to_index:
+ idx = call_id_to_index[call_id]
+ result_str = str(result)[:500] if result else ""
+ tool_calls[idx]["result"] = result_str
+
+ elif content.type == "mcp_server_tool_call":
+ call_id = getattr(content, "call_id", None)
+ idx = len(tool_calls)
+ tool_calls.append({
+ "name": getattr(content, "tool_name", "unknown"),
+ "arguments": getattr(content, "arguments", {}),
+ "result": "",
+ "call_id": call_id,
+ })
+ if call_id:
+ call_id_to_index[call_id] = idx
+
+ elif content.type == "mcp_server_tool_result":
+ call_id = getattr(content, "call_id", None)
+ output = getattr(content, "output", None)
+ if call_id and call_id in call_id_to_index:
+ idx = call_id_to_index[call_id]
+ result_str = str(output)[:500] if output else ""
+ tool_calls[idx]["result"] = result_str
+
+ # Remove call_id from final output (not needed for UI)
+ for tc in tool_calls:
+ tc.pop("call_id", None)
+
+ return tool_calls
+
+
+# ============================================================================
+# Message Types (Pydantic models for type-safe messaging)
+# ============================================================================
+
+
+class ToolCallInfo(BaseModel):
+ """Information about a tool call made during analysis."""
+ name: str
+ arguments: dict = {}
+ result: str = ""
+
+
+class SuspiciousActivityAlert(BaseModel):
+ """Initial alert from monitoring system."""
+ alert_id: str
+ customer_id: int
+ alert_type: str
+ description: str = ""
+ timestamp: str = ""
+ severity: str = "medium"
+
+
+class UsageAnalysisResult(BaseModel):
+ """Result from UsagePatternExecutor."""
+ alert_id: str
+ risk_score: float
+ findings: str
+ data_points: list[str] = []
+ tool_calls: list[ToolCallInfo] = []
+
+
+class LocationAnalysisResult(BaseModel):
+ """Result from LocationAnalysisExecutor."""
+ alert_id: str
+ risk_score: float
+ findings: str
+ locations: list[str] = []
+ tool_calls: list[ToolCallInfo] = []
+
+
+class BillingAnalysisResult(BaseModel):
+ """Result from BillingChargeExecutor."""
+ alert_id: str
+ risk_score: float
+ findings: str
+ charges: list[str] = []
+ tool_calls: list[ToolCallInfo] = []
+
+
+class FraudRiskAssessment(BaseModel):
+ """Final aggregated assessment from FraudRiskAggregator."""
+ alert_id: str
+ customer_id: int
+ overall_risk_score: float
+ risk_level: str # "low", "medium", "high", "critical"
+ recommended_action: str # "clear", "lock_account", "refund_charges", "both"
+ reasoning: str
+ usage_findings: str
+ location_findings: str
+ billing_findings: str
+ # Step details for UI
+ step_details: dict = {}
+
+
+# ============================================================================
+# Executors
+# ============================================================================
+
+
+class AlertRouterExecutor(Executor):
+ """Routes incoming alerts to all specialist executors (fan-out)."""
+
+ def __init__(self):
+ super().__init__(id="alert_router")
+
+ @handler
+ async def handle_alert(
+ self, alert: SuspiciousActivityAlert, ctx: WorkflowContext[SuspiciousActivityAlert]
+ ) -> None:
+ logger.info(f"[AlertRouter] Routing alert {alert.alert_id} to 3 specialist executors")
+
+ # Fan-out: send to all 3 specialist executors
+ # The workflow edges will handle delivery
+ await ctx.send_message(alert, target_id="usage_pattern_executor")
+ await ctx.send_message(alert, target_id="location_analysis_executor")
+ await ctx.send_message(alert, target_id="billing_charge_executor")
+
+
+class UsagePatternExecutor(Executor):
+ """Analyzes data usage patterns using MCP tools."""
+
+ def __init__(self, mcp_tool: MCPStreamableHTTPTool, chat_client: AzureOpenAIChatClient):
+ super().__init__(id="usage_pattern_executor")
+
+ # Filter MCP tools for usage analysis
+ allowed_tools = ["get_customer_detail", "get_subscription_detail", "get_data_usage", "search_knowledge_base"]
+ filtered_functions = [func for func in mcp_tool.functions if func.name in allowed_tools]
+
+ self._agent = ChatAgent(
+ chat_client=chat_client,
+ name="UsagePatternAnalyst",
+ instructions=(
+ "You are a specialist in analyzing customer data usage patterns. "
+ "Look for anomalies like sudden spikes, unusual hours, or patterns inconsistent with history. "
+ "Use the available tools to gather data and provide a risk score (0.0-1.0) and findings."
+ ),
+ tools=filtered_functions,
+ )
+
+ @handler
+ async def handle_alert(
+ self, alert: SuspiciousActivityAlert, ctx: WorkflowContext[UsageAnalysisResult]
+ ) -> None:
+ logger.info(f"[UsagePatternExecutor] Analyzing alert {alert.alert_id}")
+
+ prompt = f"""
+Analyze the usage patterns for customer {alert.customer_id}.
+Alert type: {alert.alert_type}
+Description: {alert.description}
+Severity: {alert.severity}
+
+Use tools to gather usage data, then assess the risk.
+
+Respond in this format:
+FINDINGS: [Your detailed findings]
+RISK_SCORE: [0.0-1.0]
+"""
+
+ response = await self._agent.run(prompt)
+ response_text = response.text if response.text else ""
+
+ # Extract tool calls from response
+ tool_calls_raw = extract_tool_calls(response)
+ tool_calls = [ToolCallInfo(name=tc["name"], arguments=tc.get("arguments", {}), result=tc.get("result", "")) for tc in tool_calls_raw]
+
+ # Parse risk score from LLM response (fallback to severity-based)
+ risk_score = 0.5
+ if "RISK_SCORE:" in response_text:
+ try:
+ score_line = [line for line in response_text.split("\n") if "RISK_SCORE:" in line][0]
+ risk_score = float(score_line.split("RISK_SCORE:")[1].strip())
+ except (IndexError, ValueError):
+ # Fallback based on severity
+ severity_scores = {"low": 0.3, "medium": 0.5, "high": 0.7, "critical": 0.9}
+ risk_score = severity_scores.get(alert.severity.lower(), 0.5)
+ else:
+ severity_scores = {"low": 0.3, "medium": 0.5, "high": 0.7, "critical": 0.9}
+ risk_score = severity_scores.get(alert.severity.lower(), 0.5)
+
+ result = UsageAnalysisResult(
+ alert_id=alert.alert_id,
+ risk_score=risk_score,
+ findings=response_text or "Analysis completed",
+ data_points=[],
+ tool_calls=tool_calls,
+ )
+
+ logger.info(f"[UsagePatternExecutor] Completed analysis, risk_score={result.risk_score}, tools={[tc.name for tc in tool_calls]}")
+ await ctx.send_message(result)
+
+
+class LocationAnalysisExecutor(Executor):
+ """Analyzes geolocation data for anomalies."""
+
+ def __init__(self, mcp_tool: MCPStreamableHTTPTool, chat_client: AzureOpenAIChatClient):
+ super().__init__(id="location_analysis_executor")
+
+ # Filter MCP tools for location analysis
+ allowed_tools = ["get_customer_detail", "get_security_logs", "search_knowledge_base"]
+ filtered_functions = [func for func in mcp_tool.functions if func.name in allowed_tools]
+
+ self._agent = ChatAgent(
+ chat_client=chat_client,
+ name="LocationAnalysisAgent",
+ instructions=(
+ "You are a specialist in analyzing geolocation and security patterns. "
+ "Look for impossible travel, VPN usage, or login anomalies. "
+ "Use the available tools to gather data and provide a risk score (0.0-1.0) and findings."
+ ),
+ tools=filtered_functions,
+ )
+
+ @handler
+ async def handle_alert(
+ self, alert: SuspiciousActivityAlert, ctx: WorkflowContext[LocationAnalysisResult]
+ ) -> None:
+ logger.info(f"[LocationAnalysisExecutor] Analyzing alert {alert.alert_id}")
+
+ prompt = f"""
+Analyze the location and security patterns for customer {alert.customer_id}.
+Alert type: {alert.alert_type}
+Description: {alert.description}
+Severity: {alert.severity}
+
+Use tools to gather security logs and location data, then assess the risk.
+
+Respond in this format:
+FINDINGS: [Your detailed findings]
+RISK_SCORE: [0.0-1.0]
+"""
+
+ response = await self._agent.run(prompt)
+ response_text = response.text if response.text else ""
+
+ # Extract tool calls from response
+ tool_calls_raw = extract_tool_calls(response)
+ tool_calls = [ToolCallInfo(name=tc["name"], arguments=tc.get("arguments", {}), result=tc.get("result", "")) for tc in tool_calls_raw]
+
+ # Parse risk score from LLM response (fallback to severity-based)
+ risk_score = 0.5
+ if "RISK_SCORE:" in response_text:
+ try:
+ score_line = [line for line in response_text.split("\n") if "RISK_SCORE:" in line][0]
+ risk_score = float(score_line.split("RISK_SCORE:")[1].strip())
+ except (IndexError, ValueError):
+ severity_scores = {"low": 0.3, "medium": 0.5, "high": 0.8, "critical": 0.95}
+ risk_score = severity_scores.get(alert.severity.lower(), 0.6)
+ else:
+ severity_scores = {"low": 0.3, "medium": 0.5, "high": 0.8, "critical": 0.95}
+ risk_score = severity_scores.get(alert.severity.lower(), 0.6)
+
+ result = LocationAnalysisResult(
+ alert_id=alert.alert_id,
+ risk_score=risk_score,
+ findings=response_text or "Analysis completed",
+ locations=[],
+ tool_calls=tool_calls,
+ )
+
+ logger.info(f"[LocationAnalysisExecutor] Completed analysis, risk_score={result.risk_score}, tools={[tc.name for tc in tool_calls]}")
+ await ctx.send_message(result)
+
+
+class BillingChargeExecutor(Executor):
+ """Analyzes billing and charge patterns."""
+
+ def __init__(self, mcp_tool: MCPStreamableHTTPTool, chat_client: AzureOpenAIChatClient):
+ super().__init__(id="billing_charge_executor")
+
+ # Filter MCP tools for billing analysis
+ allowed_tools = ["get_customer_detail", "get_billing_summary", "get_subscription_detail",
+ "get_customer_orders", "search_knowledge_base"]
+ filtered_functions = [func for func in mcp_tool.functions if func.name in allowed_tools]
+
+ self._agent = ChatAgent(
+ chat_client=chat_client,
+ name="BillingChargeAnalyst",
+ instructions=(
+ "You are a specialist in analyzing billing and charge patterns. "
+ "Look for unusual purchases, subscription changes, or payment anomalies. "
+ "Use the available tools to gather data and provide a risk score (0.0-1.0) and findings."
+ ),
+ tools=filtered_functions,
+ )
+
+ @handler
+ async def handle_alert(
+ self, alert: SuspiciousActivityAlert, ctx: WorkflowContext[BillingAnalysisResult]
+ ) -> None:
+ logger.info(f"[BillingChargeExecutor] Analyzing alert {alert.alert_id}")
+
+ prompt = f"""
+Analyze the billing and charge patterns for customer {alert.customer_id}.
+Alert type: {alert.alert_type}
+Description: {alert.description}
+Severity: {alert.severity}
+
+Use tools to gather billing data and orders, then assess the risk.
+
+Respond in this format:
+FINDINGS: [Your detailed findings]
+RISK_SCORE: [0.0-1.0]
+"""
+
+ response = await self._agent.run(prompt)
+ response_text = response.text if response.text else ""
+
+ # Extract tool calls from response
+ tool_calls_raw = extract_tool_calls(response)
+ tool_calls = [ToolCallInfo(name=tc["name"], arguments=tc.get("arguments", {}), result=tc.get("result", "")) for tc in tool_calls_raw]
+
+ # Parse risk score from LLM response (fallback to severity-based)
+ risk_score = 0.4
+ if "RISK_SCORE:" in response_text:
+ try:
+ score_line = [line for line in response_text.split("\n") if "RISK_SCORE:" in line][0]
+ risk_score = float(score_line.split("RISK_SCORE:")[1].strip())
+ except (IndexError, ValueError):
+ severity_scores = {"low": 0.2, "medium": 0.4, "high": 0.6, "critical": 0.85}
+ risk_score = severity_scores.get(alert.severity.lower(), 0.4)
+ else:
+ severity_scores = {"low": 0.2, "medium": 0.4, "high": 0.6, "critical": 0.85}
+ risk_score = severity_scores.get(alert.severity.lower(), 0.4)
+
+ result = BillingAnalysisResult(
+ alert_id=alert.alert_id,
+ risk_score=risk_score,
+ findings=response_text or "Analysis completed",
+ charges=[],
+ tool_calls=tool_calls,
+ )
+
+ logger.info(f"[BillingChargeExecutor] Completed analysis, risk_score={result.risk_score}, tools={[tc.name for tc in tool_calls]}")
+ await ctx.send_message(result)
+
+
+# Type alias for fan-in results
+AnalysisResult = UsageAnalysisResult | LocationAnalysisResult | BillingAnalysisResult
+
+
+class FraudRiskAggregatorExecutor(Executor):
+ """Aggregates all analysis results and produces final risk assessment.
+
+ Receives a list of results from fan-in edges (all 3 specialists at once).
+ """
+
+ def __init__(self, chat_client: AzureOpenAIChatClient):
+ super().__init__(id="fraud_risk_aggregator")
+ self._chat_client = chat_client
+
+ @handler
+ async def handle_results(
+ self, results: list[AnalysisResult], ctx: WorkflowContext[None, FraudRiskAssessment]
+ ) -> None:
+ """Handle aggregated results from fan-in (receives list of all 3 results)."""
+ logger.info(f"[Aggregator] Received {len(results)} results for aggregation")
+
+ # Separate results by type
+ usage: UsageAnalysisResult | None = None
+ location: LocationAnalysisResult | None = None
+ billing: BillingAnalysisResult | None = None
+
+ for result in results:
+ if isinstance(result, UsageAnalysisResult):
+ usage = result
+ elif isinstance(result, LocationAnalysisResult):
+ location = result
+ elif isinstance(result, BillingAnalysisResult):
+ billing = result
+
+ if not all([usage, location, billing]):
+ raise ValueError(f"Expected 3 different result types, got: {[type(r).__name__ for r in results]}")
+
+ alert_id = usage.alert_id
+ logger.info(f"[Aggregator] Aggregating results for {alert_id}")
+
+ # Use LLM to synthesize findings
+ agent = ChatAgent(
+ chat_client=self._chat_client,
+ name="FraudRiskAggregator",
+ instructions=(
+ "You are a senior fraud analyst synthesizing findings from specialist agents. "
+ "Weigh the evidence, calculate an overall risk score, and recommend an action. "
+ "Be thorough but decisive. Return a JSON response with the assessment."
+ ),
+ )
+
+ prompt = f"""
+Synthesize these fraud analysis findings for alert {alert_id}:
+
+USAGE ANALYSIS (risk: {usage.risk_score}):
+{usage.findings}
+
+LOCATION ANALYSIS (risk: {location.risk_score}):
+{location.findings}
+
+BILLING ANALYSIS (risk: {billing.risk_score}):
+{billing.findings}
+
+Provide:
+1. Overall risk score (0.0-1.0)
+2. Risk level (low/medium/high/critical)
+3. Recommended action (clear/lock_account/refund_charges/both)
+4. Reasoning (1-2 sentences)
+"""
+
+ response = await agent.run(prompt)
+
+ # Calculate weighted score
+ overall_score = (usage.risk_score * 0.3 + location.risk_score * 0.4 + billing.risk_score * 0.3)
+
+ # Determine risk level
+ if overall_score >= 0.8:
+ risk_level = "critical"
+ elif overall_score >= 0.6:
+ risk_level = "high"
+ elif overall_score >= 0.4:
+ risk_level = "medium"
+ else:
+ risk_level = "low"
+
+ # Determine recommended action
+ if overall_score >= 0.6:
+ recommended_action = "lock_account"
+ else:
+ recommended_action = "clear"
+
+ # Helper to safely extract tool calls (handles both ToolCallInfo and dict)
+ def safe_tool_calls(tool_calls_list):
+ result = []
+ for tc in tool_calls_list:
+ if hasattr(tc, 'model_dump'):
+ result.append(tc.model_dump())
+ elif isinstance(tc, dict):
+ result.append(tc)
+ else:
+ result.append({"name": str(tc), "arguments": {}, "result": ""})
+ return result
+
+ # Build step details for UI
+ step_details = {
+ "usage_pattern_executor": {
+ "status": "completed",
+ "risk_score": usage.risk_score,
+ "tool_calls": safe_tool_calls(usage.tool_calls),
+ "output": usage.findings[:300] if len(usage.findings) > 300 else usage.findings,
+ },
+ "location_analysis_executor": {
+ "status": "completed",
+ "risk_score": location.risk_score,
+ "tool_calls": safe_tool_calls(location.tool_calls),
+ "output": location.findings[:300] if len(location.findings) > 300 else location.findings,
+ },
+ "billing_charge_executor": {
+ "status": "completed",
+ "risk_score": billing.risk_score,
+ "tool_calls": safe_tool_calls(billing.tool_calls),
+ "output": billing.findings[:300] if len(billing.findings) > 300 else billing.findings,
+ },
+ "fraud_risk_aggregator": {
+ "status": "completed",
+ "risk_score": overall_score,
+ "tool_calls": [],
+ "output": f"Aggregated risk: {overall_score:.2f} ({risk_level}). Recommended: {recommended_action}",
+ },
+ }
+
+ assessment = FraudRiskAssessment(
+ alert_id=alert_id,
+ customer_id=0, # Would be extracted from original alert
+ overall_risk_score=overall_score,
+ risk_level=risk_level,
+ recommended_action=recommended_action,
+ reasoning=response.text if response.text else "Based on aggregated analysis",
+ usage_findings=usage.findings,
+ location_findings=location.findings,
+ billing_findings=billing.findings,
+ step_details=step_details,
+ )
+
+ logger.info(f"[Aggregator] Assessment complete: risk={overall_score:.2f}, action={recommended_action}")
+ await ctx.yield_output(assessment)
+
+
+# ============================================================================
+# Workflow Builder
+# ============================================================================
+
+
+def create_fraud_analysis_workflow(
+ mcp_tool: MCPStreamableHTTPTool,
+ chat_client: AzureOpenAIChatClient,
+) -> Any:
+ """
+ Create the inner fraud analysis workflow.
+
+ This workflow handles the fan-out → aggregate pattern:
+ - AlertRouter fans out to 3 specialist agents
+ - Specialists run in parallel with MCP tools
+ - Aggregator waits for all 3 and produces FraudRiskAssessment
+
+ Returns:
+ Workflow: The built workflow ready to run
+ """
+ logger.info("[Workflow] Building fraud analysis workflow...")
+
+ # Create executors
+ alert_router = AlertRouterExecutor()
+ usage_executor = UsagePatternExecutor(mcp_tool, chat_client)
+ location_executor = LocationAnalysisExecutor(mcp_tool, chat_client)
+ billing_executor = BillingChargeExecutor(mcp_tool, chat_client)
+ aggregator = FraudRiskAggregatorExecutor(chat_client)
+
+ # Build workflow topology
+ builder = WorkflowBuilder()
+
+ # Set entry point
+ builder.set_start_executor(alert_router)
+
+ # Fan-out: Alert Router → 3 Specialists
+ builder.add_edge(alert_router, usage_executor)
+ builder.add_edge(alert_router, location_executor)
+ builder.add_edge(alert_router, billing_executor)
+
+ # Fan-in: 3 Specialists → Aggregator
+ builder.add_fan_in_edges(
+ [usage_executor, location_executor, billing_executor],
+ aggregator
+ )
+
+ workflow = builder.build()
+ logger.info("[Workflow] Fraud analysis workflow built successfully")
+
+ return workflow
+
+
+# ============================================================================
+# Standalone Test
+# ============================================================================
+
+
+async def main():
+ """Test the workflow standalone (without Durable Task)."""
+ import asyncio
+ import os
+ from dotenv import load_dotenv
+ from azure.identity import AzureCliCredential
+
+ load_dotenv()
+
+ # Initialize MCP tool
+ mcp_uri = os.getenv("MCP_SERVER_URI", "http://localhost:8000/mcp")
+ mcp_tool = MCPStreamableHTTPTool(name="contoso_mcp", url=mcp_uri, timeout=30)
+
+ async with mcp_tool:
+ # Initialize chat client
+ chat_client = AzureOpenAIChatClient(
+ credential=AzureCliCredential(),
+ deployment_name=os.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT", "gpt-4o"),
+ )
+
+ # Create workflow
+ workflow = create_fraud_analysis_workflow(mcp_tool, chat_client)
+
+ # Test alert
+ alert = SuspiciousActivityAlert(
+ alert_id="TEST-001",
+ customer_id=1,
+ alert_type="multi_country_login",
+ description="Login attempts from USA and Russia within 2 hours",
+ timestamp=datetime.now().isoformat(),
+ severity="high",
+ )
+
+ print(f"\n{'='*60}")
+ print(f"Running Fraud Analysis Workflow for Alert: {alert.alert_id}")
+ print(f"{'='*60}\n")
+
+ # Run workflow
+ async for event in workflow.run_stream(alert):
+ print(f"Event: {type(event).__name__}")
+ if hasattr(event, 'data') and isinstance(event.data, FraudRiskAssessment):
+ assessment = event.data
+ print(f"\n{'='*60}")
+ print("FRAUD RISK ASSESSMENT")
+ print(f"{'='*60}")
+ print(f"Alert ID: {assessment.alert_id}")
+ print(f"Risk Score: {assessment.overall_risk_score:.2f}")
+ print(f"Risk Level: {assessment.risk_level}")
+ print(f"Recommended Action: {assessment.recommended_action}")
+ print(f"Reasoning: {assessment.reasoning}")
+
+
+if __name__ == "__main__":
+ import asyncio
+ asyncio.run(main())
diff --git a/agentic_ai/workflow/fraud_detection_durable/pyproject.toml b/agentic_ai/workflow/fraud_detection_durable/pyproject.toml
new file mode 100644
index 000000000..6b111a306
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/pyproject.toml
@@ -0,0 +1,31 @@
+[project]
+name = "fraud-detection-durable"
+version = "0.1.0"
+description = "Durable Fraud Detection Workflow - Hybrid Workflow + Durable Task Architecture"
+readme = "README.md"
+requires-python = ">=3.12"
+dependencies = [
+ # Agent Framework (includes azure sub-package)
+ "agent-framework==1.0.0b260130",
+
+ # Durable Task Scheduler
+ "durabletask-azuremanaged>=1.0.0a1",
+
+ # FastAPI Backend
+ "fastapi==0.115.12",
+ "uvicorn>=0.25.0",
+ "websockets>=15.0.1",
+
+ # Azure & Auth
+ "azure-identity>=1.15.0",
+
+ # HTTP & Tools
+ "httpx==0.28.1",
+ "pydantic==2.11.4",
+
+ # Environment
+ "python-dotenv>=1.0.0",
+]
+
+[tool.uv]
+prerelease = "allow"
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/.dockerignore b/agentic_ai/workflow/fraud_detection_durable/ui/.dockerignore
new file mode 100644
index 000000000..778a2ee8b
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/.dockerignore
@@ -0,0 +1,30 @@
+# Development dependencies and build artifacts
+node_modules/
+dist/
+build/
+
+# Logs
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Environment files
+.env
+.env.local
+.env.*.local
+
+# IDE
+.vscode/
+.idea/
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Test coverage
+coverage/
+
+# Temporary files
+*.tmp
+*.temp
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/.env.example b/agentic_ai/workflow/fraud_detection_durable/ui/.env.example
new file mode 100644
index 000000000..02c3939e0
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/.env.example
@@ -0,0 +1,6 @@
+# API Configuration
+VITE_API_BASE_URL=http://localhost:8001
+VITE_WS_URL=ws://localhost:8001/ws
+
+# App Configuration
+VITE_APP_TITLE=Fraud Detection Workflow Visualizer
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/.eslintrc.cjs b/agentic_ai/workflow/fraud_detection_durable/ui/.eslintrc.cjs
new file mode 100644
index 000000000..7617f77ca
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/.eslintrc.cjs
@@ -0,0 +1,22 @@
+module.exports = {
+ root: true,
+ env: { browser: true, es2020: true },
+ extends: [
+ 'eslint:recommended',
+ 'plugin:react/recommended',
+ 'plugin:react/jsx-runtime',
+ 'plugin:react-hooks/recommended',
+ ],
+ ignorePatterns: ['dist', '.eslintrc.cjs'],
+ parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
+ settings: { react: { version: '19.0' } },
+ plugins: ['react-refresh'],
+ rules: {
+ 'react/jsx-no-target-blank': 'off',
+ 'react-refresh/only-export-components': [
+ 'warn',
+ { allowConstantExport: true },
+ ],
+ 'react/prop-types': 'warn',
+ },
+}
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/.gitignore b/agentic_ai/workflow/fraud_detection_durable/ui/.gitignore
new file mode 100644
index 000000000..aa674a799
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/.gitignore
@@ -0,0 +1,31 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+# Environment variables
+.env
+.env.local
+.env.production.local
+.env.development.local
+.env.test.local
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/.prettierignore b/agentic_ai/workflow/fraud_detection_durable/ui/.prettierignore
new file mode 100644
index 000000000..8c444c5a1
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/.prettierignore
@@ -0,0 +1,7 @@
+node_modules
+dist
+build
+.env
+.env.local
+*.log
+coverage
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/.prettierrc.cjs b/agentic_ai/workflow/fraud_detection_durable/ui/.prettierrc.cjs
new file mode 100644
index 000000000..9f6715a36
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/.prettierrc.cjs
@@ -0,0 +1,14 @@
+// Prettier Configuration
+module.exports = {
+ semi: true,
+ trailingComma: 'es5',
+ singleQuote: true,
+ printWidth: 100,
+ tabWidth: 2,
+ useTabs: false,
+ arrowParens: 'always',
+ endOfLine: 'lf',
+ jsxSingleQuote: false,
+ bracketSpacing: true,
+ jsxBracketSameLine: false,
+};
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/.vscode/extensions.json b/agentic_ai/workflow/fraud_detection_durable/ui/.vscode/extensions.json
new file mode 100644
index 000000000..82a944c17
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/.vscode/extensions.json
@@ -0,0 +1,7 @@
+{
+ "recommendations": [
+ "dbaeumer.vscode-eslint",
+ "esbenp.prettier-vscode",
+ "ms-vscode.vscode-typescript-next"
+ ]
+}
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/Dockerfile b/agentic_ai/workflow/fraud_detection_durable/ui/Dockerfile
new file mode 100644
index 000000000..f613068f8
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/Dockerfile
@@ -0,0 +1,57 @@
+# Build stage
+FROM node:22-alpine AS builder
+
+# Set working directory
+WORKDIR /app
+
+# Add metadata
+LABEL maintainer="OpenAI Workshop Team"
+LABEL description="Fraud Detection UI - React + Vite Application"
+
+# Copy package files
+COPY package*.json ./
+
+# Install dependencies with clean install for reproducible builds
+RUN npm ci --only=production=false && \
+ npm cache clean --force
+
+# Copy application source
+COPY . .
+
+# Build the application
+RUN npm run build
+
+# Production stage
+FROM node:22-alpine AS production
+
+# Install dumb-init for proper signal handling
+RUN apk add --no-cache dumb-init
+
+# Set working directory
+WORKDIR /app
+
+# Create a non-root user
+RUN addgroup -g 1001 -S nodejs && \
+ adduser -S nodejs -u 1001
+
+# Install serve globally
+RUN npm install -g serve
+
+# Copy built assets from builder stage
+COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
+
+# Switch to non-root user
+USER nodejs
+
+# Expose port
+EXPOSE 3000
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
+ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1
+
+# Use dumb-init to handle signals properly
+ENTRYPOINT ["dumb-init", "--"]
+
+# Serve the application
+CMD ["serve", "-s", "dist", "-l", "3000"]
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/README.md b/agentic_ai/workflow/fraud_detection_durable/ui/README.md
new file mode 100644
index 000000000..9a785b969
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/README.md
@@ -0,0 +1,247 @@
+# Fraud Detection Workflow Visualizer
+
+A modern React application for visualizing and managing fraud detection workflows in real-time. Built with Vite, React, Material-UI, and React Flow.
+
+## Features
+
+- 🔄 Real-time workflow visualization
+- 🎯 Interactive workflow controls
+- 📊 Live event logging
+- 👤 Human-in-the-loop decision making
+- 🔌 WebSocket-based real-time updates
+- 🎨 Material-UI based responsive design
+- 📈 React Flow powered workflow graphs
+
+## Tech Stack
+
+- **Framework**: React 19.2
+- **Build Tool**: Vite 7.2
+- **UI Library**: Material-UI (MUI) 7.3
+- **Graph Visualization**: React Flow 11.11
+- **State Management**: React Hooks
+- **Real-time Communication**: WebSocket
+
+## Prerequisites
+
+- Node.js 18+ or newer
+- npm 9+ or newer
+
+## Getting Started
+
+### Installation
+
+1. Clone the repository
+2. Navigate to the project directory:
+
+ ```bash
+ cd agentic_ai/workflow/fraud_detection/ui
+ ```
+
+3. Install dependencies:
+
+ ```bash
+ npm install
+ ```
+
+4. Copy the environment file:
+
+ ```bash
+ cp .env.example .env
+ ```
+
+5. Configure your environment variables in `.env` if needed
+
+### Development
+
+Start the development server:
+
+```bash
+npm run dev
+```
+
+The application will open at `http://localhost:3000`
+
+### Build
+
+Build for production:
+
+```bash
+npm run build
+```
+
+Preview the production build:
+
+```bash
+npm run preview
+```
+
+### Docker
+
+Build and run the application in a Docker container:
+
+```bash
+# Build the Docker image
+docker build -t fraud-detection-ui:latest .
+
+# Run the container
+docker run -p 3000:3000 fraud-detection-ui:latest
+
+# Run with custom backend URL
+docker run -p 3000:3000 \
+ -e VITE_API_BASE_URL=http://your-backend:8001 \
+ -e VITE_WS_URL=ws://your-backend:8001/ws \
+ fraud-detection-ui:latest
+```
+
+The Docker image uses a multi-stage build with:
+
+- **Build stage**: Node.js 22 Alpine with all dependencies
+- **Production stage**: Node.js 22 Alpine serving with `serve`
+- **Security**: Runs as non-root user (nodejs:1001)
+- **Port**: Exposes port 3000
+- **Health check**: Built-in health monitoring
+
+### Linting
+
+Run ESLint:
+
+```bash
+npm run lint
+```
+
+Fix linting issues automatically:
+
+```bash
+npm run lint:fix
+```
+
+## Project Structure
+
+```text
+ui/
+├── src/
+│ ├── components/ # React components
+│ │ ├── AnalystDecisionPanel.jsx
+│ │ ├── ControlPanel.jsx
+│ │ ├── CustomNode.jsx
+│ │ ├── EventLog.jsx
+│ │ └── WorkflowVisualizer.jsx
+│ ├── hooks/ # Custom React hooks
+│ │ └── useWebSocket.js
+│ ├── utils/ # Utility functions
+│ │ ├── api.js
+│ │ ├── helpers.js
+│ │ └── uiHelpers.jsx
+│ ├── constants/ # Application constants
+│ │ ├── config.js
+│ │ └── workflow.js
+│ ├── theme/ # MUI theme configuration
+│ │ └── index.js
+│ ├── App.jsx # Main application component
+│ ├── main.jsx # Application entry point
+│ └── index.css # Global styles
+├── public/ # Static assets
+├── .env # Environment variables
+├── .env.example # Environment variables example
+├── .eslintrc.cjs # ESLint configuration
+├── .gitignore # Git ignore rules
+├── index.html # HTML template
+├── jsconfig.json # JavaScript configuration
+├── package.json # Project dependencies
+├── vite.config.js # Vite configuration
+└── README.md # This file
+```
+
+## Path Aliases
+
+The project uses path aliases for cleaner imports:
+
+- `@/` → `src/`
+- `@components/` → `src/components/`
+- `@hooks/` → `src/hooks/`
+- `@utils/` → `src/utils/`
+- `@theme/` → `src/theme/`
+- `@constants/` → `src/constants/`
+
+Example:
+
+```javascript
+import WorkflowVisualizer from '@components/WorkflowVisualizer';
+import { useWebSocket } from '@hooks/useWebSocket';
+import theme from '@theme';
+```
+
+## Environment Variables
+
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `VITE_API_BASE_URL` | Backend API URL | `http://localhost:8001` |
+| `VITE_WS_URL` | WebSocket URL | `ws://localhost:8001/ws` |
+| `VITE_APP_TITLE` | Application title | `Fraud Detection Workflow Visualizer` |
+
+## API Integration
+
+The application connects to a backend API running on `http://localhost:8001` by default. The following endpoints are used:
+
+- `GET /api/alerts` - Fetch available alerts
+- `POST /api/workflow/start` - Start a workflow
+- `POST /api/workflow/decision` - Submit analyst decision
+- `WS /ws` - WebSocket connection for real-time updates
+
+## Components
+
+### Main Components
+
+- **App.jsx** - Main application component, orchestrates all child components
+- **WorkflowVisualizer** - Displays the workflow graph with real-time state updates
+- **ControlPanel** - Alert selection and workflow control interface
+- **AnalystDecisionPanel** - Human-in-the-loop decision making interface
+- **EventLog** - Real-time event logging display
+- **CustomNode** - Custom node component for workflow graph
+
+### Hooks
+
+- **useWebSocket** - Manages WebSocket connections with automatic reconnection
+
+## Development Guidelines
+
+### Code Style
+
+- Use functional components with hooks
+- Follow ESLint rules
+- Use path aliases for imports
+- Add JSDoc comments for functions
+- Keep components small and focused
+
+### Adding New Components
+
+1. Create component in `src/components/`
+2. Export from the component file
+3. Import using path alias: `import Component from '@components/Component'`
+
+### Adding New Constants
+
+1. Add constants to appropriate file in `src/constants/`
+2. Export named exports
+3. Import where needed: `import { CONSTANT } from '@constants/config'`
+
+## Troubleshooting
+
+### WebSocket Connection Issues
+
+- Ensure backend server is running
+- Check `VITE_WS_URL` in `.env`
+- Check browser console for connection errors
+
+### Build Issues
+
+- Clear node_modules and reinstall: `rm -rf node_modules && npm install`
+- Clear Vite cache: `rm -rf node_modules/.vite`
+
+## License
+
+See the main project LICENSE file.
+
+## Contributing
+
+Please read the main project CONTRIBUTING guidelines.
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/index.html b/agentic_ai/workflow/fraud_detection_durable/ui/index.html
new file mode 100644
index 000000000..fcdac8966
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+ Fraud Detection Workflow Visualizer
+
+
+
+
+
+
+
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/package-lock.json b/agentic_ai/workflow/fraud_detection_durable/ui/package-lock.json
new file mode 100644
index 000000000..0264ec278
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/package-lock.json
@@ -0,0 +1,5909 @@
+{
+ "name": "fraud-detection-ui",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "fraud-detection-ui",
+ "version": "1.0.0",
+ "dependencies": {
+ "@emotion/react": "^11.14.0",
+ "@emotion/styled": "^11.14.1",
+ "@mui/icons-material": "^7.3.6",
+ "@mui/material": "^7.3.6",
+ "react": "^19.2.1",
+ "react-dom": "^19.2.1",
+ "reactflow": "^11.11.4"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^5.1.2",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react": "^7.37.5",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "vite": "^7.2.7"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
+ "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
+ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
+ "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+ "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.5"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+ "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
+ "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.5",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+ "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.3.3",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+ "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
+ "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+ "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/styled": {
+ "version": "11.14.1",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz",
+ "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/is-prop-valid": "^1.3.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.0.0-rc.0",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+ "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
+ "license": "MIT"
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
+ "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.1",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
+ "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@mui/core-downloads-tracker": {
+ "version": "7.3.6",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.6.tgz",
+ "integrity": "sha512-QaYtTHlr8kDFN5mE1wbvVARRKH7Fdw1ZuOjBJcFdVpfNfRYKF3QLT4rt+WaB6CKJvpqxRsmEo0kpYinhH5GeHg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ }
+ },
+ "node_modules/@mui/icons-material": {
+ "version": "7.3.6",
+ "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.6.tgz",
+ "integrity": "sha512-0FfkXEj22ysIq5pa41A2NbcAhJSvmcZQ/vcTIbjDsd6hlslG82k5BEBqqS0ZJprxwIL3B45qpJ+bPHwJPlF7uQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@mui/material": "^7.3.6",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material": {
+ "version": "7.3.6",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.6.tgz",
+ "integrity": "sha512-R4DaYF3dgCQCUAkr4wW1w26GHXcf5rCmBRHVBuuvJvaGLmZdD8EjatP80Nz5JCw0KxORAzwftnHzXVnjR8HnFw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.4",
+ "@mui/core-downloads-tracker": "^7.3.6",
+ "@mui/system": "^7.3.6",
+ "@mui/types": "^7.4.9",
+ "@mui/utils": "^7.3.6",
+ "@popperjs/core": "^2.11.8",
+ "@types/react-transition-group": "^4.4.12",
+ "clsx": "^2.1.1",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1",
+ "react-is": "^19.2.0",
+ "react-transition-group": "^4.4.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@mui/material-pigment-css": "^7.3.6",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@mui/material-pigment-css": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/private-theming": {
+ "version": "7.3.6",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.6.tgz",
+ "integrity": "sha512-Ws9wZpqM+FlnbZXaY/7yvyvWQo1+02Tbx50mVdNmzWEi51C51y56KAbaDCYyulOOBL6BJxuaqG8rNNuj7ivVyw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.4",
+ "@mui/utils": "^7.3.6",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/styled-engine": {
+ "version": "7.3.6",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.6.tgz",
+ "integrity": "sha512-+wiYbtvj+zyUkmDB+ysH6zRjuQIJ+CM56w0fEXV+VDNdvOuSywG+/8kpjddvvlfMLsaWdQe5oTuYGBcodmqGzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.4",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/sheet": "^1.4.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.4.1",
+ "@emotion/styled": "^11.3.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/system": {
+ "version": "7.3.6",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.6.tgz",
+ "integrity": "sha512-8fehAazkHNP1imMrdD2m2hbA9sl7Ur6jfuNweh5o4l9YPty4iaZzRXqYvBCWQNwFaSHmMEj2KPbyXGp7Bt73Rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.4",
+ "@mui/private-theming": "^7.3.6",
+ "@mui/styled-engine": "^7.3.6",
+ "@mui/types": "^7.4.9",
+ "@mui/utils": "^7.3.6",
+ "clsx": "^2.1.1",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/types": {
+ "version": "7.4.9",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.9.tgz",
+ "integrity": "sha512-dNO8Z9T2cujkSIaCnWwprfeKmTWh97cnjkgmpFJ2sbfXLx8SMZijCYHOtP/y5nnUb/Rm2omxbDMmtUoSaUtKaw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.4"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/utils": {
+ "version": "7.3.6",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.6.tgz",
+ "integrity": "sha512-jn+Ba02O6PiFs7nKva8R2aJJ9kJC+3kQ2R0BbKNY3KQQ36Qng98GnPRFTlbwYTdMD6hLEBKaMLUktyg/rTfd2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.4",
+ "@mui/types": "^7.4.9",
+ "@types/prop-types": "^15.7.15",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-is": "^19.2.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
+ "node_modules/@reactflow/background": {
+ "version": "11.3.14",
+ "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz",
+ "integrity": "sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==",
+ "license": "MIT",
+ "dependencies": {
+ "@reactflow/core": "11.11.4",
+ "classcat": "^5.0.3",
+ "zustand": "^4.4.1"
+ },
+ "peerDependencies": {
+ "react": ">=17",
+ "react-dom": ">=17"
+ }
+ },
+ "node_modules/@reactflow/controls": {
+ "version": "11.2.14",
+ "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.14.tgz",
+ "integrity": "sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==",
+ "license": "MIT",
+ "dependencies": {
+ "@reactflow/core": "11.11.4",
+ "classcat": "^5.0.3",
+ "zustand": "^4.4.1"
+ },
+ "peerDependencies": {
+ "react": ">=17",
+ "react-dom": ">=17"
+ }
+ },
+ "node_modules/@reactflow/core": {
+ "version": "11.11.4",
+ "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.11.4.tgz",
+ "integrity": "sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3": "^7.4.0",
+ "@types/d3-drag": "^3.0.1",
+ "@types/d3-selection": "^3.0.3",
+ "@types/d3-zoom": "^3.0.1",
+ "classcat": "^5.0.3",
+ "d3-drag": "^3.0.0",
+ "d3-selection": "^3.0.0",
+ "d3-zoom": "^3.0.0",
+ "zustand": "^4.4.1"
+ },
+ "peerDependencies": {
+ "react": ">=17",
+ "react-dom": ">=17"
+ }
+ },
+ "node_modules/@reactflow/minimap": {
+ "version": "11.7.14",
+ "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.14.tgz",
+ "integrity": "sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@reactflow/core": "11.11.4",
+ "@types/d3-selection": "^3.0.3",
+ "@types/d3-zoom": "^3.0.1",
+ "classcat": "^5.0.3",
+ "d3-selection": "^3.0.0",
+ "d3-zoom": "^3.0.0",
+ "zustand": "^4.4.1"
+ },
+ "peerDependencies": {
+ "react": ">=17",
+ "react-dom": ">=17"
+ }
+ },
+ "node_modules/@reactflow/node-resizer": {
+ "version": "2.2.14",
+ "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz",
+ "integrity": "sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==",
+ "license": "MIT",
+ "dependencies": {
+ "@reactflow/core": "11.11.4",
+ "classcat": "^5.0.4",
+ "d3-drag": "^3.0.0",
+ "d3-selection": "^3.0.0",
+ "zustand": "^4.4.1"
+ },
+ "peerDependencies": {
+ "react": ">=17",
+ "react-dom": ">=17"
+ }
+ },
+ "node_modules/@reactflow/node-toolbar": {
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz",
+ "integrity": "sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@reactflow/core": "11.11.4",
+ "classcat": "^5.0.3",
+ "zustand": "^4.4.1"
+ },
+ "peerDependencies": {
+ "react": ">=17",
+ "react-dom": ">=17"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.53",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz",
+ "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
+ "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
+ "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
+ "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
+ "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
+ "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
+ "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
+ "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
+ "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
+ "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
+ "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
+ "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
+ "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
+ "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
+ "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
+ "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
+ "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
+ "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
+ "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
+ "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
+ "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
+ "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
+ "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/d3": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
+ "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/d3-axis": "*",
+ "@types/d3-brush": "*",
+ "@types/d3-chord": "*",
+ "@types/d3-color": "*",
+ "@types/d3-contour": "*",
+ "@types/d3-delaunay": "*",
+ "@types/d3-dispatch": "*",
+ "@types/d3-drag": "*",
+ "@types/d3-dsv": "*",
+ "@types/d3-ease": "*",
+ "@types/d3-fetch": "*",
+ "@types/d3-force": "*",
+ "@types/d3-format": "*",
+ "@types/d3-geo": "*",
+ "@types/d3-hierarchy": "*",
+ "@types/d3-interpolate": "*",
+ "@types/d3-path": "*",
+ "@types/d3-polygon": "*",
+ "@types/d3-quadtree": "*",
+ "@types/d3-random": "*",
+ "@types/d3-scale": "*",
+ "@types/d3-scale-chromatic": "*",
+ "@types/d3-selection": "*",
+ "@types/d3-shape": "*",
+ "@types/d3-time": "*",
+ "@types/d3-time-format": "*",
+ "@types/d3-timer": "*",
+ "@types/d3-transition": "*",
+ "@types/d3-zoom": "*"
+ }
+ },
+ "node_modules/@types/d3-array": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
+ "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-axis": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
+ "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-brush": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
+ "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-chord": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
+ "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-contour": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
+ "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-dispatch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz",
+ "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-drag": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+ "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-dsv": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
+ "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-fetch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
+ "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-dsv": "*"
+ }
+ },
+ "node_modules/@types/d3-force": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
+ "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-format": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
+ "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-geo": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
+ "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-hierarchy": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
+ "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-polygon": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
+ "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-quadtree": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
+ "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-random": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
+ "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-selection": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
+ "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
+ "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
+ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-time-format": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
+ "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-transition": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
+ "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-zoom": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+ "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-interpolate": "*",
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/geojson": {
+ "version": "7946.0.16",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.15",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-transition-group": {
+ "version": "4.4.12",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
+ "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz",
+ "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.5",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.53",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.18.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.5",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz",
+ "integrity": "sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001760",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz",
+ "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/classcat": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
+ "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==",
+ "license": "MIT"
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "license": "MIT"
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cosmiconfig/node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "d3-selection": "2 - 3"
+ }
+ },
+ "node_modules/d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.267",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+ "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
+ "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
+ "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.0.3",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.6",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "iterator.prototype": "^1.1.4",
+ "safe-array-concat": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
+ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.1",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.39.1",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.37.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.3",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.2.1",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.9",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.12",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+ "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz",
+ "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=8.40"
+ }
+ },
+ "node_modules/eslint-plugin-react/node_modules/resolve": {
+ "version": "2.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
+ "license": "MIT"
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "license": "MIT"
+ },
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/iterator.prototype": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "license": "MIT"
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "license": "MIT"
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/prop-types/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
+ "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
+ "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.1"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "19.2.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.1.tgz",
+ "integrity": "sha512-L7BnWgRbMwzMAubQcS7sXdPdNLmKlucPlopgAzx7FtYbksWZgEWiuYM5x9T6UqS2Ne0rsgQTq5kY2SGqpzUkYA==",
+ "license": "MIT"
+ },
+ "node_modules/react-refresh": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
+ "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
+ "node_modules/reactflow": {
+ "version": "11.11.4",
+ "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.11.4.tgz",
+ "integrity": "sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==",
+ "license": "MIT",
+ "dependencies": {
+ "@reactflow/background": "11.3.14",
+ "@reactflow/controls": "11.2.14",
+ "@reactflow/core": "11.11.4",
+ "@reactflow/minimap": "11.7.14",
+ "@reactflow/node-resizer": "2.2.14",
+ "@reactflow/node-toolbar": "1.3.14"
+ },
+ "peerDependencies": {
+ "react": ">=17",
+ "react-dom": ">=17"
+ }
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
+ "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.53.3",
+ "@rollup/rollup-android-arm64": "4.53.3",
+ "@rollup/rollup-darwin-arm64": "4.53.3",
+ "@rollup/rollup-darwin-x64": "4.53.3",
+ "@rollup/rollup-freebsd-arm64": "4.53.3",
+ "@rollup/rollup-freebsd-x64": "4.53.3",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
+ "@rollup/rollup-linux-arm-musleabihf": "4.53.3",
+ "@rollup/rollup-linux-arm64-gnu": "4.53.3",
+ "@rollup/rollup-linux-arm64-musl": "4.53.3",
+ "@rollup/rollup-linux-loong64-gnu": "4.53.3",
+ "@rollup/rollup-linux-ppc64-gnu": "4.53.3",
+ "@rollup/rollup-linux-riscv64-gnu": "4.53.3",
+ "@rollup/rollup-linux-riscv64-musl": "4.53.3",
+ "@rollup/rollup-linux-s390x-gnu": "4.53.3",
+ "@rollup/rollup-linux-x64-gnu": "4.53.3",
+ "@rollup/rollup-linux-x64-musl": "4.53.3",
+ "@rollup/rollup-openharmony-arm64": "4.53.3",
+ "@rollup/rollup-win32-arm64-msvc": "4.53.3",
+ "@rollup/rollup-win32-ia32-msvc": "4.53.3",
+ "@rollup/rollup-win32-x64-gnu": "4.53.3",
+ "@rollup/rollup-win32-x64-msvc": "4.53.3",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
+ "license": "MIT"
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
+ "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.2.7",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz",
+ "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
+ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
+ "extraneous": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz",
+ "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ },
+ "node_modules/zustand": {
+ "version": "4.5.7",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
+ "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
+ "license": "MIT",
+ "dependencies": {
+ "use-sync-external-store": "^1.2.2"
+ },
+ "engines": {
+ "node": ">=12.7.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.8",
+ "immer": ">=9.0.6",
+ "react": ">=16.8"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ }
+ }
+}
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/package.json b/agentic_ai/workflow/fraud_detection_durable/ui/package.json
new file mode 100644
index 000000000..77e904866
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "fraud-detection-ui",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview",
+ "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
+ "lint:fix": "eslint . --ext js,jsx --fix"
+ },
+ "dependencies": {
+ "@emotion/react": "^11.14.0",
+ "@emotion/styled": "^11.14.1",
+ "@mui/icons-material": "^7.3.6",
+ "@mui/material": "^7.3.6",
+ "react": "^19.2.1",
+ "react-dom": "^19.2.1",
+ "reactflow": "^11.11.4"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^5.1.2",
+ "eslint": "^9.39.1",
+ "eslint-plugin-react": "^7.37.5",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.4.24",
+ "vite": "^7.2.7"
+ }
+}
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/src/App.jsx b/agentic_ai/workflow/fraud_detection_durable/ui/src/App.jsx
new file mode 100644
index 000000000..af279a829
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/src/App.jsx
@@ -0,0 +1,388 @@
+import React, { useState, useCallback, useEffect, useRef } from 'react';
+import {
+ Box,
+ ThemeProvider,
+ createTheme,
+ CssBaseline,
+ AppBar,
+ Toolbar,
+ Typography,
+ Container,
+ Paper,
+ Grid,
+ Chip,
+} from '@mui/material';
+import SecurityIcon from '@mui/icons-material/Security';
+import CloudSyncIcon from '@mui/icons-material/CloudSync';
+import WorkflowVisualizer from './components/WorkflowVisualizer';
+import ControlPanel from './components/ControlPanel';
+import AnalystDecisionPanel from './components/AnalystDecisionPanel';
+
+const theme = createTheme({
+ palette: {
+ mode: 'light',
+ primary: {
+ main: '#1976d2',
+ },
+ secondary: {
+ main: '#dc004e',
+ },
+ success: {
+ main: '#4caf50',
+ },
+ warning: {
+ main: '#ff9800',
+ },
+ error: {
+ main: '#f44336',
+ },
+ },
+ typography: {
+ fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
+ },
+});
+
+function App() {
+ const [alerts, setAlerts] = useState([]);
+ const [selectedAlert, setSelectedAlert] = useState(null);
+ const [workflowRunning, setWorkflowRunning] = useState(false);
+ const [events, setEvents] = useState([]);
+ const [pendingDecision, setPendingDecision] = useState(null);
+ const [executorStates, setExecutorStates] = useState({});
+ const [instanceId, setInstanceId] = useState(null);
+ const [orchestrationStatus, setOrchestrationStatus] = useState(null);
+ const [stepDetails, setStepDetails] = useState({});
+
+ const ws = useRef(null);
+
+ // Load sample alerts on mount
+ useEffect(() => {
+ fetch('http://localhost:8001/api/alerts')
+ .then((res) => res.json())
+ .then((data) => setAlerts(data))
+ .catch((err) => console.error('Error loading alerts:', err));
+ }, []);
+
+ const addEvent = useCallback((event) => {
+ setEvents((prev) => [...prev, { ...event, timestamp: event.timestamp || new Date().toISOString() }]);
+ }, []);
+
+ // Connect to WebSocket when we have an instance ID
+ useEffect(() => {
+ if (!instanceId) return;
+
+ const wsUrl = `ws://localhost:8001/ws/${instanceId}`;
+ console.log('Connecting to WebSocket:', wsUrl);
+
+ ws.current = new WebSocket(wsUrl);
+
+ ws.current.onopen = () => {
+ console.log('WebSocket connected for instance:', instanceId);
+ addEvent({ type: 'websocket_connected', message: `Connected to orchestration ${instanceId}` });
+ };
+
+ ws.current.onmessage = (event) => {
+ try {
+ const data = JSON.parse(event.data);
+ console.log('WebSocket message:', data);
+ handleWebSocketMessage(data);
+ } catch (error) {
+ console.error('Error parsing WebSocket message:', error);
+ }
+ };
+
+ ws.current.onclose = () => {
+ console.log('WebSocket disconnected');
+ };
+
+ ws.current.onerror = (error) => {
+ console.error('WebSocket error:', error);
+ };
+
+ return () => {
+ if (ws.current) {
+ ws.current.close();
+ }
+ };
+ }, [instanceId]);
+
+ const handleWebSocketMessage = useCallback((data) => {
+ // Skip ping/pong messages
+ if (data.type === 'ping' || data.type === 'pong') return;
+
+ // Add to event log
+ addEvent(data);
+
+ // Handle different message types
+ if (data.type === 'status_update' || data.type === 'initial_status') {
+ setOrchestrationStatus(data.status);
+
+ // Update step details from backend (real tool calls and outputs)
+ if (data.step_details) {
+ setStepDetails(data.step_details);
+ }
+
+ // Map orchestration status to executor states based on custom_status
+ const customStatus = data.custom_status || '';
+
+ if (customStatus.includes('Running fraud analysis')) {
+ // Workflow is running - show specialist agents as active
+ setExecutorStates({
+ alert_router: 'completed',
+ usage_pattern_executor: 'running',
+ location_analysis_executor: 'running',
+ billing_charge_executor: 'running',
+ });
+ } else if (customStatus.includes('Awaiting analyst')) {
+ // Waiting for analyst - aggregation complete, waiting for human
+ setExecutorStates({
+ alert_router: 'completed',
+ usage_pattern_executor: 'completed',
+ location_analysis_executor: 'completed',
+ billing_charge_executor: 'completed',
+ fraud_risk_aggregator: 'completed',
+ review_gateway: 'running',
+ });
+ } else if (customStatus.includes('Executing')) {
+ // Executing analyst-approved action
+ setExecutorStates((prev) => ({
+ ...prev,
+ review_gateway: 'completed',
+ fraud_action_executor: 'running',
+ }));
+ } else if (customStatus.includes('Auto-clearing')) {
+ // Auto-clearing low risk
+ setExecutorStates({
+ alert_router: 'completed',
+ usage_pattern_executor: 'completed',
+ location_analysis_executor: 'completed',
+ billing_charge_executor: 'completed',
+ fraud_risk_aggregator: 'completed',
+ auto_clear_executor: 'running',
+ });
+ } else if (customStatus.includes('Sending notification') || customStatus.includes('Sending')) {
+ // Sending final notification
+ setExecutorStates((prev) => ({
+ ...prev,
+ fraud_action_executor: prev.fraud_action_executor === 'running' ? 'completed' : prev.fraud_action_executor,
+ auto_clear_executor: prev.auto_clear_executor === 'running' ? 'completed' : prev.auto_clear_executor,
+ final_notification_executor: 'running',
+ }));
+ } else if (customStatus.includes('Completed') || data.status === 'COMPLETED') {
+ // Completed - mark everything as done
+ setExecutorStates((prev) => ({
+ ...prev,
+ fraud_action_executor: prev.fraud_action_executor === 'running' ? 'completed' : prev.fraud_action_executor,
+ auto_clear_executor: prev.auto_clear_executor === 'running' ? 'completed' : prev.auto_clear_executor,
+ final_notification_executor: 'completed',
+ }));
+ }
+
+ // Check if decision is required
+ if (data.decision_required) {
+ setPendingDecision({
+ instance_id: instanceId,
+ alert_id: selectedAlert?.alert_id,
+ customer_id: selectedAlert?.customer_id,
+ });
+ setWorkflowRunning(false);
+ }
+
+ // Check if completed
+ if (data.status === 'COMPLETED' || data.status === 'FAILED') {
+ setWorkflowRunning(false);
+
+ // Determine which path was taken from the result
+ const actionTaken = data.result?.action_taken || '';
+ const riskScore = data.result?.risk_score || 0;
+
+ // Update step details from result if available
+ if (data.result?.step_details) {
+ setStepDetails(data.result.step_details);
+ }
+
+ // Set the complete final state based on the path taken
+ if (actionTaken === 'auto_clear' || riskScore < 0.6) {
+ // Low risk path: auto-clear
+ setExecutorStates({
+ alert_router: 'completed',
+ usage_pattern_executor: 'completed',
+ location_analysis_executor: 'completed',
+ billing_charge_executor: 'completed',
+ fraud_risk_aggregator: 'completed',
+ auto_clear_executor: 'completed',
+ final_notification_executor: 'completed',
+ });
+ } else {
+ // High risk path: review gateway → fraud action
+ setExecutorStates({
+ alert_router: 'completed',
+ usage_pattern_executor: 'completed',
+ location_analysis_executor: 'completed',
+ billing_charge_executor: 'completed',
+ fraud_risk_aggregator: 'completed',
+ review_gateway: 'completed',
+ fraud_action_executor: 'completed',
+ final_notification_executor: 'completed',
+ });
+ }
+ }
+
+ // Handle result
+ if (data.result) {
+ addEvent({ type: 'workflow_result', ...data.result });
+ }
+ }
+
+ if (data.type === 'decision_submitted') {
+ setPendingDecision(null);
+ setWorkflowRunning(true);
+ setExecutorStates((prev) => ({
+ ...prev,
+ review_gateway: 'completed',
+ fraud_action_executor: 'running',
+ }));
+ }
+ }, [instanceId, selectedAlert, addEvent]);
+
+ const handleStartWorkflow = useCallback(async (alert) => {
+ console.log('Starting workflow for alert:', alert);
+ setSelectedAlert(alert);
+ setWorkflowRunning(true);
+ setEvents([]);
+ setExecutorStates({ alert_router: 'running' });
+ setPendingDecision(null);
+ setInstanceId(null);
+ setOrchestrationStatus(null);
+ setStepDetails({}); // Reset step details for new workflow
+
+ try {
+ const response = await fetch('http://localhost:8001/api/workflow/start', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ alert_id: alert.alert_id,
+ customer_id: alert.customer_id,
+ alert_type: alert.alert_type,
+ description: alert.description,
+ severity: alert.severity,
+ approval_timeout_hours: 0.05, // 3 minutes for demo
+ }),
+ });
+
+ const data = await response.json();
+ console.log('Workflow started:', data);
+
+ // Store instance ID - this triggers WebSocket connection
+ setInstanceId(data.instance_id);
+ addEvent({ type: 'workflow_started', instance_id: data.instance_id, alert_id: alert.alert_id });
+
+ } catch (error) {
+ console.error('Error starting workflow:', error);
+ setWorkflowRunning(false);
+ addEvent({ type: 'error', message: error.message });
+ }
+ }, [addEvent]);
+
+ const handleSubmitDecision = useCallback(async (decision) => {
+ console.log('Submitting decision:', decision);
+
+ try {
+ const response = await fetch('http://localhost:8001/api/workflow/decision', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ instance_id: instanceId,
+ alert_id: selectedAlert?.alert_id,
+ approved_action: decision.approved_action,
+ analyst_notes: decision.analyst_notes,
+ analyst_id: 'analyst_ui',
+ }),
+ });
+
+ const data = await response.json();
+ console.log('Decision submitted:', data);
+ addEvent({ type: 'decision_submitted', action: decision.approved_action });
+
+ } catch (error) {
+ console.error('Error submitting decision:', error);
+ addEvent({ type: 'error', message: error.message });
+ }
+ }, [instanceId, selectedAlert, addEvent]);
+
+ return (
+
+
+
+ {/* App Bar */}
+
+
+
+
+ Durable Fraud Detection Workflow
+
+
+ {instanceId && (
+ }
+ label={`Instance: ${instanceId.substring(0, 20)}...`}
+ color={orchestrationStatus === 'RUNNING' ? 'primary' : orchestrationStatus === 'COMPLETED' ? 'success' : 'default'}
+ size="small"
+ />
+ )}
+
+ Hybrid Workflow + Durable Task
+
+
+
+
+
+ {/* Main Content */}
+
+
+ {/* Left Column - Controls and Decision Panel */}
+
+
+
+ {pendingDecision && (
+
+ )}
+
+
+ {/* Center Column - Workflow Visualization */}
+
+
+
+ Workflow Graph
+
+ {selectedAlert
+ ? `Alert: ${selectedAlert.alert_id} - ${selectedAlert.description}`
+ : 'Select an alert to start'}
+ {orchestrationStatus && ` | Status: ${orchestrationStatus}`}
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/src/components/AnalystDecisionPanel.jsx b/agentic_ai/workflow/fraud_detection_durable/ui/src/components/AnalystDecisionPanel.jsx
new file mode 100644
index 000000000..1c87aea79
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/src/components/AnalystDecisionPanel.jsx
@@ -0,0 +1,146 @@
+import { useState } from 'react';
+import {
+ Paper,
+ Box,
+ Typography,
+ Button,
+ TextField,
+ Select,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ Chip,
+ Alert,
+ Divider,
+} from '@mui/material';
+import GavelIcon from '@mui/icons-material/Gavel';
+import SendIcon from '@mui/icons-material/Send';
+import { ACTION_OPTIONS } from '../constants/workflow';
+
+/**
+ * Panel for analyst to make decisions on fraud alerts (Durable version)
+ * @param {Object} props - Component props
+ * @param {Object} props.decision - Decision request object with instance_id, alert_id, customer_id
+ * @param {Function} props.onSubmit - Callback to submit decision
+ */
+function AnalystDecisionPanel({ decision, onSubmit }) {
+ const [selectedAction, setSelectedAction] = useState(
+ decision.recommended_action || 'block'
+ );
+ const [notes, setNotes] = useState('');
+
+ const handleSubmit = () => {
+ onSubmit({
+ instance_id: decision.instance_id,
+ alert_id: decision.alert_id,
+ customer_id: decision.customer_id,
+ approved_action: selectedAction,
+ analyst_notes: notes || 'Analyst decision from UI',
+ analyst_id: 'analyst_ui',
+ });
+ };
+
+ return (
+
+
+
+ Analyst Review Required
+
+
+
+
+ Human Decision Needed
+
+
+
+
+
+ {/* Risk Assessment */}
+
+
+ Review Required
+
+
+ Alert ID:
+
+
+
+ Customer:
+
+
+
+
+ {/* Instance Info */}
+
+
+ Instance ID
+
+
+ {decision.instance_id}
+
+
+
+
+
+ {/* Decision Form */}
+
+ Your Decision
+
+
+
+ setNotes(e.target.value)}
+ placeholder="Add notes..."
+ sx={{ '& .MuiInputBase-input': { fontSize: '0.875rem' } }}
+ />
+
+ }
+ onClick={handleSubmit}
+ sx={{ mt: 0.5, py: 0.75 }}
+ >
+ Submit Decision
+
+
+ );
+}
+
+export default AnalystDecisionPanel;
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/src/components/ControlPanel.jsx b/agentic_ai/workflow/fraud_detection_durable/ui/src/components/ControlPanel.jsx
new file mode 100644
index 000000000..acacc11f1
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/src/components/ControlPanel.jsx
@@ -0,0 +1,128 @@
+import { useState } from 'react';
+import {
+ Paper,
+ Box,
+ Typography,
+ Button,
+ Select,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ Chip,
+ CircularProgress,
+} from '@mui/material';
+import PlayArrowIcon from '@mui/icons-material/PlayArrow';
+import WarningIcon from '@mui/icons-material/Warning';
+import ErrorIcon from '@mui/icons-material/Error';
+import InfoIcon from '@mui/icons-material/Info';
+import { getSeverityIcon, getSeverityColor } from '../utils/uiHelpers';
+
+/**
+ * Control panel component for selecting alerts and starting workflows
+ * @param {Object} props - Component props
+ * @param {Array} props.alerts - Array of alert objects
+ * @param {Function} props.onStartWorkflow - Callback to start workflow
+ * @param {boolean} props.workflowRunning - Whether workflow is currently running
+ * @param {Object} props.selectedAlert - Currently selected alert object
+ */
+function ControlPanel({ alerts = [], onStartWorkflow, workflowRunning = false, selectedAlert }) {
+ const [selectedAlertId, setSelectedAlertId] = useState('');
+
+ const handleStartClick = () => {
+ const alert = alerts.find((a) => a.alert_id === selectedAlertId);
+ if (alert) {
+ onStartWorkflow(alert);
+ }
+ };
+
+ return (
+
+
+ Workflow Control
+
+
+
+ Select Alert
+
+
+
+ {selectedAlertId && !workflowRunning && (
+
+
+ Description:
+
+
+ {alerts.find((a) => a.alert_id === selectedAlertId)?.description}
+
+
+ a.alert_id === selectedAlertId)?.customer_id}`}
+ size="small"
+ variant="outlined"
+ sx={{ height: 18, fontSize: '0.7rem' }}
+ />
+ a.alert_id === selectedAlertId)?.alert_type}
+ size="small"
+ variant="outlined"
+ sx={{ height: 18, fontSize: '0.7rem' }}
+ />
+
+
+ )}
+
+ : }
+ onClick={handleStartClick}
+ disabled={!selectedAlertId || workflowRunning}
+ sx={{ mt: 0.5, py: 0.75, fontSize: '0.875rem' }}
+ >
+ {workflowRunning ? 'Running...' : 'Start Workflow'}
+
+
+ {selectedAlert && workflowRunning && (
+
+
+ Active Workflow
+
+
+ Processing {selectedAlert.alert_id}
+
+
+ )}
+
+ );
+}
+
+export default ControlPanel;
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/src/components/CustomNode.jsx b/agentic_ai/workflow/fraud_detection_durable/ui/src/components/CustomNode.jsx
new file mode 100644
index 000000000..52d13030e
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/src/components/CustomNode.jsx
@@ -0,0 +1,83 @@
+import { Handle, Position } from 'reactflow';
+import { Box, Typography, Paper, Chip } from '@mui/material';
+import CheckCircleIcon from '@mui/icons-material/CheckCircle';
+import HourglassEmptyIcon from '@mui/icons-material/HourglassEmpty';
+import CircleIcon from '@mui/icons-material/Circle';
+import { getNodeStatusColor, getStatusLabel } from '../utils/uiHelpers';
+
+/**
+ * Gets the appropriate icon for node status
+ * @param {string} status - Node status
+ * @returns {JSX.Element} Icon component
+ */
+const getStatusIcon = (status) => {
+ switch (status) {
+ case 'running':
+ return ;
+ case 'completed':
+ return ;
+ default:
+ return ;
+ }
+};
+
+/**
+ * Custom node component for React Flow workflow visualization
+ * @param {Object} props - Component props
+ * @param {Object} props.data - Node data object
+ */
+function CustomNode({ data = {} }) {
+ const statusColor = getNodeStatusColor(data.status);
+ const isActive = data.status === 'running';
+
+ return (
+
+
+
+
+
+
+ {data.label}
+
+
+
+
+ {data.description && (
+
+ {data.description}
+
+ )}
+
+
+
+
+ );
+}
+
+export default CustomNode;
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/src/components/WorkflowVisualizer.jsx b/agentic_ai/workflow/fraud_detection_durable/ui/src/components/WorkflowVisualizer.jsx
new file mode 100644
index 000000000..e2d399ae3
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/src/components/WorkflowVisualizer.jsx
@@ -0,0 +1,371 @@
+import { useMemo, useEffect, useState, useCallback } from 'react';
+import ReactFlow, {
+ Background,
+ Controls,
+ MiniMap,
+ useNodesState,
+ useEdgesState,
+} from 'reactflow';
+import 'reactflow/dist/style.css';
+import { Box, Popover, Paper, Typography, Chip, Divider, List, ListItem, ListItemIcon, ListItemText } from '@mui/material';
+import FunctionsIcon from '@mui/icons-material/Functions';
+import OutputIcon from '@mui/icons-material/Output';
+import AccessTimeIcon from '@mui/icons-material/AccessTime';
+import CheckCircleIcon from '@mui/icons-material/CheckCircle';
+import HourglassEmptyIcon from '@mui/icons-material/HourglassEmpty';
+import CustomNode from './CustomNode';
+import { EXECUTOR_STATES } from '../constants/workflow';
+import { getNodeStatusColor, getStatusLabel } from '../utils/uiHelpers';
+
+const nodeTypes = {
+ custom: CustomNode,
+};
+
+// Define the workflow graph structure
+const initialNodes = [
+ {
+ id: 'alert_router',
+ type: 'custom',
+ position: { x: 400, y: 50 },
+ data: { label: 'Alert Router', status: 'idle', description: 'Routes alerts to analysts' },
+ },
+ {
+ id: 'usage_pattern_executor',
+ type: 'custom',
+ position: { x: 150, y: 200 },
+ data: { label: 'Usage Analyst', status: 'idle', description: 'Analyzes usage patterns' },
+ },
+ {
+ id: 'location_analysis_executor',
+ type: 'custom',
+ position: { x: 400, y: 200 },
+ data: { label: 'Location Analyst', status: 'idle', description: 'Analyzes location anomalies' },
+ },
+ {
+ id: 'billing_charge_executor',
+ type: 'custom',
+ position: { x: 650, y: 200 },
+ data: { label: 'Billing Analyst', status: 'idle', description: 'Analyzes billing patterns' },
+ },
+ {
+ id: 'fraud_risk_aggregator',
+ type: 'custom',
+ position: { x: 400, y: 350 },
+ data: { label: 'Risk Aggregator', status: 'idle', description: 'Aggregates risk assessment' },
+ },
+ {
+ id: 'review_gateway',
+ type: 'custom',
+ position: { x: 550, y: 500 },
+ data: { label: 'Review Gateway', status: 'idle', description: 'Human analyst review (pauses workflow)' },
+ },
+ {
+ id: 'auto_clear_executor',
+ type: 'custom',
+ position: { x: 250, y: 500 },
+ data: { label: 'Auto Clear', status: 'idle', description: 'Auto-clears low risk' },
+ },
+ {
+ id: 'fraud_action_executor',
+ type: 'custom',
+ position: { x: 550, y: 650 },
+ data: { label: 'Fraud Action', status: 'idle', description: 'Execute fraud action' },
+ },
+ {
+ id: 'final_notification_executor',
+ type: 'custom',
+ position: { x: 400, y: 800 },
+ data: { label: 'Final Notification', status: 'idle', description: 'Send notifications' },
+ },
+];
+
+const initialEdges = [
+ // Fan-out from alert router to analysts
+ { id: 'e1-1', source: 'alert_router', target: 'usage_pattern_executor', animated: true },
+ { id: 'e1-2', source: 'alert_router', target: 'location_analysis_executor', animated: true },
+ { id: 'e1-3', source: 'alert_router', target: 'billing_charge_executor', animated: true },
+
+ // Fan-in to aggregator
+ { id: 'e2-1', source: 'usage_pattern_executor', target: 'fraud_risk_aggregator' },
+ { id: 'e2-2', source: 'location_analysis_executor', target: 'fraud_risk_aggregator' },
+ { id: 'e2-3', source: 'billing_charge_executor', target: 'fraud_risk_aggregator' },
+
+ // Switch case from aggregator
+ { id: 'e3-1', source: 'fraud_risk_aggregator', target: 'review_gateway', label: 'High Risk', style: { stroke: '#f44336' } },
+ { id: 'e3-2', source: 'fraud_risk_aggregator', target: 'auto_clear_executor', label: 'Low Risk', style: { stroke: '#4caf50' } },
+
+ // Review gateway to fraud action (human review happens via request_info, then proceeds)
+ { id: 'e4-1', source: 'review_gateway', target: 'fraud_action_executor', animated: true, style: { stroke: '#ff9800' } },
+
+ // Final paths
+ { id: 'e5-1', source: 'auto_clear_executor', target: 'final_notification_executor' },
+ { id: 'e5-2', source: 'fraud_action_executor', target: 'final_notification_executor' },
+];
+
+/**
+ * Workflow visualizer component using React Flow
+ * Displays the fraud detection workflow as an interactive graph
+ * @param {Object} props - Component props
+ * @param {Object} props.executorStates - Map of executor IDs to their current states
+ * @param {Object} props.stepDetails - Real step details from backend (tool calls, outputs)
+ */
+function WorkflowVisualizer({ executorStates = {}, stepDetails = {} }) {
+ // Popover state
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [selectedNode, setSelectedNode] = useState(null);
+
+ // Static descriptions for nodes (fallback)
+ const nodeDescriptions = useMemo(() => ({
+ alert_router: 'Routes incoming fraud alerts to specialist analysts for parallel investigation.',
+ usage_pattern_executor: 'AI agent that analyzes customer data usage patterns to detect anomalies.',
+ location_analysis_executor: 'AI agent that detects geographic anomalies and suspicious location patterns.',
+ billing_charge_executor: 'AI agent that identifies unusual billing patterns and charge irregularities.',
+ fraud_risk_aggregator: 'Combines analyses from all specialists to calculate overall risk score.',
+ review_gateway: 'Human-in-the-loop checkpoint. Pauses workflow for analyst approval on high-risk cases.',
+ auto_clear_executor: 'Automatically clears alerts with low risk scores (< 0.6).',
+ fraud_action_executor: 'Executes the analyst-approved action (suspend, flag, or additional verification).',
+ final_notification_executor: 'Sends notification to customer and internal teams about the resolution.',
+ }), []);
+
+ // Get step info from backend data or fallback
+ const getStepInfo = useCallback((nodeId) => {
+ const backendData = stepDetails[nodeId];
+
+ if (backendData) {
+ // Real data from backend
+ return {
+ toolCalls: backendData.tool_calls || [],
+ output: backendData.output || '',
+ riskScore: backendData.risk_score,
+ hasRealData: true,
+ };
+ }
+
+ // Fallback static data for nodes without backend info
+ const staticToolCalls = {
+ alert_router: [{ name: 'distribute_alert', result: 'Sent to 3 analysts' }],
+ review_gateway: [{ name: 'wait_for_decision', result: 'Analyst decision pending' }],
+ auto_clear_executor: [{ name: 'clear_alert', result: '' }],
+ fraud_action_executor: [{ name: 'execute_action', result: '' }],
+ final_notification_executor: [{ name: 'send_notification', result: '' }],
+ };
+
+ return {
+ toolCalls: staticToolCalls[nodeId] || [],
+ output: '',
+ hasRealData: false,
+ };
+ }, [stepDetails]);
+
+ // Handle node click
+ const handleNodeClick = useCallback((event, node) => {
+ setAnchorEl(event.target);
+ setSelectedNode(node);
+ }, []);
+
+ const handleClosePopover = useCallback(() => {
+ setAnchorEl(null);
+ setSelectedNode(null);
+ }, []);
+
+ // Update nodes with current executor states
+ const nodes = useMemo(() => {
+ return initialNodes.map((node) => ({
+ ...node,
+ data: {
+ ...node.data,
+ status: executorStates[node.id] || EXECUTOR_STATES.IDLE,
+ },
+ }));
+ }, [executorStates]);
+
+ const [nodesState, setNodes, onNodesChange] = useNodesState(nodes);
+ const [edgesState, setEdges, onEdgesChange] = useEdgesState(initialEdges);
+
+ // Update nodes when executor states change
+ useEffect(() => {
+ setNodes(nodes);
+ }, [nodes, setNodes]);
+
+ // Get status icon
+ const getStatusIcon = (status) => {
+ if (status === 'running') return ;
+ if (status === 'completed') return ;
+ return ;
+ };
+
+ return (
+
+
+
+
+ {
+ const status = node.data.status;
+ if (status === 'running') return '#1976d2';
+ if (status === 'completed') return '#4caf50';
+ return '#9e9e9e';
+ }}
+ />
+
+
+ {/* Node Details Popover */}
+
+ {selectedNode && (() => {
+ const stepInfo = getStepInfo(selectedNode.id);
+ return (
+
+ {/* Header */}
+
+ {getStatusIcon(selectedNode.data.status)}
+
+ {selectedNode.data.label}
+
+
+
+ {/* Status chip + Risk Score */}
+
+
+ {stepInfo.riskScore !== undefined && (
+ = 0.6 ? 'error' : 'success'}
+ variant="outlined"
+ />
+ )}
+
+
+ {/* Description */}
+
+ {nodeDescriptions[selectedNode.id] || selectedNode.data.description}
+
+
+
+
+ {/* Function/Tool Calls */}
+
+
+ {stepInfo.hasRealData ? 'Tool Calls (Real)' : 'Expected Tools'}
+
+
+ {stepInfo.toolCalls.length > 0 ? (
+
+ {stepInfo.toolCalls.map((tc, idx) => (
+
+
+
+
+
+
+
+ {tc.result && (
+
+
+ {tc.result.length > 200 ? tc.result.slice(0, 200) + '...' : tc.result}
+
+
+ )}
+
+ ))}
+
+ ) : (
+
+ No tool calls recorded
+
+ )}
+
+ {/* Output Summary (if available) */}
+ {stepInfo.output && (
+ <>
+
+ Analysis Output
+
+
+
+ {stepInfo.output}
+
+
+ >
+ )}
+
+ );
+ })()}
+
+
+ );
+}
+
+export default WorkflowVisualizer;
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/src/constants/config.js b/agentic_ai/workflow/fraud_detection_durable/ui/src/constants/config.js
new file mode 100644
index 000000000..b5bbabc03
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/src/constants/config.js
@@ -0,0 +1,33 @@
+/**
+ * API configuration constants
+ */
+export const API_CONFIG = {
+ BASE_URL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:8001',
+ WS_URL: import.meta.env.VITE_WS_URL || 'ws://localhost:8001/ws',
+ ENDPOINTS: {
+ ALERTS: '/api/alerts',
+ WORKFLOW_START: '/api/workflow/start',
+ WORKFLOW_DECISION: '/api/workflow/decision',
+ },
+};
+
+/**
+ * WebSocket configuration
+ */
+export const WS_CONFIG = {
+ RECONNECT_DELAY: 3000,
+ MAX_RECONNECT_ATTEMPTS: 10,
+};
+
+/**
+ * Application constants
+ */
+export const APP_CONFIG = {
+ TITLE: import.meta.env.VITE_APP_TITLE || 'Fraud Detection Workflow Visualizer',
+};
+
+export default {
+ API_CONFIG,
+ WS_CONFIG,
+ APP_CONFIG,
+};
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/src/constants/workflow.js b/agentic_ai/workflow/fraud_detection_durable/ui/src/constants/workflow.js
new file mode 100644
index 000000000..07d791540
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/src/constants/workflow.js
@@ -0,0 +1,53 @@
+/**
+ * Workflow event types
+ */
+export const EVENT_TYPES = {
+ WORKFLOW_INITIALIZING: 'workflow_initializing',
+ WORKFLOW_STARTED: 'workflow_started',
+ WORKFLOW_COMPLETED: 'workflow_completed',
+ WORKFLOW_ERROR: 'workflow_error',
+ EXECUTOR_INVOKED: 'executor_invoked',
+ EXECUTOR_COMPLETED: 'executor_completed',
+ DECISION_REQUIRED: 'decision_required',
+ STATUS_CHANGE: 'status_change',
+ WORKFLOW_OUTPUT: 'workflow_output',
+};
+
+/**
+ * Alert severity levels
+ */
+export const SEVERITY_LEVELS = {
+ HIGH: 'high',
+ MEDIUM: 'medium',
+ LOW: 'low',
+};
+
+/**
+ * Executor states
+ */
+export const EXECUTOR_STATES = {
+ IDLE: 'idle',
+ RUNNING: 'running',
+ COMPLETED: 'completed',
+ ERROR: 'error',
+};
+
+/**
+ * Fraud action options
+ */
+export const ACTION_OPTIONS = [
+ { value: 'clear', label: 'Clear - No Action Needed', color: 'success' },
+ { value: 'lock_account', label: 'Lock Account', color: 'error' },
+ { value: 'refund_charges', label: 'Refund Charges', color: 'warning' },
+ { value: 'both', label: 'Lock Account & Refund', color: 'error' },
+];
+
+/**
+ * WebSocket ready states
+ */
+export const WS_READY_STATES = {
+ CONNECTING: 'CONNECTING',
+ OPEN: 'OPEN',
+ CLOSING: 'CLOSING',
+ CLOSED: 'CLOSED',
+};
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/src/index.css b/agentic_ai/workflow/fraud_detection_durable/ui/src/index.css
new file mode 100644
index 000000000..0979488ae
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/src/index.css
@@ -0,0 +1,18 @@
+body {
+ margin: 0;
+ font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ background-color: #f5f5f5;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+#root {
+ height: 100vh;
+ width: 100vw;
+}
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/src/main.jsx b/agentic_ai/workflow/fraud_detection_durable/ui/src/main.jsx
new file mode 100644
index 000000000..8e3e8340e
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/src/main.jsx
@@ -0,0 +1,18 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import App from './App';
+import './index.css';
+
+const rootElement = document.getElementById('root');
+
+if (!rootElement) {
+ throw new Error('Failed to find the root element');
+}
+
+const root = createRoot(rootElement);
+
+root.render(
+
+
+
+);
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/src/theme/index.js b/agentic_ai/workflow/fraud_detection_durable/ui/src/theme/index.js
new file mode 100644
index 000000000..799f49a2f
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/src/theme/index.js
@@ -0,0 +1,84 @@
+import { createTheme } from '@mui/material';
+
+/**
+ * Application theme configuration
+ * Uses Material-UI theme customization
+ */
+export const theme = createTheme({
+ palette: {
+ mode: 'light',
+ primary: {
+ main: '#1976d2',
+ light: '#42a5f5',
+ dark: '#1565c0',
+ contrastText: '#fff',
+ },
+ secondary: {
+ main: '#dc004e',
+ light: '#f33a6a',
+ dark: '#9a0036',
+ contrastText: '#fff',
+ },
+ success: {
+ main: '#4caf50',
+ light: '#81c784',
+ dark: '#388e3c',
+ },
+ warning: {
+ main: '#ff9800',
+ light: '#ffb74d',
+ dark: '#f57c00',
+ },
+ error: {
+ main: '#f44336',
+ light: '#e57373',
+ dark: '#d32f2f',
+ },
+ info: {
+ main: '#2196f3',
+ light: '#64b5f6',
+ dark: '#1976d2',
+ },
+ background: {
+ default: '#f5f5f5',
+ paper: '#ffffff',
+ },
+ },
+ typography: {
+ fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
+ h6: {
+ fontWeight: 500,
+ },
+ subtitle1: {
+ fontWeight: 500,
+ },
+ body1: {
+ fontSize: '0.875rem',
+ },
+ body2: {
+ fontSize: '0.75rem',
+ },
+ },
+ shape: {
+ borderRadius: 8,
+ },
+ components: {
+ MuiButton: {
+ styleOverrides: {
+ root: {
+ textTransform: 'none',
+ borderRadius: 8,
+ },
+ },
+ },
+ MuiPaper: {
+ styleOverrides: {
+ root: {
+ borderRadius: 8,
+ },
+ },
+ },
+ },
+});
+
+export default theme;
diff --git a/agentic_ai/workflow/fraud_detection_durable/ui/src/utils/api.js b/agentic_ai/workflow/fraud_detection_durable/ui/src/utils/api.js
new file mode 100644
index 000000000..26ea9986c
--- /dev/null
+++ b/agentic_ai/workflow/fraud_detection_durable/ui/src/utils/api.js
@@ -0,0 +1,73 @@
+import { API_CONFIG } from '../constants/config';
+
+/**
+ * Fetches alerts from the API
+ * @returns {Promise} Array of alerts
+ */
+export const fetchAlerts = async () => {
+ try {
+ const response = await fetch(`${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.ALERTS}`);
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ const data = await response.json();
+ return data.alerts || [];
+ } catch (error) {
+ console.error('Error loading alerts:', error);
+ throw error;
+ }
+};
+
+/**
+ * Starts a workflow for a given alert
+ * @param {Object} alert - The alert object
+ * @returns {Promise