From 2e5ae9739c261c07ab524fe170a872dde96e4ff7 Mon Sep 17 00:00:00 2001 From: Gage Krumbach Date: Tue, 10 Feb 2026 18:22:49 -0600 Subject: [PATCH 1/2] feat: add capabilities endpoint and enhance AGUI event handling - Introduced a new endpoint for retrieving runner capabilities at `/agentic-sessions/:sessionName/agui/capabilities`. - Implemented the `HandleCapabilities` function to authenticate users, verify permissions, and proxy requests to the runner. - Enhanced AGUI event handling by adding support for custom events and persisting message snapshots for faster reconnections. - Updated the frontend to utilize the new capabilities endpoint and replaced the existing chat component with `CopilotChatPanel` for improved user experience. This update improves the overall functionality and performance of the AG-UI system, allowing for better integration with the runner's capabilities and enhancing user interactions. --- components/backend/routes.go | 3 + components/backend/websocket/agui.go | 43 + components/backend/websocket/agui_proxy.go | 91 + components/backend/websocket/compaction.go | 35 +- components/frontend/package-lock.json | 12683 ++++++++++++---- components/frontend/package.json | 5 + .../copilotkit/[project]/[session]/route.ts | 42 + .../[name]/sessions/[sessionName]/page.tsx | 85 +- .../components/session/CopilotChatPanel.tsx | 51 + .../frontend/src/hooks/use-agui-stream.ts | 16 +- .../frontend/src/services/api/sessions.ts | 23 + .../frontend/src/services/queries/index.ts | 1 + .../src/services/queries/use-capabilities.ts | 40 + components/frontend/src/types/agui.ts | 11 + .../runners/claude-code-runner/adapter.py | 1109 +- .../ag_ui_claude_sdk/__init__.py | 48 + .../ag_ui_claude_sdk/adapter.py | 982 ++ .../ag_ui_claude_sdk/config.py | 40 + .../ag_ui_claude_sdk/handlers.py | 296 + .../ag_ui_claude_sdk/types.py | 6 + .../ag_ui_claude_sdk/utils.py | 413 + .../ambient_runner/__init__.py | 22 + .../claude-code-runner/ambient_runner/app.py | 86 + .../ambient_runner/bridge.py | 108 + .../ambient_runner/bridges/__init__.py | 1 + .../ambient_runner/bridges/claude.py | 137 + .../ambient_runner/bridges/langgraph.py | 119 + .../ambient_runner/endpoints/__init__.py | 1 + .../ambient_runner/endpoints/capabilities.py | 26 + .../ambient_runner/endpoints/feedback.py | 7 + .../ambient_runner/endpoints/health.py | 11 + .../ambient_runner/endpoints/interrupt.py | 23 + .../ambient_runner/endpoints/mcp_status.py | 5 + .../ambient_runner/endpoints/repos.py | 5 + .../ambient_runner/endpoints/run.py | 83 + .../ambient_runner/endpoints/workflow.py | 5 + .../ambient_runner/middleware/__init__.py | 6 + .../ambient_runner/platform/__init__.py | 5 + components/runners/claude-code-runner/auth.py | 41 + .../claude-code-runner/endpoints/__init__.py | 5 + .../endpoints/capabilities.py | 52 + .../claude-code-runner/endpoints/feedback.py | 117 + .../endpoints/mcp_status.py | 205 + .../claude-code-runner/endpoints/repos.py | 364 + .../claude-code-runner/endpoints/state.py | 23 + .../claude-code-runner/endpoints/workflow.py | 174 + components/runners/claude-code-runner/main.py | 1625 +- components/runners/claude-code-runner/mcp.py | 89 + .../claude-code-runner/middleware/__init__.py | 11 + .../middleware/developer_events.py | 58 + .../claude-code-runner/middleware/tracing.py | 82 + .../claude-code-runner/observability.py | 199 + .../runners/claude-code-runner/prompts.py | 36 + .../runners/claude-code-runner/pyproject.toml | 4 +- .../claude-code-runner/tests/conftest.py | 129 + .../claude-code-runner/tests/test_adapter.py | 243 + .../tests/test_bridge_claude.py | 270 + .../tests/test_bridge_langgraph.py | 144 + .../tests/test_capabilities_endpoint.py | 132 + .../tests/test_developer_events.py | 51 + .../tests/test_tracing_middleware.py | 157 + .../runners/claude-code-runner/workspace.py | 42 + .../0006-ambient-runner-sdk-architecture.md | 423 + 63 files changed, 15897 insertions(+), 5452 deletions(-) create mode 100644 components/frontend/src/app/api/copilotkit/[project]/[session]/route.ts create mode 100644 components/frontend/src/components/session/CopilotChatPanel.tsx create mode 100644 components/frontend/src/services/queries/use-capabilities.ts create mode 100644 components/runners/claude-code-runner/ag_ui_claude_sdk/__init__.py create mode 100644 components/runners/claude-code-runner/ag_ui_claude_sdk/adapter.py create mode 100644 components/runners/claude-code-runner/ag_ui_claude_sdk/config.py create mode 100644 components/runners/claude-code-runner/ag_ui_claude_sdk/handlers.py create mode 100644 components/runners/claude-code-runner/ag_ui_claude_sdk/types.py create mode 100644 components/runners/claude-code-runner/ag_ui_claude_sdk/utils.py create mode 100644 components/runners/claude-code-runner/ambient_runner/__init__.py create mode 100644 components/runners/claude-code-runner/ambient_runner/app.py create mode 100644 components/runners/claude-code-runner/ambient_runner/bridge.py create mode 100644 components/runners/claude-code-runner/ambient_runner/bridges/__init__.py create mode 100644 components/runners/claude-code-runner/ambient_runner/bridges/claude.py create mode 100644 components/runners/claude-code-runner/ambient_runner/bridges/langgraph.py create mode 100644 components/runners/claude-code-runner/ambient_runner/endpoints/__init__.py create mode 100644 components/runners/claude-code-runner/ambient_runner/endpoints/capabilities.py create mode 100644 components/runners/claude-code-runner/ambient_runner/endpoints/feedback.py create mode 100644 components/runners/claude-code-runner/ambient_runner/endpoints/health.py create mode 100644 components/runners/claude-code-runner/ambient_runner/endpoints/interrupt.py create mode 100644 components/runners/claude-code-runner/ambient_runner/endpoints/mcp_status.py create mode 100644 components/runners/claude-code-runner/ambient_runner/endpoints/repos.py create mode 100644 components/runners/claude-code-runner/ambient_runner/endpoints/run.py create mode 100644 components/runners/claude-code-runner/ambient_runner/endpoints/workflow.py create mode 100644 components/runners/claude-code-runner/ambient_runner/middleware/__init__.py create mode 100644 components/runners/claude-code-runner/ambient_runner/platform/__init__.py create mode 100644 components/runners/claude-code-runner/endpoints/__init__.py create mode 100644 components/runners/claude-code-runner/endpoints/capabilities.py create mode 100644 components/runners/claude-code-runner/endpoints/feedback.py create mode 100644 components/runners/claude-code-runner/endpoints/mcp_status.py create mode 100644 components/runners/claude-code-runner/endpoints/repos.py create mode 100644 components/runners/claude-code-runner/endpoints/state.py create mode 100644 components/runners/claude-code-runner/endpoints/workflow.py create mode 100644 components/runners/claude-code-runner/mcp.py create mode 100644 components/runners/claude-code-runner/middleware/__init__.py create mode 100644 components/runners/claude-code-runner/middleware/developer_events.py create mode 100644 components/runners/claude-code-runner/middleware/tracing.py create mode 100644 components/runners/claude-code-runner/tests/conftest.py create mode 100644 components/runners/claude-code-runner/tests/test_adapter.py create mode 100644 components/runners/claude-code-runner/tests/test_bridge_claude.py create mode 100644 components/runners/claude-code-runner/tests/test_bridge_langgraph.py create mode 100644 components/runners/claude-code-runner/tests/test_capabilities_endpoint.py create mode 100644 components/runners/claude-code-runner/tests/test_developer_events.py create mode 100644 components/runners/claude-code-runner/tests/test_tracing_middleware.py create mode 100644 docs/adr/0006-ambient-runner-sdk-architecture.md diff --git a/components/backend/routes.go b/components/backend/routes.go index 69f52d096..663dee778 100644 --- a/components/backend/routes.go +++ b/components/backend/routes.go @@ -83,6 +83,9 @@ func registerRoutes(r *gin.Engine) { projectGroup.GET("/agentic-sessions/:sessionName/agui/history", websocket.HandleAGUIHistory) projectGroup.GET("/agentic-sessions/:sessionName/agui/runs", websocket.HandleAGUIRuns) + // Runner capabilities endpoint + projectGroup.GET("/agentic-sessions/:sessionName/agui/capabilities", websocket.HandleCapabilities) + // MCP status endpoint projectGroup.GET("/agentic-sessions/:sessionName/mcp/status", websocket.HandleMCPStatus) diff --git a/components/backend/websocket/agui.go b/components/backend/websocket/agui.go index f7ebfe97a..d44cf50d4 100644 --- a/components/backend/websocket/agui.go +++ b/components/backend/websocket/agui.go @@ -198,6 +198,12 @@ func RouteAGUIEvent(sessionID string, event map[string]interface{}) { // Persist the event (use runID from event, not activeRunState) go persistAGUIEventMap(sessionID, runID, event) + // If this is a MESSAGES_SNAPSHOT, also persist as the latest snapshot + // for fast reconnect (avoids replaying the full event log) + if eventType == types.EventTypeMessagesSnapshot { + go persistMessagesSnapshot(sessionID, event) + } + // Check for terminal events - mark run as complete if isTerminalEventType(eventType) { activeRunState.Status = getTerminalStatusFromType(eventType) @@ -207,6 +213,43 @@ func RouteAGUIEvent(sessionID string, event map[string]interface{}) { } } +// persistMessagesSnapshot writes the latest MESSAGES_SNAPSHOT to a separate +// file for fast reconnect. On reconnect the backend can read this file +// directly instead of replaying all events through the compactor. +func persistMessagesSnapshot(sessionID string, event map[string]interface{}) { + path := fmt.Sprintf("%s/sessions/%s/messages-snapshot.json", StateBaseDir, sessionID) + _ = ensureDir(fmt.Sprintf("%s/sessions/%s", StateBaseDir, sessionID)) + + data, err := json.Marshal(event) + if err != nil { + log.Printf("AGUI: failed to marshal MESSAGES_SNAPSHOT for persistence: %v", err) + return + } + + if err := os.WriteFile(path, data, 0644); err != nil { + log.Printf("AGUI: failed to write MESSAGES_SNAPSHOT: %v", err) + return + } + log.Printf("AGUI: Persisted MESSAGES_SNAPSHOT for session %s (%d bytes)", sessionID, len(data)) +} + +// loadPersistedSnapshot reads the latest MESSAGES_SNAPSHOT from disk. +// Returns nil if no snapshot exists. +func loadPersistedSnapshot(sessionID string) map[string]interface{} { + path := fmt.Sprintf("%s/sessions/%s/messages-snapshot.json", StateBaseDir, sessionID) + data, err := os.ReadFile(path) + if err != nil { + return nil // No snapshot yet + } + + var event map[string]interface{} + if err := json.Unmarshal(data, &event); err != nil { + log.Printf("AGUI: failed to unmarshal persisted MESSAGES_SNAPSHOT: %v", err) + return nil + } + return event +} + // loadCompactedMessages loads pre-compacted messages from completed runs // NOTE: Removed loadCompactedMessages and compactAndPersistRun functions. // We now use "compact-on-read" strategy in streamThreadEvents. diff --git a/components/backend/websocket/agui_proxy.go b/components/backend/websocket/agui_proxy.go index 37a878bc1..ca5fa0638 100644 --- a/components/backend/websocket/agui_proxy.go +++ b/components/backend/websocket/agui_proxy.go @@ -410,6 +410,97 @@ func HandleAGUIInterrupt(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "Interrupt signal sent"}) } +// HandleCapabilities proxies GET /capabilities to the runner. +// Returns the runner's framework capabilities, platform features, and config. +// GET /api/projects/:projectName/agentic-sessions/:sessionName/agui/capabilities +func HandleCapabilities(c *gin.Context) { + projectName := c.Param("projectName") + sessionName := c.Param("sessionName") + + // SECURITY: Authenticate user + reqK8s, _ := handlers.GetK8sClientsForRequest(c) + if reqK8s == nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or missing token"}) + c.Abort() + return + } + + // SECURITY: Verify read permission + ctx := context.Background() + ssar := &authv1.SelfSubjectAccessReview{ + Spec: authv1.SelfSubjectAccessReviewSpec{ + ResourceAttributes: &authv1.ResourceAttributes{ + Group: "vteam.ambient-code", + Resource: "agenticsessions", + Verb: "get", + Namespace: projectName, + Name: sessionName, + }, + }, + } + res, err := reqK8s.AuthorizationV1().SelfSubjectAccessReviews().Create(ctx, ssar, metav1.CreateOptions{}) + if err != nil || !res.Status.Allowed { + log.Printf("Capabilities: User not authorized to read session %s/%s", projectName, sessionName) + c.JSON(http.StatusForbidden, gin.H{"error": "Unauthorized"}) + c.Abort() + return + } + + // Proxy to runner + runnerURL, err := getRunnerEndpoint(projectName, sessionName) + if err != nil { + log.Printf("Capabilities: Failed to get runner endpoint: %v", err) + c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Runner not available"}) + return + } + + capURL := strings.TrimSuffix(runnerURL, "/") + "/capabilities" + log.Printf("Capabilities: Forwarding to runner: %s", capURL) + + req, err := http.NewRequest("GET", capURL, nil) + if err != nil { + log.Printf("Capabilities: Failed to create request: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + client := &http.Client{Timeout: 10 * time.Second} + resp, err := client.Do(req) + if err != nil { + log.Printf("Capabilities: Request failed: %v", err) + // Runner not ready — return minimal default + c.JSON(http.StatusOK, gin.H{ + "framework": "unknown", + "agent_features": []interface{}{}, + "platform_features": []interface{}{}, + "file_system": false, + "mcp": false, + }) + return + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + log.Printf("Capabilities: Runner returned %d: %s", resp.StatusCode, string(body)) + c.JSON(http.StatusOK, gin.H{ + "framework": "unknown", + "agent_features": []interface{}{}, + "platform_features": []interface{}{}, + }) + return + } + + var result map[string]interface{} + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + log.Printf("Capabilities: Failed to decode response: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse runner response"}) + return + } + + c.JSON(http.StatusOK, result) +} + // HandleMCPStatus proxies MCP status requests to runner // GET /api/projects/:projectName/agentic-sessions/:sessionName/mcp/status func HandleMCPStatus(c *gin.Context) { diff --git a/components/backend/websocket/compaction.go b/components/backend/websocket/compaction.go index caa45add9..de94dc1bc 100644 --- a/components/backend/websocket/compaction.go +++ b/components/backend/websocket/compaction.go @@ -379,20 +379,39 @@ func (c *MessageCompactor) handleMessagesSnapshot(event map[string]interface{}) } -// CompactEvents is the main entry point for event compaction +// CompactEvents is the main entry point for event compaction. +// +// Optimisation: if the event stream contains MESSAGES_SNAPSHOT events +// (sent by the AG-UI adapter), we skip to the LAST snapshot and only +// compact events that come after it. This avoids replaying the entire +// event log on reconnect. func CompactEvents(events []map[string]interface{}) []types.Message { - // Count event types to help debug - eventTypeCounts := make(map[string]int) - for _, event := range events { - eventType, _ := event["type"].(string) - eventTypeCounts[eventType]++ + // Find the index of the last MESSAGES_SNAPSHOT event. + // Everything before it is already accounted for in that snapshot. + lastSnapshotIdx := -1 + for i := len(events) - 1; i >= 0; i-- { + if et, _ := events[i]["type"].(string); et == types.EventTypeMessagesSnapshot { + lastSnapshotIdx = i + break + } } compactor := NewMessageCompactor() - for _, event := range events { - compactor.HandleEvent(event) + if lastSnapshotIdx >= 0 { + // Fast path: start from the snapshot, then replay only subsequent events + compactor.HandleEvent(events[lastSnapshotIdx]) + for _, event := range events[lastSnapshotIdx+1:] { + compactor.HandleEvent(event) + } + log.Printf("Compaction: fast-path via MESSAGES_SNAPSHOT at index %d (%d events skipped)", + lastSnapshotIdx, lastSnapshotIdx) + } else { + // Fallback: replay all events + for _, event := range events { + compactor.HandleEvent(event) + } } messages := compactor.GetMessages() diff --git a/components/frontend/package-lock.json b/components/frontend/package-lock.json index 39dddb186..c2bb09ce8 100644 --- a/components/frontend/package-lock.json +++ b/components/frontend/package-lock.json @@ -8,6 +8,11 @@ "name": "ambient-runner-frontend", "version": "0.1.0", "dependencies": { + "@ag-ui/client": "^0.0.44", + "@copilotkit/react-core": "^1.51.2", + "@copilotkit/react-ui": "^1.51.2", + "@copilotkit/runtime": "^1.51.2", + "@copilotkit/runtime-client-gql": "^1.51.2", "@hookform/resolvers": "^5.2.1", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-avatar": "^1.1.10", @@ -55,860 +60,4967 @@ "typescript": "^5" } }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, + "node_modules/@0no-co/graphql.web": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.2.0.tgz", + "integrity": "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==", "license": "MIT", - "engines": { - "node": ">=10" + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "graphql": { + "optional": true + } } }, - "node_modules/@borewit/text-codec": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", - "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", + "node_modules/@ag-ui/client": { + "version": "0.0.44", + "resolved": "https://registry.npmjs.org/@ag-ui/client/-/client-0.0.44.tgz", + "integrity": "sha512-lYOdVSD/PSzVqY6CuWmQxUFoMEF6mo3CYvy22P/Wgs6bROjc6JDs3N0Lu46ZADJPiS1hcoDPtfMwSF2XisftFg==", + "dependencies": { + "@ag-ui/core": "0.0.44", + "@ag-ui/encoder": "0.0.44", + "@ag-ui/proto": "0.0.44", + "@types/uuid": "^10.0.0", + "compare-versions": "^6.1.1", + "fast-json-patch": "^3.1.1", + "rxjs": "7.8.1", + "untruncate-json": "^0.0.1", + "uuid": "^11.1.0", + "zod": "^3.22.4" + } + }, + "node_modules/@ag-ui/client/node_modules/@ag-ui/core": { + "version": "0.0.44", + "resolved": "https://registry.npmjs.org/@ag-ui/core/-/core-0.0.44.tgz", + "integrity": "sha512-xX6oSGZ+yqG/qIhzfbGRcenIKoSzWhrYerRZIFwYqa1keN55Zybbp29/zuauWNy0OwffVnWQ5bhR2mRVqgsk/w==", + "dependencies": { + "rxjs": "7.8.1", + "zod": "^3.22.4" + } + }, + "node_modules/@ag-ui/client/node_modules/@ag-ui/encoder": { + "version": "0.0.44", + "resolved": "https://registry.npmjs.org/@ag-ui/encoder/-/encoder-0.0.44.tgz", + "integrity": "sha512-5jXmWv02ONy7BViMNdx1U9UJAnecAPPefq+Tjva2bXQSZ7NnKmnqLlg/m+d0UFm1Jt5JhIgdgSx2YGg0p8QDxA==", + "dependencies": { + "@ag-ui/core": "0.0.44", + "@ag-ui/proto": "0.0.44" + } + }, + "node_modules/@ag-ui/client/node_modules/@ag-ui/proto": { + "version": "0.0.44", + "resolved": "https://registry.npmjs.org/@ag-ui/proto/-/proto-0.0.44.tgz", + "integrity": "sha512-bp01eCvuq7OOTP+W/gvbllYxhhoHG0X7RLvFonn0sh181O8iD49plnD1v50gr0yiq4Y9N8Hgcue+ia10tiQ1oQ==", + "dependencies": { + "@ag-ui/core": "0.0.44", + "@bufbuild/protobuf": "^2.2.5", + "@protobuf-ts/protoc": "^2.11.1" + } + }, + "node_modules/@ag-ui/client/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@emnapi/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", - "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/@ag-ui/core": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@ag-ui/core/-/core-0.0.42.tgz", + "integrity": "sha512-C2hMg4Gs5oiUDgK9cA2RsTwSSmFZdIsqPklDrFw/Ue+quH6EU3vKp5YoOq7nuaQYO4pO8Em+Z+l5/M5PpcvP1g==", "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" + "rxjs": "7.8.1", + "zod": "^3.22.4" } }, - "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "node_modules/@ag-ui/core/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "optional": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@ag-ui/encoder": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@ag-ui/encoder/-/encoder-0.0.42.tgz", + "integrity": "sha512-97B5MMCSs82t/y41uk2NrLBYFhbvn4kYsKQHMCfy8tjSWubyxh3zP7N9yHo8zJeSPe3WvzTvclyXNiGxSOsorg==", "dependencies": { - "tslib": "^2.4.0" + "@ag-ui/core": "0.0.42", + "@ag-ui/proto": "0.0.42" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/@ag-ui/langgraph": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@ag-ui/langgraph/-/langgraph-0.0.20.tgz", + "integrity": "sha512-MQ35S8IUt5xwhA+NdinVJxBq3AHgpaeDrS92undC9Y3UX09HOUUojZcG46WGgZrxxqdFZNaZcupS4Opuf02DsA==", "dependencies": { - "tslib": "^2.4.0" + "@langchain/core": "^0.3.66", + "@langchain/langgraph-sdk": "^0.1.2", + "partial-json": "^0.1.7", + "rxjs": "7.8.1" + }, + "peerDependencies": { + "@ag-ui/client": ">=0.0.42", + "@ag-ui/core": ">=0.0.42" } }, - "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, + "node_modules/@ag-ui/langgraph/node_modules/@langchain/core": { + "version": "0.3.80", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.80.tgz", + "integrity": "sha512-vcJDV2vk1AlCwSh3aBm/urQ1ZrlXFFBocv11bz/NBUfLWD5/UDNMzwPdaAd2dKvNmTWa9FM2lirLU3+JCf4cRA==", "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "@cfworker/json-schema": "^4.0.2", + "ansi-styles": "^5.0.0", + "camelcase": "6", + "decamelize": "1.2.0", + "js-tiktoken": "^1.0.12", + "langsmith": "^0.3.67", + "mustache": "^4.2.0", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^10.0.0", + "zod": "^3.25.32", + "zod-to-json-schema": "^3.22.3" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18" + } + }, + "node_modules/@ag-ui/langgraph/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@ag-ui/langgraph/node_modules/langsmith": { + "version": "0.3.87", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.87.tgz", + "integrity": "sha512-XXR1+9INH8YX96FKWc5tie0QixWz6tOqAsAKfcJyPkE0xPep+NDz0IQLR32q4bn10QK3LqD2HN6T3n6z1YLW7Q==", + "license": "MIT", + "dependencies": { + "@types/uuid": "^10.0.0", + "chalk": "^4.1.2", + "console-table-printer": "^2.12.1", + "p-queue": "^6.6.2", + "semver": "^7.6.3", + "uuid": "^10.0.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "@opentelemetry/api": "*", + "@opentelemetry/exporter-trace-otlp-proto": "*", + "@opentelemetry/sdk-trace-base": "*", + "openai": "*" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-proto": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + }, + "openai": { + "optional": true + } } }, - "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, + "node_modules/@ag-ui/langgraph/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@ag-ui/langgraph/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@ag-ui/proto": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@ag-ui/proto/-/proto-0.0.42.tgz", + "integrity": "sha512-NDUwSgMnGEqxZGkWIJ1ge5t3Q7Kiddj360x2JAWaIfv9w+7tDJ0pmgyzf3/SXp605aY2wZiDLBtJ6jKZeg1lFg==", + "dependencies": { + "@ag-ui/core": "0.0.42", + "@bufbuild/protobuf": "^2.2.5", + "@protobuf-ts/protoc": "^2.11.1" + } + }, + "node_modules/@ai-sdk/anthropic": { + "version": "2.0.61", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-2.0.61.tgz", + "integrity": "sha512-vJVXVlw+jvvh87ccUgEIJyQoO7+ERhkRyZ3e8NIhVQLHN+Xu5fGCm1/ewc8KCeoWFFh0GWDeN2MmB/cwCEYlbw==", "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.1", + "@ai-sdk/provider-utils": "3.0.20" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, - "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", + "node_modules/@ai-sdk/gateway": { + "version": "2.0.35", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-2.0.35.tgz", + "integrity": "sha512-fMzhC9artgY2s2GgXEWB+cECRJEHHoFJKzDpzsuneguNQ656vydPHhvDdoMjbWW+UtLc4nGf3VwlqG0t4FeQ/w==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.1", + "@ai-sdk/provider-utils": "3.0.20", + "@vercel/oidc": "3.1.0" + }, "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, - "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, + "node_modules/@ai-sdk/google": { + "version": "2.0.52", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-2.0.52.tgz", + "integrity": "sha512-2XUnGi3f7TV4ujoAhA+Fg3idUoG/+Y2xjCRg70a1/m0DH1KSQqYaCboJ1C19y6ZHGdf5KNT20eJdswP6TvrY2g==", "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" + "@ai-sdk/provider": "2.0.1", + "@ai-sdk/provider-utils": "3.0.20" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, - "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, + "node_modules/@ai-sdk/mcp": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/mcp/-/mcp-0.0.8.tgz", + "integrity": "sha512-9y9GuGcZ9/+pMIHfpOCJgZVp+AZMv6TkjX2NVT17SQZvTF2N8LXuCXyoUPyi1PxIxzxl0n463LxxaB2O6olC+Q==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0" + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.17", + "pkce-challenge": "^5.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, - "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, + "node_modules/@ai-sdk/mcp/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", "license": "Apache-2.0", "dependencies": { - "@types/json-schema": "^7.0.15" + "json-schema": "^0.4.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", + "node_modules/@ai-sdk/mcp/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.17.tgz", + "integrity": "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw==", + "license": "Apache-2.0", "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.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.6" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, - "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", + "node_modules/@ai-sdk/openai": { + "version": "2.0.89", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-2.0.89.tgz", + "integrity": "sha512-4+qWkBCbL9HPKbgrUO/F2uXZ8GqrYxHa8SWEYIzxEJ9zvWw3ISr3t1/27O1i8MGSym+PzEyHBT48EV4LAwWaEw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.1", + "@ai-sdk/provider-utils": "3.0.20" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" }, - "funding": { - "url": "https://eslint.org/donate" + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, - "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, + "node_modules/@ai-sdk/provider": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.1.tgz", + "integrity": "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng==", "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "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, + "node_modules/@ai-sdk/provider-utils": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.20.tgz", + "integrity": "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" + "@ai-sdk/provider": "2.0.1", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.6" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.10" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@floating-ui/dom": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", - "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", - "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.7.4" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "node_modules/@borewit/text-codec": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", + "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", "license": "MIT" }, - "node_modules/@hookform/resolvers": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", - "integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==", - "license": "MIT", + "node_modules/@bufbuild/protobuf": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz", + "integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@cfworker/json-schema": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", + "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", + "license": "MIT" + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "license": "Apache-2.0", "dependencies": { - "@standard-schema/utils": "^0.3.0" - }, - "peerDependencies": { - "react-hook-form": "^7.55.0" + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" } }, - "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, + "node_modules/@chevrotain/cst-dts-gen/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" } }, - "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", + "node_modules/@chevrotain/gast/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "license": "Apache-2.0" + }, + "node_modules/@copilotkit/react-core": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/@copilotkit/react-core/-/react-core-1.51.2.tgz", + "integrity": "sha512-7D72R2KqdyK1yfeS1XyWeV8wJ4Jd8mcaXR7/hZKPDAo0eh4HSh0v6SbNLbV4gDkCey4Ep8jGPb/y5RCZgxYPuA==", + "license": "MIT", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" + "@ag-ui/client": "^0.0.42", + "@copilotkit/runtime-client-gql": "1.51.2", + "@copilotkit/shared": "1.51.2", + "@copilotkitnext/core": "1.51.2", + "@copilotkitnext/react": "1.51.2", + "@scarf/scarf": "^1.3.0", + "react-markdown": "^8.0.7", + "untruncate-json": "^0.0.1" }, - "engines": { - "node": ">=18.18.0" + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc", + "zod": ">=3.0.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/@copilotkit/react-core/node_modules/@ag-ui/client": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@ag-ui/client/-/client-0.0.42.tgz", + "integrity": "sha512-zAbP+sZJImR5bUpR2ni7RtuuNZMuesaxviynyIgzKlr1k2VCM49mFpbDUKU4TH4Cneu+Xe7OEnO8qCOCIzBAww==", + "dependencies": { + "@ag-ui/core": "0.0.42", + "@ag-ui/encoder": "0.0.42", + "@ag-ui/proto": "0.0.42", + "@types/uuid": "^10.0.0", + "compare-versions": "^6.1.1", + "fast-json-patch": "^3.1.1", + "rxjs": "7.8.1", + "untruncate-json": "^0.0.1", + "uuid": "^11.1.0", + "zod": "^3.22.4" } }, - "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" - }, + "node_modules/@copilotkit/react-core/node_modules/@ag-ui/client/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "node_modules/@copilotkit/react-core/node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", "license": "MIT", - "optional": true, - "engines": { - "node": ">=18" + "dependencies": { + "@types/unist": "^2" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, + "node_modules/@copilotkit/react-core/node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@copilotkit/react-core/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/@copilotkit/react-core/node_modules/hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==", + "license": "MIT", "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node_modules/@copilotkit/react-core/node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", + "license": "MIT" + }, + "node_modules/@copilotkit/react-core/node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" }, "funding": { - "url": "https://opencollective.com/libvips" + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@copilotkit/react-core/node_modules/mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@copilotkit/react-core/node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0" + }, "funding": { - "url": "https://opencollective.com/libvips" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", - "cpu": [ - "x64" + "node_modules/@copilotkit/react-core/node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/@copilotkit/react-core/node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "funding": { - "url": "https://opencollective.com/libvips" + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/@copilotkit/react-core/node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" + "node_modules/@copilotkit/react-core/node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "funding": { - "url": "https://opencollective.com/libvips" + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" } }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" + "node_modules/@copilotkit/react-core/node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "funding": { - "url": "https://opencollective.com/libvips" + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" + "node_modules/@copilotkit/react-core/node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "funding": { - "url": "https://opencollective.com/libvips" + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" + "node_modules/@copilotkit/react-core/node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "funding": { - "url": "https://opencollective.com/libvips" + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", - "cpu": [ - "s390x" + "node_modules/@copilotkit/react-core/node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/@copilotkit/react-core/node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "funding": { - "url": "https://opencollective.com/libvips" + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" } }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", - "cpu": [ - "x64" + "node_modules/@copilotkit/react-core/node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/@copilotkit/react-core/node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "funding": { - "url": "https://opencollective.com/libvips" + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", - "cpu": [ - "arm64" + "node_modules/@copilotkit/react-core/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/@copilotkit/react-core/node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "funding": { - "url": "https://opencollective.com/libvips" + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" } }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", - "cpu": [ - "x64" + "node_modules/@copilotkit/react-core/node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" + "license": "MIT" + }, + "node_modules/@copilotkit/react-core/node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "funding": { - "url": "https://opencollective.com/libvips" + "license": "MIT" + }, + "node_modules/@copilotkit/react-core/node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^1.0.0" } }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", - "cpu": [ - "arm" + "node_modules/@copilotkit/react-core/node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" + "license": "MIT", + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/@copilotkit/react-core/node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/@copilotkit/react-core/node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/@copilotkit/react-core/node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/@copilotkit/react-core/node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/@copilotkit/react-core/node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/@copilotkit/react-core/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/@copilotkit/react-core/node_modules/react-markdown": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", + "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/prop-types": "^15.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "prop-types": "^15.0.0", + "property-information": "^6.0.0", + "react-is": "^18.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/@copilotkit/react-core/node_modules/remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@copilotkit/react-core/node_modules/remark-rehype": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", + "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-to-hast": "^12.1.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@copilotkit/react-core/node_modules/style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/@copilotkit/react-core/node_modules/unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@copilotkit/react-core/node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@copilotkit/react-core/node_modules/unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@copilotkit/react-core/node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@copilotkit/react-core/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@copilotkit/react-core/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@copilotkit/react-core/node_modules/vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@copilotkit/react-core/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@copilotkit/react-ui": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/@copilotkit/react-ui/-/react-ui-1.51.2.tgz", + "integrity": "sha512-Attu02uG/KkHmOHUAdHlOu98qTjuR4dpv8YTxeDZIkrcDnttpLg4qjSvpZZm5bXP+Yd68ImQv5DCBlEBDpFyNg==", + "license": "MIT", + "dependencies": { + "@copilotkit/react-core": "1.51.2", + "@copilotkit/runtime-client-gql": "1.51.2", + "@copilotkit/shared": "1.51.2", + "@headlessui/react": "^2.2.9", + "react-markdown": "^10.1.0", + "react-syntax-highlighter": "^15.6.1", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.1", + "remark-math": "^6.0.0" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/@copilotkit/runtime": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/@copilotkit/runtime/-/runtime-1.51.2.tgz", + "integrity": "sha512-FeAQdekH1sweQx3KGk1LGJOeCSfnGfBxFgzkEeni0OBRS/eOo92TGk9ZnJaso/hrPQ3L7frl8+sxOYiZ2i0spA==", + "license": "MIT", + "dependencies": { + "@ag-ui/client": "^0.0.42", + "@ag-ui/core": "^0.0.42", + "@ag-ui/langgraph": "^0.0.20", + "@copilotkit/shared": "1.51.2", + "@copilotkitnext/agent": "1.51.2", + "@copilotkitnext/runtime": "1.51.2", + "@graphql-yoga/plugin-defer-stream": "^3.3.1", + "@hono/node-server": "^1.13.5", + "@scarf/scarf": "^1.3.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", + "graphql": "^16.8.1", + "graphql-scalars": "^1.23.0", + "graphql-yoga": "^5.3.1", + "hono": "^4.10.3", + "openai": "^4.85.1", + "partial-json": "^0.1.7", + "pino": "^9.2.0", + "pino-pretty": "^11.2.1", + "reflect-metadata": "^0.2.2", + "rxjs": "7.8.1", + "type-graphql": "2.0.0-rc.1", + "zod": "^3.23.3" + }, + "peerDependencies": { + "@anthropic-ai/sdk": "^0.57.0", + "@copilotkit/shared": "1.51.2", + "@copilotkitnext/agent": "1.51.2", + "@copilotkitnext/runtime": "1.51.2", + "@langchain/aws": ">=0.1.9", + "@langchain/community": ">=0.3.58", + "@langchain/core": ">=0.3.66", + "@langchain/google-gauth": ">=0.1.0", + "@langchain/langgraph-sdk": ">=0.1.2", + "@langchain/openai": ">=0.4.2", + "groq-sdk": ">=0.3.0 <1.0.0", + "langchain": ">=0.3.3", + "openai": "^4.85.1" + }, + "peerDependenciesMeta": { + "@anthropic-ai/sdk": { + "optional": true + }, + "@langchain/aws": { + "optional": true + }, + "@langchain/community": { + "optional": true + }, + "@langchain/google-gauth": { + "optional": true + }, + "@langchain/langgraph-sdk": { + "optional": true + }, + "@langchain/openai": { + "optional": true + }, + "groq-sdk": { + "optional": true + }, + "langchain": { + "optional": true + }, + "openai": { + "optional": true + } + } + }, + "node_modules/@copilotkit/runtime-client-gql": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/@copilotkit/runtime-client-gql/-/runtime-client-gql-1.51.2.tgz", + "integrity": "sha512-ZTOJqkr9uumGGb6gHFg5/jOU2QkrrqDSt72VkqIl6JPIcSkeL4butOGXpkpOzQGGw1/m9N18uwSW917puqSqvQ==", + "license": "MIT", + "dependencies": { + "@copilotkit/runtime": "1.51.2", + "@copilotkit/shared": "1.51.2", + "@urql/core": "^5.0.3", + "untruncate-json": "^0.0.1", + "urql": "^4.1.0" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/@copilotkit/runtime/node_modules/@ag-ui/client": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@ag-ui/client/-/client-0.0.42.tgz", + "integrity": "sha512-zAbP+sZJImR5bUpR2ni7RtuuNZMuesaxviynyIgzKlr1k2VCM49mFpbDUKU4TH4Cneu+Xe7OEnO8qCOCIzBAww==", + "dependencies": { + "@ag-ui/core": "0.0.42", + "@ag-ui/encoder": "0.0.42", + "@ag-ui/proto": "0.0.42", + "@types/uuid": "^10.0.0", + "compare-versions": "^6.1.1", + "fast-json-patch": "^3.1.1", + "rxjs": "7.8.1", + "untruncate-json": "^0.0.1", + "uuid": "^11.1.0", + "zod": "^3.22.4" + } + }, + "node_modules/@copilotkit/runtime/node_modules/@copilotkitnext/runtime": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/@copilotkitnext/runtime/-/runtime-1.51.2.tgz", + "integrity": "sha512-r6dmy7p/uCgBFcyRzw88MresXuD2Xuflj4hli/vPS79KJV2d0d4o6TYzb6NeQ2Jz/9jxyklJ/3Q37sr1NkeIOw==", + "dependencies": { + "@ag-ui/client": "0.0.42", + "@ag-ui/core": "0.0.42", + "@ag-ui/encoder": "0.0.42", + "@copilotkitnext/shared": "1.51.2", + "cors": "^2.8.5", + "express": "^4.21.2", + "hono": "^4.6.13", + "rxjs": "7.8.1" + }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=18" + }, + "peerDependencies": { + "openai": "^5.9.0" + } + }, + "node_modules/@copilotkit/runtime/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@copilotkit/runtime/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@copilotkit/runtime/node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/@copilotkit/runtime/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@copilotkit/runtime/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/@copilotkit/runtime/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@copilotkit/runtime/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/@copilotkit/runtime/node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@copilotkit/runtime/node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@copilotkit/runtime/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@copilotkit/runtime/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@copilotkit/runtime/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@copilotkit/runtime/node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@copilotkit/runtime/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@copilotkit/runtime/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@copilotkit/runtime/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@copilotkit/runtime/node_modules/openai": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@copilotkit/runtime/node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/@copilotkit/runtime/node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@copilotkit/runtime/node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@copilotkit/runtime/node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@copilotkit/runtime/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@copilotkit/runtime/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/@copilotkit/runtime/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@copilotkit/shared": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/@copilotkit/shared/-/shared-1.51.2.tgz", + "integrity": "sha512-h8Jk+w/eDAJOL5l4Sccrz+5KF9BMTzlHBW1d6MYus91Ms4DZCS4UiU90wlUJJS8kOY3jkFS650Rx9v17k56bpg==", + "license": "MIT", + "dependencies": { + "@segment/analytics-node": "^2.1.2", + "chalk": "4.1.2", + "graphql": "^16.8.1", + "uuid": "^10.0.0", + "zod": "^3.23.3" + }, + "peerDependencies": { + "@ag-ui/core": "^0.0.42" + } + }, + "node_modules/@copilotkit/shared/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@copilotkit/shared/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@copilotkitnext/agent": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/@copilotkitnext/agent/-/agent-1.51.2.tgz", + "integrity": "sha512-qGUFt/m+AkA9Y/mzp0Nh5HBbPdrPupzFn6vInvXR1/KCh1+Y0RLZ5QDsGc0SJvh6FMiA6XkEuM4mKp0WimGa+g==", + "dependencies": { + "@ag-ui/client": "0.0.42", + "@ai-sdk/anthropic": "^2.0.22", + "@ai-sdk/google": "^2.0.17", + "@ai-sdk/mcp": "^0.0.8", + "@ai-sdk/openai": "^2.0.42", + "@modelcontextprotocol/sdk": "^1.18.2", + "ai": "^5.0.92", + "rxjs": "7.8.1", + "zod": "^3.25.75" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@copilotkitnext/agent/node_modules/@ag-ui/client": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@ag-ui/client/-/client-0.0.42.tgz", + "integrity": "sha512-zAbP+sZJImR5bUpR2ni7RtuuNZMuesaxviynyIgzKlr1k2VCM49mFpbDUKU4TH4Cneu+Xe7OEnO8qCOCIzBAww==", + "dependencies": { + "@ag-ui/core": "0.0.42", + "@ag-ui/encoder": "0.0.42", + "@ag-ui/proto": "0.0.42", + "@types/uuid": "^10.0.0", + "compare-versions": "^6.1.1", + "fast-json-patch": "^3.1.1", + "rxjs": "7.8.1", + "untruncate-json": "^0.0.1", + "uuid": "^11.1.0", + "zod": "^3.22.4" + } + }, + "node_modules/@copilotkitnext/agent/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@copilotkitnext/core": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/@copilotkitnext/core/-/core-1.51.2.tgz", + "integrity": "sha512-jcQUJSZBFa+7CRaSdegfNMM2UAKa2ryElL9SF/QZwDM7La08OlDggcZgaSIT8p8Ndv6L+UdtG/na/IP5phrBXg==", + "dependencies": { + "@ag-ui/client": "0.0.42", + "@copilotkitnext/shared": "1.51.2", + "rxjs": "7.8.1", + "zod": "^3.25.75", + "zod-to-json-schema": "^3.24.6" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@copilotkitnext/core/node_modules/@ag-ui/client": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@ag-ui/client/-/client-0.0.42.tgz", + "integrity": "sha512-zAbP+sZJImR5bUpR2ni7RtuuNZMuesaxviynyIgzKlr1k2VCM49mFpbDUKU4TH4Cneu+Xe7OEnO8qCOCIzBAww==", + "dependencies": { + "@ag-ui/core": "0.0.42", + "@ag-ui/encoder": "0.0.42", + "@ag-ui/proto": "0.0.42", + "@types/uuid": "^10.0.0", + "compare-versions": "^6.1.1", + "fast-json-patch": "^3.1.1", + "rxjs": "7.8.1", + "untruncate-json": "^0.0.1", + "uuid": "^11.1.0", + "zod": "^3.22.4" + } + }, + "node_modules/@copilotkitnext/core/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@copilotkitnext/react": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/@copilotkitnext/react/-/react-1.51.2.tgz", + "integrity": "sha512-jGlyY6mXp6L5gg/DWTxGa+YgFOI+DR6xEXS1edcoByK/phNDR5wqBkF4fZAMqcumJYv7kCG8tSdut8bUna5kyA==", + "dependencies": { + "@ag-ui/client": "0.0.42", + "@ag-ui/core": "0.0.42", + "@copilotkitnext/core": "1.51.2", + "@copilotkitnext/shared": "1.51.2", + "@copilotkitnext/web-inspector": "1.51.2", + "@lit-labs/react": "^2.0.2", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-tooltip": "^1.2.7", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "katex": "^0.16.22", + "lucide-react": "^0.525.0", + "streamdown": "^1.3.0", + "tailwind-merge": "^3.3.1", + "ts-deepmerge": "^7.0.3", + "tw-animate-css": "^1.3.5", + "use-stick-to-bottom": "^1.1.1", + "zod": "^3.25.75" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@copilotkitnext/react/node_modules/@ag-ui/client": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@ag-ui/client/-/client-0.0.42.tgz", + "integrity": "sha512-zAbP+sZJImR5bUpR2ni7RtuuNZMuesaxviynyIgzKlr1k2VCM49mFpbDUKU4TH4Cneu+Xe7OEnO8qCOCIzBAww==", + "dependencies": { + "@ag-ui/core": "0.0.42", + "@ag-ui/encoder": "0.0.42", + "@ag-ui/proto": "0.0.42", + "@types/uuid": "^10.0.0", + "compare-versions": "^6.1.1", + "fast-json-patch": "^3.1.1", + "rxjs": "7.8.1", + "untruncate-json": "^0.0.1", + "uuid": "^11.1.0", + "zod": "^3.22.4" + } + }, + "node_modules/@copilotkitnext/react/node_modules/lucide-react": { + "version": "0.525.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.525.0.tgz", + "integrity": "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@copilotkitnext/react/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@copilotkitnext/shared": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/@copilotkitnext/shared/-/shared-1.51.2.tgz", + "integrity": "sha512-fAYsRBNvVQkf1psixsl1a+wn/23thbFc8PAifxXQHmWfbU6Mr2XXCkKUnBbv3TtLa0MbvL1RZJ8+K+LJl8Roiw==", + "dependencies": { + "@ag-ui/client": "0.0.42", + "partial-json": "^0.1.7", + "uuid": "^11.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@copilotkitnext/shared/node_modules/@ag-ui/client": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@ag-ui/client/-/client-0.0.42.tgz", + "integrity": "sha512-zAbP+sZJImR5bUpR2ni7RtuuNZMuesaxviynyIgzKlr1k2VCM49mFpbDUKU4TH4Cneu+Xe7OEnO8qCOCIzBAww==", + "dependencies": { + "@ag-ui/core": "0.0.42", + "@ag-ui/encoder": "0.0.42", + "@ag-ui/proto": "0.0.42", + "@types/uuid": "^10.0.0", + "compare-versions": "^6.1.1", + "fast-json-patch": "^3.1.1", + "rxjs": "7.8.1", + "untruncate-json": "^0.0.1", + "uuid": "^11.1.0", + "zod": "^3.22.4" + } + }, + "node_modules/@copilotkitnext/shared/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@copilotkitnext/web-inspector": { + "version": "1.51.2", + "resolved": "https://registry.npmjs.org/@copilotkitnext/web-inspector/-/web-inspector-1.51.2.tgz", + "integrity": "sha512-s07K39TZWweOEblPTi2fx83uM9/xV3lf37rp4SmD4jjPBCc1d3cttHq/fKh/l21LCYrGjK7W7mdPy9swaKYYrA==", + "dependencies": { + "@ag-ui/client": "0.0.42", + "@copilotkitnext/core": "1.51.2", + "lit": "^3.2.0", + "lucide": "^0.525.0", + "marked": "^12.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@copilotkitnext/web-inspector/node_modules/@ag-ui/client": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@ag-ui/client/-/client-0.0.42.tgz", + "integrity": "sha512-zAbP+sZJImR5bUpR2ni7RtuuNZMuesaxviynyIgzKlr1k2VCM49mFpbDUKU4TH4Cneu+Xe7OEnO8qCOCIzBAww==", + "dependencies": { + "@ag-ui/core": "0.0.42", + "@ag-ui/encoder": "0.0.42", + "@ag-ui/proto": "0.0.42", + "@types/uuid": "^10.0.0", + "compare-versions": "^6.1.1", + "fast-json-patch": "^3.1.1", + "rxjs": "7.8.1", + "untruncate-json": "^0.0.1", + "uuid": "^11.1.0", + "zod": "^3.22.4" + } + }, + "node_modules/@copilotkitnext/web-inspector/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@emnapi/core": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", + "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@envelop/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@envelop/core/-/core-5.5.0.tgz", + "integrity": "sha512-nsU1EyJQAStaKHR1ZkB/ug9XBm+WPTliYtdedbJ/L1ykrp7dbbn0srqBeDnZ2mbZVp4hH3d0Fy+Og9OgPWZx+g==", + "license": "MIT", + "dependencies": { + "@envelop/instrumentation": "^1.0.0", + "@envelop/types": "^5.2.1", + "@whatwg-node/promise-helpers": "^1.2.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@envelop/instrumentation": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@envelop/instrumentation/-/instrumentation-1.0.0.tgz", + "integrity": "sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw==", + "license": "MIT", + "dependencies": { + "@whatwg-node/promise-helpers": "^1.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@envelop/types": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@envelop/types/-/types-5.2.1.tgz", + "integrity": "sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg==", + "license": "MIT", + "dependencies": { + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "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.0", + "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/@fastify/busboy": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", + "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", + "license": "MIT" + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.28", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@graphql-tools/executor": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.5.1.tgz", + "integrity": "sha512-n94Qcu875Mji9GQ52n5UbgOTxlgvFJicBPYD+FRks9HKIQpdNPjkkrKZUYNG51XKa+bf03rxNflm4+wXhoHHrA==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "^11.0.0", + "@graphql-typed-document-node/core": "^3.2.0", + "@repeaterjs/repeater": "^3.0.4", + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/executor/node_modules/@graphql-tools/utils": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.0.0.tgz", + "integrity": "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "@whatwg-node/promise-helpers": "^1.0.0", + "cross-inspect": "1.0.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/merge": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.7.tgz", + "integrity": "sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "^11.0.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/merge/node_modules/@graphql-tools/utils": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.0.0.tgz", + "integrity": "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "@whatwg-node/promise-helpers": "^1.0.0", + "cross-inspect": "1.0.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/schema": { + "version": "10.0.31", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.31.tgz", + "integrity": "sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ==", + "license": "MIT", + "dependencies": { + "@graphql-tools/merge": "^9.1.7", + "@graphql-tools/utils": "^11.0.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/schema/node_modules/@graphql-tools/utils": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.0.0.tgz", + "integrity": "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "@whatwg-node/promise-helpers": "^1.0.0", + "cross-inspect": "1.0.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/utils": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.11.0.tgz", + "integrity": "sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "@whatwg-node/promise-helpers": "^1.0.0", + "cross-inspect": "1.0.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-yoga/logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@graphql-yoga/logger/-/logger-2.0.1.tgz", + "integrity": "sha512-Nv0BoDGLMg9QBKy9cIswQ3/6aKaKjlTh87x3GiBg2Z4RrjyrM48DvOOK0pJh1C1At+b0mUIM67cwZcFTDLN4sA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@graphql-yoga/plugin-defer-stream": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@graphql-yoga/plugin-defer-stream/-/plugin-defer-stream-3.18.0.tgz", + "integrity": "sha512-VbvQP6hzSNHXXtX+OlK0DrbzQxE4ifP/PQzXUiECCYFOI8manRz+lmEmI1cL+HS6YlppXbtO7Qeb41MNEWLegA==", + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "^10.11.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "graphql": "^15.2.0 || ^16.0.0", + "graphql-yoga": "^5.18.0" + } + }, + "node_modules/@graphql-yoga/subscription": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@graphql-yoga/subscription/-/subscription-5.0.5.tgz", + "integrity": "sha512-oCMWOqFs6QV96/NZRt/ZhTQvzjkGB4YohBOpKM4jH/lDT4qb7Lex/aGCxpi/JD9njw3zBBtMqxbaC22+tFHVvw==", + "license": "MIT", + "dependencies": { + "@graphql-yoga/typed-event-target": "^3.0.2", + "@repeaterjs/repeater": "^3.0.4", + "@whatwg-node/events": "^0.1.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@graphql-yoga/typed-event-target": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@graphql-yoga/typed-event-target/-/typed-event-target-3.0.2.tgz", + "integrity": "sha512-ZpJxMqB+Qfe3rp6uszCQoag4nSw42icURnBRfFYSOmTgEeOe4rD0vYlbA8spvCu2TlCesNTlEN9BLWtQqLxabA==", + "license": "MIT", + "dependencies": { + "@repeaterjs/repeater": "^3.0.4", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@headlessui/react": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.9.tgz", + "integrity": "sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.26.16", + "@react-aria/focus": "^3.20.2", + "@react-aria/interactions": "^3.25.0", + "@tanstack/react-virtual": "^3.13.9", + "use-sync-external-store": "^1.5.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@hookform/resolvers": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", + "integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.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/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", + "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "mlly": "^1.8.0" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@langchain/core": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.20.tgz", + "integrity": "sha512-rwi7ZMhR336xIewGxVtOVZd63QBzVsV+zg/o1Og82yG4xTWODI8RIczMUATE5YYbEQQcbqsLPmM7vPpXtD5eHQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@cfworker/json-schema": "^4.0.2", + "ansi-styles": "^5.0.0", + "camelcase": "6", + "decamelize": "1.2.0", + "js-tiktoken": "^1.0.12", + "langsmith": ">=0.5.0 <1.0.0", + "mustache": "^4.2.0", + "p-queue": "^6.6.2", + "uuid": "^10.0.0", + "zod": "^3.25.76 || ^4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@langchain/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@langchain/core/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/langgraph-sdk": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-0.1.10.tgz", + "integrity": "sha512-9srSCb2bSvcvehMgjA2sMMwX0o1VUgPN6ghwm5Fwc9JGAKsQa6n1S4eCwy1h4abuYxwajH5n3spBw+4I2WYbgw==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.15", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^9.0.0" + }, + "peerDependencies": { + "@langchain/core": ">=0.2.31 <0.4.0 || ^1.0.0-alpha", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@langchain/core": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@langchain/langgraph-sdk/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@lit-labs/react": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-2.1.3.tgz", + "integrity": "sha512-OD9h2JynerBQUMNzb563jiVpxfvPF0HjQkKY2mx0lpVYvD7F+rtJpOGz6ek+6ufMidV3i+MPT9SX62OKWHFrQg==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/react": "^1.0.3" + } + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.5.1.tgz", + "integrity": "sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/react": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@lit/react/-/react-1.0.8.tgz", + "integrity": "sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==", + "license": "BSD-3-Clause", + "peerDependencies": { + "@types/react": "17 || 18 || 19" + } + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz", + "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0" + } + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@lukeed/uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@lukeed/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==", + "license": "MIT", + "dependencies": { + "@lukeed/csprng": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.3.tgz", + "integrity": "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==", + "license": "MIT", + "dependencies": { + "langium": "3.3.1" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", + "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.5.tgz", + "integrity": "sha512-CRSCPJiSZoi4Pn69RYBDI9R7YK2g59vLexPQFXY0eyw+ILevIenCywzg+DqmlBik9zszEnw2HLFOUlLAcJbL7g==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.2.tgz", + "integrity": "sha512-lkLrRVxcftuOsJNhWatf1P2hNVfh98k/omQHrCEPPriUypR6RcS13IvLdIrEvkm9AH2Nu2YpR5vLqBuy6twH3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.5.tgz", + "integrity": "sha512-eK7Wdm3Hjy/SCL7TevlH0C9chrpeOYWx2iR7guJDaz4zEQKWcS1IMVfMb9UKBFMg1XgzcPTYPIp1Vcpukkjg6Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.5.tgz", + "integrity": "sha512-foQscSHD1dCuxBmGkbIr6ScAUF6pRoDZP6czajyvmXPAOFNnQUJu2Os1SGELODjKp/ULa4fulnBWoHV3XdPLfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.5.tgz", + "integrity": "sha512-qNIb42o3C02ccIeSeKjacF3HXotGsxh/FMk/rSRmCzOVMtoWH88odn2uZqF8RLsSUWHcAqTgYmPD3pZ03L9ZAA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.5.tgz", + "integrity": "sha512-U+kBxGUY1xMAzDTXmuVMfhaWUZQAwzRaHJ/I6ihtR5SbTVUEaDRiEU9YMjy1obBWpdOBuk1bcm+tsmifYSygfw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.5.tgz", + "integrity": "sha512-gq2UtoCpN7Ke/7tKaU7i/1L7eFLfhMbXjNghSv0MVGF1dmuoaPeEVDvkDuO/9LVa44h5gqpWeJ4mRRznjDv7LA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.5.tgz", + "integrity": "sha512-bQWSE729PbXT6mMklWLf8dotislPle2L70E9q6iwETYEOt092GDn0c+TTNj26AjmeceSsC4ndyGsK5nKqHYXjQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.5.tgz", + "integrity": "sha512-LZli0anutkIllMtTAWZlDqdfvjWX/ch8AFK5WgkNTvaqwlouiD1oHM+WW8RXMiL0+vAkAJyAGEzPPjO+hnrSNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.5.tgz", + "integrity": "sha512-7is37HJTNQGhjPpQbkKjKEboHYQnCgpVt/4rBrrln0D9nderNxZ8ZWs8w1fAtzUx7wEyYjQ+/13myFgFj6K2Ng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, + "node_modules/@protobuf-ts/protoc": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/protoc/-/protoc-2.11.1.tgz", + "integrity": "sha512-mUZJaV0daGO6HUX90o/atzQ6A7bbN2RSuHtdwo8SSF2Qoe3zHwa4IHyCN1evftTeHfLmdz+45qo47sL+5P8nyg==", + "license": "Apache-2.0", + "bin": { + "protoc": "protoc.js" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz", + "integrity": "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collapsible": "1.1.12", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.11.tgz", + "integrity": "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.3", + "@radix-ui/react-primitive": "2.1.4", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-is-hydrated": "0.1.0", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar/node_modules/@radix-ui/react-context": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", + "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", + "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz", + "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.8.tgz", + "integrity": "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.3", + "@radix-ui/react-primitive": "2.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-context": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", + "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", + "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", + "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.5.0" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, + "node_modules/@react-aria/focus": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.4.tgz", + "integrity": "sha512-6gz+j9ip0/vFRTKJMl3R30MHopn4i19HqqLfSQfElxJD+r9hBnYG1Q6Wd/kl/WRR1+CALn2F+rn06jUnf5sT8Q==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/interactions": "^3.27.0", + "@react-aria/utils": "^3.33.0", + "@react-types/shared": "^3.33.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.27.0.tgz", + "integrity": "sha512-D27pOy+0jIfHK60BB26AgqjjRFOYdvVSkwC31b2LicIzRCSPOSP06V4gMHuGmkhNTF4+YWDi1HHYjxIvMeiSlA==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-aria/utils": "^3.33.0", + "@react-stately/flags": "^3.1.2", + "@react-types/shared": "^3.33.0", + "@swc/helpers": "^0.5.0" }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, + "node_modules/@react-aria/ssr": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz", + "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==", + "license": "Apache-2.0", "dependencies": { - "@emnapi/runtime": "^1.2.0" + "@swc/helpers": "^0.5.0" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">= 12" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node_modules/@react-aria/utils": { + "version": "3.33.0", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.33.0.tgz", + "integrity": "sha512-yvz7CMH8d2VjwbSa5nGXqjU031tYhD8ddax95VzJsHSPyqHDEGfxul8RkhGV6oO7bVqZxVs6xY66NIgae+FHjw==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-stately/flags": "^3.1.2", + "@react-stately/utils": "^3.11.0", + "@react-types/shared": "^3.33.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "node_modules/@react-stately/flags": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz", + "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" } }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node_modules/@react-stately/utils": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.11.0.tgz", + "integrity": "sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, - "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==", + "node_modules/@react-types/shared": { + "version": "3.33.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.33.0.tgz", + "integrity": "sha512-xuUpP6MyuPmJtzNOqF5pzFUIHH2YogyOQfUQHag54PRmWB7AbjuGWBUv0l1UDmz6+AbzAYGmDVAzcRDOu2PFpw==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@repeaterjs/repeater": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz", + "integrity": "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==", + "license": "MIT" + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.15.0.tgz", + "integrity": "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" + }, + "node_modules/@segment/analytics-core": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@segment/analytics-core/-/analytics-core-1.8.2.tgz", + "integrity": "sha512-5FDy6l8chpzUfJcNlIcyqYQq4+JTUynlVoCeCUuVz+l+6W0PXg+ljKp34R4yLVCcY5VVZohuW+HH0VLWdwYVAg==", "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "@lukeed/uuid": "^2.0.0", + "@segment/analytics-generic-utils": "1.2.0", + "dset": "^3.1.4", + "tslib": "^2.4.1" } }, - "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, + "node_modules/@segment/analytics-generic-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@segment/analytics-generic-utils/-/analytics-generic-utils-1.2.0.tgz", + "integrity": "sha512-DfnW6mW3YQOLlDQQdR89k4EqfHb0g/3XvBXkovH1FstUN93eL1kfW9CsDcVQyH3bAC5ZsFyjA/o/1Q2j0QeoWw==", "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "tslib": "^2.4.1" } }, - "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==", - "dev": true, + "node_modules/@segment/analytics-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@segment/analytics-node/-/analytics-node-2.3.0.tgz", + "integrity": "sha512-fOXLL8uY0uAWw/sTLmezze80hj8YGgXXlAfvSS6TUmivk4D/SP0C0sxnbpFdkUzWg2zT64qWIZj26afEtSnxUA==", "license": "MIT", + "dependencies": { + "@lukeed/uuid": "^2.0.0", + "@segment/analytics-core": "1.8.2", + "@segment/analytics-generic-utils": "1.2.0", + "buffer": "^6.0.3", + "jose": "^5.1.0", + "node-fetch": "^2.6.7", + "tslib": "^2.4.1" + }, "engines": { - "node": ">=6.0.0" + "node": ">=20" } }, - "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==", - "dev": true, - "license": "MIT" + "node_modules/@segment/analytics-node/node_modules/jose": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } }, - "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==", - "dev": true, + "node_modules/@shikijs/core": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.22.0.tgz", + "integrity": "sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA==", "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@shikijs/types": "3.22.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, + "node_modules/@shikijs/engine-javascript": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.22.0.tgz", + "integrity": "sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw==", "license": "MIT", - "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "@shikijs/types": "3.22.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.4" } }, - "node_modules/@next/env": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.5.tgz", - "integrity": "sha512-CRSCPJiSZoi4Pn69RYBDI9R7YK2g59vLexPQFXY0eyw+ILevIenCywzg+DqmlBik9zszEnw2HLFOUlLAcJbL7g==", + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.22.0.tgz", + "integrity": "sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.22.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.22.0.tgz", + "integrity": "sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.22.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.22.0.tgz", + "integrity": "sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.22.0" + } + }, + "node_modules/@shikijs/types": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.22.0.tgz", + "integrity": "sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "license": "MIT" }, - "node_modules/@next/eslint-plugin-next": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.2.tgz", - "integrity": "sha512-lkLrRVxcftuOsJNhWatf1P2hNVfh98k/omQHrCEPPriUypR6RcS13IvLdIrEvkm9AH2Nu2YpR5vLqBuy6twH3Q==", + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", + "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", "dev": true, "license": "MIT", "dependencies": { - "fast-glob": "3.3.1" + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.17" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", + "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-x64": "4.1.17", + "@tailwindcss/oxide-freebsd-x64": "4.1.17", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-x64-musl": "4.1.17", + "@tailwindcss/oxide-wasm32-wasi": "4.1.17", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", + "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.5.tgz", - "integrity": "sha512-eK7Wdm3Hjy/SCL7TevlH0C9chrpeOYWx2iR7guJDaz4zEQKWcS1IMVfMb9UKBFMg1XgzcPTYPIp1Vcpukkjg6Q==", + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", + "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -918,13 +5030,14 @@ "node": ">= 10" } }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.5.tgz", - "integrity": "sha512-foQscSHD1dCuxBmGkbIr6ScAUF6pRoDZP6czajyvmXPAOFNnQUJu2Os1SGELODjKp/ULa4fulnBWoHV3XdPLfA==", + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", + "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -934,29 +5047,31 @@ "node": ">= 10" } }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.5.tgz", - "integrity": "sha512-qNIb42o3C02ccIeSeKjacF3HXotGsxh/FMk/rSRmCzOVMtoWH88odn2uZqF8RLsSUWHcAqTgYmPD3pZ03L9ZAA==", + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", + "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", "cpu": [ - "arm64" + "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "freebsd" ], "engines": { "node": ">= 10" } }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.5.tgz", - "integrity": "sha512-U+kBxGUY1xMAzDTXmuVMfhaWUZQAwzRaHJ/I6ihtR5SbTVUEaDRiEU9YMjy1obBWpdOBuk1bcm+tsmifYSygfw==", + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", + "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", "cpu": [ - "arm64" + "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -966,13 +5081,14 @@ "node": ">= 10" } }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.5.tgz", - "integrity": "sha512-gq2UtoCpN7Ke/7tKaU7i/1L7eFLfhMbXjNghSv0MVGF1dmuoaPeEVDvkDuO/9LVa44h5gqpWeJ4mRRznjDv7LA==", + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", + "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", "cpu": [ - "x64" + "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -982,13 +5098,14 @@ "node": ">= 10" } }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.5.tgz", - "integrity": "sha512-bQWSE729PbXT6mMklWLf8dotislPle2L70E9q6iwETYEOt092GDn0c+TTNj26AjmeceSsC4ndyGsK5nKqHYXjQ==", + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", + "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", "cpu": [ - "x64" + "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -998,1266 +5115,1024 @@ "node": ">= 10" } }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.5.tgz", - "integrity": "sha512-LZli0anutkIllMtTAWZlDqdfvjWX/ch8AFK5WgkNTvaqwlouiD1oHM+WW8RXMiL0+vAkAJyAGEzPPjO+hnrSNQ==", + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", + "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", "cpu": [ - "arm64" + "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": ">= 10" } }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.5.tgz", - "integrity": "sha512-7is37HJTNQGhjPpQbkKjKEboHYQnCgpVt/4rBrrln0D9nderNxZ8ZWs8w1fAtzUx7wEyYjQ+/13myFgFj6K2Ng==", + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", + "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": ">= 10" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.4.0" - } - }, - "node_modules/@radix-ui/number": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", - "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", - "license": "MIT" - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-accordion": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz", - "integrity": "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collapsible": "1.1.12", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", - "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-avatar": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.11.tgz", - "integrity": "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-context": "1.1.3", - "@radix-ui/react-primitive": "2.1.4", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-is-hydrated": "0.1.0", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-avatar/node_modules/@radix-ui/react-context": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", - "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-avatar/node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", + "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@radix-ui/react-slot": "1.2.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@emnapi/core": "^1.6.0", + "@emnapi/runtime": "^1.6.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.7", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@radix-ui/react-checkbox": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", - "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.6.0", + "dev": true, + "inBundle": true, "license": "MIT", + "optional": true, "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" } }, - "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", - "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.6.0", + "dev": true, + "inBundle": true, "license": "MIT", + "optional": true, "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "tslib": "^2.4.0" } }, - "node_modules/@radix-ui/react-collection": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", - "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "dev": true, + "inBundle": true, "license": "MIT", + "optional": true, "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "tslib": "^2.4.0" } }, - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.0.7", + "dev": true, + "inBundle": true, "license": "MIT", + "optional": true, "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@tybys/wasm-util": "^0.10.1" } }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "dev": true, + "inBundle": true, "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", + "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", + "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "node_modules/@tailwindcss/postcss": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.17.tgz", + "integrity": "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==", + "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.17", + "@tailwindcss/oxide": "4.1.17", + "postcss": "^8.4.41", + "tailwindcss": "4.1.17" } }, - "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", - "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "node_modules/@tanstack/query-core": { + "version": "5.90.10", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.10.tgz", + "integrity": "sha512-EhZVFu9rl7GfRNuJLJ3Y7wtbTnENsvzp+YpcAV7kCYiXni1v8qZh++lpw4ch4rrwC0u/EZRnBHIehzCGzwXDSQ==", "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.16", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "node_modules/@tanstack/query-devtools": { + "version": "5.90.1", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.90.1.tgz", + "integrity": "sha512-GtINOPjPUH0OegJExZ70UahT9ykmAhmtNVcmtdnOZbxLwT7R5OmRztR5Ahe3/Cu7LArEmR6/588tAycuaWb1xQ==", "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "node_modules/@tanstack/react-query": { + "version": "5.90.10", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.10.tgz", + "integrity": "sha512-BKLss9Y8PQ9IUjPYQiv3/Zmlx92uxffUOX8ZZNoQlCIZBJPT5M+GOMQj7xislvVQ6l1BstBjcX0XB/aHfFYVNw==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1" + "@tanstack/query-core": "5.90.10" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "react": "^18 || ^19" } }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "node_modules/@tanstack/react-query-devtools": { + "version": "5.90.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.90.2.tgz", + "integrity": "sha512-vAXJzZuBXtCQtrY3F/yUNJCV4obT/A/n81kb3+YqLbro5Z2+phdAbceO+deU3ywPw8B42oyJlp4FhO0SoivDFQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" + "@tanstack/query-devtools": "5.90.1" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "peerDependencies": { + "@tanstack/react-query": "^5.90.2", + "react": "^18 || ^19" } }, - "node_modules/@radix-ui/react-label": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz", - "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==", + "node_modules/@tanstack/react-virtual": { + "version": "3.13.18", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.18.tgz", + "integrity": "sha512-dZkhyfahpvlaV0rIKnvQiVoWPyURppl6w4m9IwMDpuIjcJ1sD9YGWrt0wISvgU7ewACXx2Ct46WPgI6qAD4v6A==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.4" + "@tanstack/virtual-core": "3.13.18" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "node_modules/@tanstack/virtual-core": { + "version": "3.13.18", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.18.tgz", + "integrity": "sha512-Mx86Hqu1k39icq2Zusq+Ey2J6dDWTjDvEv43PJtRCoEYTLyfaPnxIQ6iy7YAOK0NV/qOEmZQ/uCufrppZxTgcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.4" + "debug": "^4.4.3", + "token-types": "^6.1.1" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": ">=18" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/@radix-ui/react-menu": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", - "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "tslib": "^2.4.0" } }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "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": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@types/d3-selection": "*" } }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", - "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "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": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-rect": "1.1.1", - "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/d3-selection": "*" } }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "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": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/d3-array": "*", + "@types/geojson": "*" } }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "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": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/d3-selection": "*" } }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "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": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/d3-dsv": "*" } }, - "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "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": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@types/geojson": "*" } }, - "node_modules/@radix-ui/react-progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.8.tgz", - "integrity": "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==", + "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": { - "@radix-ui/react-context": "1.1.3", - "@radix-ui/react-primitive": "2.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/d3-color": "*" } }, - "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-context": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", - "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", + "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", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "dependencies": { + "@types/d3-time": "*" } }, - "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "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.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/d3-path": "*" } }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", - "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "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": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/d3-selection": "*" } }, - "node_modules/@radix-ui/react-select": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", - "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "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": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" } }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "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==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@types/estree": "*" } }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "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/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@types/unist": "*" } }, - "node_modules/@radix-ui/react-tabs": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", - "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "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==", + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/unist": "*" } }, - "node_modules/@radix-ui/react-toast": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", - "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz", + "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "undici-types": "~6.21.0" } }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", - "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-visually-hidden": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "@types/node": "*", + "form-data": "^4.0.4" } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "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.6", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.6.tgz", + "integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "csstype": "^3.2.2" } }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "devOptional": true, "license": "MIT", "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@types/react": "^19.2.0" } }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "license": "MIT" + }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz", + "integrity": "sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==", + "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.47.0", + "@typescript-eslint/type-utils": "8.47.0", + "@typescript-eslint/utils": "8.47.0", + "@typescript-eslint/visitor-keys": "8.47.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.47.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" } }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "node_modules/@typescript-eslint/parser": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.47.0.tgz", + "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==", + "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" + "@typescript-eslint/scope-manager": "8.47.0", + "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/typescript-estree": "8.47.0", + "@typescript-eslint/visitor-keys": "8.47.0", + "debug": "^4.3.4" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz", + "integrity": "sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==", + "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" + "@typescript-eslint/tsconfig-utils": "^8.47.0", + "@typescript-eslint/types": "^8.47.0", + "debug": "^4.3.4" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@radix-ui/react-use-is-hydrated": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", - "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz", + "integrity": "sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==", + "dev": true, "license": "MIT", "dependencies": { - "use-sync-external-store": "^1.5.0" + "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/visitor-keys": "8.47.0" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz", + "integrity": "sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==", + "dev": true, "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", - "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", - "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz", + "integrity": "sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==", + "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/rect": "1.1.1" + "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/typescript-estree": "8.47.0", + "@typescript-eslint/utils": "8.47.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "node_modules/@typescript-eslint/types": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz", + "integrity": "sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==", + "dev": true, "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", - "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz", + "integrity": "sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==", + "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.3" + "@typescript-eslint/project-service": "8.47.0", + "@typescript-eslint/tsconfig-utils": "8.47.0", + "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/visitor-keys": "8.47.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@radix-ui/rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", - "license": "MIT" - }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.15.0.tgz", - "integrity": "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } }, - "node_modules/@standard-schema/utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", - "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", - "license": "MIT" + "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", "dependencies": { - "tslib": "^2.8.0" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@tailwindcss/node": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", - "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", + "node_modules/@typescript-eslint/utils": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz", + "integrity": "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/remapping": "^2.3.4", - "enhanced-resolve": "^5.18.3", - "jiti": "^2.6.1", - "lightningcss": "1.30.2", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.17" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.47.0", + "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/typescript-estree": "8.47.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", - "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz", + "integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.47.0", + "eslint-visitor-keys": "^4.2.1" + }, "engines": { - "node": ">= 10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.17", - "@tailwindcss/oxide-darwin-arm64": "4.1.17", - "@tailwindcss/oxide-darwin-x64": "4.1.17", - "@tailwindcss/oxide-freebsd-x64": "4.1.17", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", - "@tailwindcss/oxide-linux-x64-musl": "4.1.17", - "@tailwindcss/oxide-wasm32-wasi": "4.1.17", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", - "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", "cpu": [ - "arm64" + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" ], - "engines": { - "node": ">= 10" - } + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", - "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", "cpu": [ "arm64" ], @@ -2266,15 +6141,12 @@ "optional": true, "os": [ "darwin" - ], - "engines": { - "node": ">= 10" - } + ] }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", - "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", "cpu": [ "x64" ], @@ -2283,15 +6155,12 @@ "optional": true, "os": [ "darwin" - ], - "engines": { - "node": ">= 10" - } + ] }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", - "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", "cpu": [ "x64" ], @@ -2300,15 +6169,26 @@ "optional": true, "os": [ "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" ], - "engines": { - "node": ">= 10" - } + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", - "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", "cpu": [ "arm" ], @@ -2317,15 +6197,26 @@ "optional": true, "os": [ "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" ], - "engines": { - "node": ">= 10" - } + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", - "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", "cpu": [ "arm64" ], @@ -2334,32 +6225,68 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10" - } + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", - "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", "cpu": [ - "arm64" + "s390x" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10" - } + ] }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", - "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", "cpu": [ "x64" ], @@ -2368,15 +6295,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10" - } + ] }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", - "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", "cpu": [ "x64" ], @@ -2385,23 +6309,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10" - } + ] }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", - "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", "cpu": [ "wasm32" ], @@ -2409,1472 +6322,1647 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.6.0", - "@emnapi/runtime": "^1.6.0", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.0.7", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.4.0" + "@napi-rs/wasm-runtime": "^0.2.11" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.6.0", + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], "dev": true, - "inBundle": true, "license": "MIT", "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } + "os": [ + "win32" + ] }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.6.0", + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], "dev": true, - "inBundle": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } + "os": [ + "win32" + ] }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], "dev": true, - "inBundle": true, "license": "MIT", "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@urql/core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.2.0.tgz", + "integrity": "sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==", + "license": "MIT", "dependencies": { - "tslib": "^2.4.0" + "@0no-co/graphql.web": "^1.0.13", + "wonka": "^6.3.2" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.7", - "dev": true, - "inBundle": true, + "node_modules/@vercel/oidc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.1.0.tgz", + "integrity": "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==", + "license": "Apache-2.0", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@whatwg-node/disposablestack": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz", + "integrity": "sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==", "license": "MIT", - "optional": true, "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", - "@tybys/wasm-util": "^0.10.1" + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "dev": true, - "inBundle": true, + "node_modules/@whatwg-node/events": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.2.tgz", + "integrity": "sha512-ApcWxkrs1WmEMS2CaLLFUEem/49erT3sxIVjpzU5f6zmVcnijtDSrhoK2zVobOIikZJdH63jdAXOrvjf6eOUNQ==", "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.1", - "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", - "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@whatwg-node/fetch": { + "version": "0.10.13", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.13.tgz", + "integrity": "sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@whatwg-node/node-fetch": "^0.8.3", + "urlpattern-polyfill": "^10.0.0" + }, "engines": { - "node": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", - "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@whatwg-node/node-fetch": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.8.5.tgz", + "integrity": "sha512-4xzCl/zphPqlp9tASLVeUhB5+WJHbuWGYpfoC2q1qh5dw0AqZBW7L27V5roxYWijPxj4sspRAAoOH3d2ztaHUQ==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@fastify/busboy": "^3.1.1", + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/promise-helpers": "^1.3.2", + "tslib": "^2.6.3" + }, "engines": { - "node": ">= 10" + "node": ">=18.0.0" } }, - "node_modules/@tailwindcss/postcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.17.tgz", - "integrity": "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==", - "dev": true, + "node_modules/@whatwg-node/promise-helpers": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz", + "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==", "license": "MIT", "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.17", - "@tailwindcss/oxide": "4.1.17", - "postcss": "^8.4.41", - "tailwindcss": "4.1.17" + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@tanstack/query-core": { - "version": "5.90.10", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.10.tgz", - "integrity": "sha512-EhZVFu9rl7GfRNuJLJ3Y7wtbTnENsvzp+YpcAV7kCYiXni1v8qZh++lpw4ch4rrwC0u/EZRnBHIehzCGzwXDSQ==", + "node_modules/@whatwg-node/server": { + "version": "0.10.18", + "resolved": "https://registry.npmjs.org/@whatwg-node/server/-/server-0.10.18.tgz", + "integrity": "sha512-kMwLlxUbduttIgaPdSkmEarFpP+mSY8FEm+QWMBRJwxOHWkri+cxd8KZHO9EMrB9vgUuz+5WEaCawaL5wGVoXg==", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" + "dependencies": { + "@envelop/instrumentation": "^1.0.0", + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/fetch": "^0.10.13", + "@whatwg-node/promise-helpers": "^1.3.2", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@tanstack/query-devtools": { - "version": "5.90.1", - "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.90.1.tgz", - "integrity": "sha512-GtINOPjPUH0OegJExZ70UahT9ykmAhmtNVcmtdnOZbxLwT7R5OmRztR5Ahe3/Cu7LArEmR6/588tAycuaWb1xQ==", + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" } }, - "node_modules/@tanstack/react-query": { - "version": "5.90.10", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.10.tgz", - "integrity": "sha512-BKLss9Y8PQ9IUjPYQiv3/Zmlx92uxffUOX8ZZNoQlCIZBJPT5M+GOMQj7xislvVQ6l1BstBjcX0XB/aHfFYVNw==", + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.90.10" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "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": { - "react": "^18 || ^19" + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@tanstack/react-query-devtools": { - "version": "5.90.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.90.2.tgz", - "integrity": "sha512-vAXJzZuBXtCQtrY3F/yUNJCV4obT/A/n81kb3+YqLbro5Z2+phdAbceO+deU3ywPw8B42oyJlp4FhO0SoivDFQ==", + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", "license": "MIT", "dependencies": { - "@tanstack/query-devtools": "5.90.1" + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ai": { + "version": "5.0.129", + "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.129.tgz", + "integrity": "sha512-IARdFetNTedDfqpByNMm9p0oHj7JS+SpOrbgLdQdyCiDe70Xk07wnKP4Lub1ckCrxkhAxY3yxOHllGEjbpXgpQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/gateway": "2.0.35", + "@ai-sdk/provider": "2.0.1", + "@ai-sdk/provider-utils": "3.0.20", + "@opentelemetry/api": "1.9.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" + "engines": { + "node": ">=18" }, "peerDependencies": { - "@tanstack/react-query": "^5.90.2", - "react": "^18 || ^19" + "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@tokenizer/inflate": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", - "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "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": { - "debug": "^4.4.3", - "token-types": "^6.1.1" - }, - "engines": { - "node": ">=18" + "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/Borewit" + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "license": "MIT" - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", "dependencies": { - "@types/ms": "*" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "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==", + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "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==", "license": "MIT", "dependencies": { - "@types/estree": "*" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "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/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", "license": "MIT", "dependencies": { - "@types/unist": "*" + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "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==", + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, - "license": "MIT" + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "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" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", "license": "MIT", "dependencies": { - "@types/unist": "*" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, - "node_modules/@types/node": { - "version": "20.19.25", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz", - "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==", + "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": { - "undici-types": "~6.21.0" + "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/@types/react": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.6.tgz", - "integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==", + "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": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "devOptional": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" + "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/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz", - "integrity": "sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/type-utils": "8.47.0", - "@typescript-eslint/utils": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.47.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "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": ">= 4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.47.0.tgz", - "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==", + "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": { - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", - "debug": "^4.3.4" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz", - "integrity": "sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==", + "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": { - "@typescript-eslint/tsconfig-utils": "^8.47.0", - "@typescript-eslint/types": "^8.47.0", - "debug": "^4.3.4" + "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": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "node": ">= 0.4" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz", - "integrity": "sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==", + "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": { - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0" + "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": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz", - "integrity": "sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==", + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "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": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "node": ">= 0.4" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz", - "integrity": "sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "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": { - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0", - "@typescript-eslint/utils": "8.47.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz", - "integrity": "sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==", + "node_modules/axe-core": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", + "integrity": "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==", "dev": true, - "license": "MIT", + "license": "MPL-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz", - "integrity": "sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==", + "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/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.47.0", - "@typescript-eslint/tsconfig-utils": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "url": "https://opencollective.com/express" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "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" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">= 0.8" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "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": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "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": ">=16 || 14 >=14.17" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz", - "integrity": "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==", - "dev": true, + "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==", "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "node": ">= 0.4" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz", - "integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==", - "dev": true, + "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==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.47.0", - "eslint-visitor-keys": "^4.2.1" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=6" + } }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" + "node_modules/caniuse-lite": { + "version": "1.0.30001756", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz", + "integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==", + "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" + } ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "license": "CC-BY-4.0" }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "dev": true, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "dev": true, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "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/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "dev": true, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "dev": true, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "dev": true, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", "license": "MIT", - "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" + "lodash-es": "^4.17.21" }, - "engines": { - "node": ">=14.0.0" + "peerDependencies": { + "chevrotain": "^11.0.0" } }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/chevrotain/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT" + }, + "node_modules/class-validator": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", + "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.20" + } }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "dev": true, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "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", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": ">=6" + } }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.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, + "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==", "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "color-name": "~1.1.4" }, "engines": { - "node": ">=0.4.0" + "node": ">=7.0.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, + "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==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, - "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, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "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" + "delayed-stream": "~1.0.0" }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", "funding": { "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/wooorm" } }, - "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==", + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "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/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT" + }, + "node_modules/console-table-printer": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.15.0.tgz", + "integrity": "sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==", "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, + "simple-wcswidth": "^1.1.2" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "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/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "dev": true, - "license": "Apache-2.0", + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=6.6.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, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" + "object-assign": "^4", + "vary": "^1" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "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, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", "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" + "layout-base": "^1.0.0" } }, - "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, + "node_modules/cross-inspect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", + "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", "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" + "tslib": "^2.4.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=16.0.0" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", - "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", - "dev": true, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-shim-unscopables": "^1.1.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 8" } }, - "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, + "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/cytoscape": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", + "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "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": ">=0.10" } }, - "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, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", "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" + "cose-base": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "cytoscape": "^3.2.0" } }, - "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, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", "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" + "cose-base": "^2.2.0" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "cytoscape": "^3.2.0" } }, - "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, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", "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" + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" }, "engines": { - "node": ">= 0.4" + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=12" } }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true, - "license": "MIT" + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } }, - "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", + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "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", + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "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-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", "dependencies": { - "possible-typed-array-names": "^1.0.0" + "d3-array": "^3.2.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/axe-core": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", - "integrity": "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==", - "dev": true, - "license": "MPL-2.0", + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "dev": true, - "license": "Apache-2.0", + "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": ">= 0.4" + "node": ">=12" } }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "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/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.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", - "license": "Apache-2.0", + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, "bin": { - "baseline-browser-mapping": "dist/cli.js" + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" } }, - "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, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">= 10" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, + "node_modules/d3-dsv/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "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", + "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-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", "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" + "d3-dsv": "1 - 3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "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", + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" }, "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "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", + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "d3-array": "2.5.0 - 3" }, "engines": { - "node": ">= 0.4" + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "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" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=12" } }, - "node_modules/callsites": { + "node_modules/d3-path": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001756", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz", - "integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==", - "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/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" } }, - "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" - }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=12" } }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" } }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" } }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" } }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" } }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "license": "Apache-2.0", + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", "dependencies": { - "clsx": "^2.1.1" + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" }, - "funding": { - "url": "https://polar.sh/cva" + "engines": { + "node": ">=12" } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", + "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": ">=6" + "node": ">=12" } }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" + "d3-path": "^3.1.0" }, "engines": { - "node": ">=12.5.0" + "node": ">=12" } }, - "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==", - "license": "MIT", + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", "dependencies": { - "color-name": "~1.1.4" + "d3-array": "2 - 3" }, "engines": { - "node": ">=7.0.0" + "node": ">=12" } }, - "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==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" } }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "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/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/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/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", + "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": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" }, "engines": { - "node": ">= 8" + "node": ">=12" } }, - "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/dagre-d3-es": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz", + "integrity": "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==", + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -3947,6 +8035,21 @@ "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -3964,6 +8067,15 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/decode-named-character-reference": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", @@ -4020,6 +8132,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -4029,6 +8168,16 @@ "node": ">=6" } }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -4057,6 +8206,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -4070,11 +8228,28 @@ "node": ">=0.10.0" } }, + "node_modules/dompurify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "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", @@ -4085,6 +8260,12 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -4092,6 +8273,24 @@ "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -4106,6 +8305,18 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.24.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", @@ -4179,7 +8390,6 @@ "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" @@ -4189,7 +8399,6 @@ "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" @@ -4227,7 +8436,6 @@ "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" @@ -4240,7 +8448,6 @@ "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", @@ -4283,6 +8490,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4731,17 +8944,137 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", + "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", + "license": "MIT", + "dependencies": { + "ip-address": "10.0.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", + "license": "MIT" + }, "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-glob": { @@ -4774,6 +9107,12 @@ "node": ">= 6" } }, + "node_modules/fast-json-patch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==", + "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", @@ -4788,6 +9127,28 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -4798,6 +9159,19 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -4842,6 +9216,27 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -4896,11 +9291,92 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "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==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4947,11 +9423,22 @@ "node": ">= 0.4" } }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "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", @@ -4985,7 +9472,6 @@ "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", @@ -5073,7 +9559,6 @@ "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" @@ -5096,6 +9581,74 @@ "dev": true, "license": "MIT" }, + "node_modules/graphql": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", + "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-query-complexity": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/graphql-query-complexity/-/graphql-query-complexity-0.12.0.tgz", + "integrity": "sha512-fWEyuSL6g/+nSiIRgIipfI6UXTI7bAxrpPlCY1c0+V3pAEUo1ybaKmSBgNr1ed2r+agm1plJww8Loig9y6s2dw==", + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2" + }, + "peerDependencies": { + "graphql": "^14.6.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/graphql-scalars": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/graphql-scalars/-/graphql-scalars-1.25.0.tgz", + "integrity": "sha512-b0xyXZeRFkne4Eq7NAnL400gStGqG/Sx9VqX0A05nHyEbv57UJnWKsjNnrpVqv5e/8N1MUxkt0wwcRXbiyKcFg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/graphql-yoga": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/graphql-yoga/-/graphql-yoga-5.18.0.tgz", + "integrity": "sha512-xFt1DVXS1BZ3AvjnawAGc5OYieSe56WuQuyk3iEpBwJ3QDZJWQGLmU9z/L5NUZ+pUcyprsz/bOwkYIV96fXt/g==", + "license": "MIT", + "dependencies": { + "@envelop/core": "^5.3.0", + "@envelop/instrumentation": "^1.0.0", + "@graphql-tools/executor": "^1.5.0", + "@graphql-tools/schema": "^10.0.11", + "@graphql-tools/utils": "^10.11.0", + "@graphql-yoga/logger": "^2.0.1", + "@graphql-yoga/subscription": "^5.0.5", + "@whatwg-node/fetch": "^0.10.6", + "@whatwg-node/promise-helpers": "^1.3.2", + "@whatwg-node/server": "^0.10.14", + "lru-cache": "^10.0.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "graphql": "^15.2.0 || ^16.0.0" + } + }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "license": "MIT" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -5106,97 +9659,302 @@ "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "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==", + "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==", + "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==", + "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/hast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hast/-/hast-1.0.0.tgz", + "integrity": "sha512-vFUqlRV5C+xqP76Wwq2SrM0kipnmpxJm7OfvVXpB35Fp+Fn4MV+ozr+JZr5qFvyR1q/U+Foim2x+3P+x9S1PLA==", + "deprecated": "Renamed to rehype", + "license": "MIT" + }, + "node_modules/hast-util-from-dom": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", + "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", + "license": "ISC", + "dependencies": { + "@types/hast": "^3.0.0", + "hastscript": "^9.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-dom/node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-dom/node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", + "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-from-html": "^2.0.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/has-flag": { + "node_modules/hast-util-from-parse5/node_modules/hast-util-parse-selector": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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, + "node_modules/hast-util-from-parse5/node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0" + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", "license": "MIT", "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "@types/hast": "^3.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", "license": "MIT", - "engines": { - "node": ">= 0.4" - }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", "license": "MIT", "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, + "node_modules/hast-util-sanitize": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz", + "integrity": "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==", "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "@types/hast": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "unist-util-position": "^5.0.0" }, - "engines": { - "node": ">= 0.4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-is-element": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", - "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", "license": "MIT", "dependencies": { - "@types/hast": "^3.0.0" + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" }, "funding": { "type": "opencollective", @@ -5230,6 +9988,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-text": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", @@ -5259,6 +10036,77 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript/node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/hastscript/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/hastscript/node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hastscript/node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hastscript/node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, "node_modules/highlight.js": { "version": "11.11.1", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", @@ -5268,6 +10116,21 @@ "node": ">=12.0.0" } }, + "node_modules/highlightjs-vue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", + "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", + "license": "CC0-1.0" + }, + "node_modules/hono": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.9.tgz", + "integrity": "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/html-url-attributes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", @@ -5278,6 +10141,61 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -5335,6 +10253,12 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/inline-style-parser": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", @@ -5356,6 +10280,33 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -5457,6 +10408,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/is-bun-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", @@ -5675,6 +10649,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -5831,7 +10811,6 @@ "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": { @@ -5858,15 +10837,41 @@ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tiktoken": { + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", + "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.5.1" } }, "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==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -5889,6 +10894,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -5896,6 +10907,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "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", @@ -5932,6 +10949,22 @@ "node": ">=4.0" } }, + "node_modules/katex": { + "version": "0.16.28", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.28.tgz", + "integrity": "sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -5942,6 +10975,85 @@ "json-buffer": "3.0.1" } }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/langium": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz", + "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", + "license": "MIT", + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/langsmith": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.5.2.tgz", + "integrity": "sha512-CfkcQsiajtTWknAcyItvJsKEQdY2VgDpm6U8pRI9wnM07mevnOv5EF+RcqWGwx37SEUxtyi2RXMwnKW8b06JtA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/uuid": "^10.0.0", + "chalk": "^4.1.2", + "console-table-printer": "^2.12.1", + "p-queue": "^6.6.2", + "semver": "^7.6.3", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "*", + "@opentelemetry/exporter-trace-otlp-proto": "*", + "@opentelemetry/sdk-trace-base": "*", + "openai": "*" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-proto": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + }, + "openai": { + "optional": true + } + } + }, + "node_modules/langsmith/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -5962,6 +11074,12 @@ "node": ">=0.10" } }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -5976,6 +11094,12 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.12.36", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.36.tgz", + "integrity": "sha512-woWhKMAVx1fzzUnMCyOzglgSgf6/AFHLASdOBcchYCyvWSGWt12imw3iu2hdI5d4dGZRsNWAmWiz37sDKUPaRQ==", + "license": "MIT" + }, "node_modules/lightningcss": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", @@ -6237,6 +11361,37 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lit": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.2.tgz", + "integrity": "sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz", + "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.2.tgz", + "integrity": "sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6253,6 +11408,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6280,7 +11448,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -6304,6 +11471,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/lucide": { + "version": "0.525.0", + "resolved": "https://registry.npmjs.org/lucide/-/lucide-0.525.0.tgz", + "integrity": "sha512-sfehWlaE/7NVkcEQ4T9JD3eID8RNMIGJBBUq9wF3UFiJIrcMKRbU3g1KGfDk4svcW7yw8BtDLXaXo02scDtUYQ==", + "license": "ISC" + }, "node_modules/lucide-react": { "version": "0.542.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.542.0.tgz", @@ -6333,16 +11512,99 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "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/mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-definitions/node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/mdast-util-definitions/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/mdast-util-definitions/node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-definitions/node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-definitions/node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", @@ -6496,6 +11758,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-mdx-expression": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", @@ -6625,6 +11906,27 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -6635,6 +11937,55 @@ "node": ">= 8" } }, + "node_modules/mermaid": { + "version": "11.12.2", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.12.2.tgz", + "integrity": "sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^7.1.1", + "@iconify/utils": "^3.0.1", + "@mermaid-js/parser": "^0.6.3", + "@types/d3": "^7.4.3", + "cytoscape": "^3.29.3", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.13", + "dayjs": "^1.11.18", + "dompurify": "^3.2.5", + "katex": "^0.16.22", + "khroma": "^2.1.0", + "lodash-es": "^4.17.21", + "marked": "^16.2.1", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0" + } + }, + "node_modules/mermaid/node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromark": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", @@ -6704,6 +12055,77 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/micromark-extension-cjk-friendly": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/micromark-extension-cjk-friendly/-/micromark-extension-cjk-friendly-1.2.3.tgz", + "integrity": "sha512-gRzVLUdjXBLX6zNPSnHGDoo+ZTp5zy+MZm0g3sv+3chPXY7l9gW+DnrcHcZh/jiPR6MjPKO4AEJNp4Aw6V9z5Q==", + "license": "MIT", + "dependencies": { + "devlop": "^1.1.0", + "micromark-extension-cjk-friendly-util": "2.1.1", + "micromark-util-chunked": "^2.0.1", + "micromark-util-resolve-all": "^2.0.1", + "micromark-util-symbol": "^2.0.1" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "micromark": "^4.0.0", + "micromark-util-types": "^2.0.0" + }, + "peerDependenciesMeta": { + "micromark-util-types": { + "optional": true + } + } + }, + "node_modules/micromark-extension-cjk-friendly-gfm-strikethrough": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/micromark-extension-cjk-friendly-gfm-strikethrough/-/micromark-extension-cjk-friendly-gfm-strikethrough-1.2.3.tgz", + "integrity": "sha512-gSPnxgHDDqXYOBvQRq6lerrq9mjDhdtKn+7XETuXjxWcL62yZEfUdA28Ml1I2vDIPfAOIKLa0h2XDSGkInGHFQ==", + "license": "MIT", + "dependencies": { + "devlop": "^1.1.0", + "get-east-asian-width": "^1.3.0", + "micromark-extension-cjk-friendly-util": "2.1.1", + "micromark-util-character": "^2.1.1", + "micromark-util-chunked": "^2.0.1", + "micromark-util-resolve-all": "^2.0.1", + "micromark-util-symbol": "^2.0.1" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "micromark": "^4.0.0", + "micromark-util-types": "^2.0.0" + }, + "peerDependenciesMeta": { + "micromark-util-types": { + "optional": true + } + } + }, + "node_modules/micromark-extension-cjk-friendly-util": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-cjk-friendly-util/-/micromark-extension-cjk-friendly-util-2.1.1.tgz", + "integrity": "sha512-egs6+12JU2yutskHY55FyR48ZiEcFOJFyk9rsiyIhcJ6IvWB6ABBqVrBw8IobqJTDZ/wdSr9eoXDPb5S2nW1bg==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "micromark-util-character": "^2.1.1", + "micromark-util-symbol": "^2.0.1" + }, + "engines": { + "node": ">=16" + }, + "peerDependenciesMeta": { + "micromark-util-types": { + "optional": true + } + } + }, "node_modules/micromark-extension-gfm": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", @@ -6825,6 +12247,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/micromark-factory-destination": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", @@ -7208,8 +12649,45 @@ "braces": "^3.0.3", "picomatch": "^2.3.1" }, - "engines": { - "node": ">=8.6" + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/minimatch": { @@ -7229,18 +12707,47 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "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/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -7282,6 +12789,15 @@ "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next": { "version": "16.1.5", "resolved": "https://registry.npmjs.org/next/-/next-16.1.5.tgz", @@ -7779,11 +13295,50 @@ "@img/sharp-win32-x64": "0.34.5" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "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==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7793,7 +13348,6 @@ "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" @@ -7902,6 +13456,53 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/oniguruma-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", + "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz", + "integrity": "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.1", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -7938,6 +13539,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -7970,6 +13580,53 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "license": "MIT" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8008,6 +13665,39 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", + "license": "MIT" + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -8022,7 +13712,6 @@ "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" @@ -8035,6 +13724,22 @@ "dev": true, "license": "MIT" }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -8054,6 +13759,104 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pino": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.3.0.tgz", + "integrity": "sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.2", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pump": "^3.0.0", + "readable-stream": "^4.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "license": "MIT" + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "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", @@ -8103,11 +13906,44 @@ "node": ">= 0.8.0" } }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "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==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -8125,6 +13961,29 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -8144,6 +14003,21 @@ "long": "^4.0.0" } }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8165,6 +14039,36 @@ ], "license": "MIT" }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/react": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", @@ -8206,7 +14110,6 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/react-markdown": { @@ -8315,6 +14218,77 @@ } } }, + "node_modules/react-syntax-highlighter": { + "version": "15.6.6", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.6.tgz", + "integrity": "sha512-DgXrc+AZF47+HvAPEmn7Ua/1p10jNoVZVI/LoPiYdtY+OM+/nG5yefLHKJwdKqY1adMuHFbeyBaG9j64ML7vTw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "highlightjs-vue": "^1.0.0", + "lowlight": "^1.17.0", + "prismjs": "^1.30.0", + "refractor": "^3.6.0" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, + "node_modules/react-syntax-highlighter/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/react-syntax-highlighter/node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "license": "MIT", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -8338,6 +14312,146 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "license": "MIT", + "dependencies": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "license": "MIT", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/prismjs": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -8359,6 +14473,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype-harden": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/rehype-harden/-/rehype-harden-1.1.7.tgz", + "integrity": "sha512-j5DY0YSK2YavvNGV+qBHma15J9m0WZmRe8posT5AtKDS6TNWtMVTo6RiqF8SidfcASYz8f3k2J/1RWmq5zTXUw==", + "license": "MIT", + "dependencies": { + "unist-util-visit": "^5.0.0" + } + }, "node_modules/rehype-highlight": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/rehype-highlight/-/rehype-highlight-7.0.2.tgz", @@ -8376,6 +14499,96 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/rehype-katex": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", + "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/katex": "^0.16.0", + "hast-util-from-html-isomorphic": "^2.0.0", + "hast-util-to-text": "^4.0.0", + "katex": "^0.16.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-sanitize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz", + "integrity": "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-sanitize": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-cjk-friendly": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/remark-cjk-friendly/-/remark-cjk-friendly-1.2.3.tgz", + "integrity": "sha512-UvAgxwlNk+l9Oqgl/9MWK2eWRS7zgBW/nXX9AthV7nd/3lNejF138E7Xbmk9Zs4WjTJGs721r7fAEc7tNFoH7g==", + "license": "MIT", + "dependencies": { + "micromark-extension-cjk-friendly": "1.2.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@types/mdast": "^4.0.0", + "unified": "^11.0.0" + }, + "peerDependenciesMeta": { + "@types/mdast": { + "optional": true + } + } + }, + "node_modules/remark-cjk-friendly-gfm-strikethrough": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/remark-cjk-friendly-gfm-strikethrough/-/remark-cjk-friendly-gfm-strikethrough-1.2.3.tgz", + "integrity": "sha512-bXfMZtsaomK6ysNN/UGRIcasQAYkC10NtPmP0oOHOV8YOhA2TXmwRXCku4qOzjIFxAPfish5+XS0eIug2PzNZA==", + "license": "MIT", + "dependencies": { + "micromark-extension-cjk-friendly-gfm-strikethrough": "1.2.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@types/mdast": "^4.0.0", + "unified": "^11.0.0" + }, + "peerDependenciesMeta": { + "@types/mdast": { + "optional": true + } + } + }, "node_modules/remark-gfm": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", @@ -8394,6 +14607,22 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/remark-math": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", + "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", @@ -8442,6 +14671,21 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/remend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/remend/-/remend-1.0.1.tgz", + "integrity": "sha512-152puVH0qMoRJQFnaMG+rVDdf01Jq/CaED+MBuXExurJgdbkLp0c3TIe4R12o28Klx8uyGsjvFNG05aFG69G9w==", + "license": "Apache-2.0" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -8483,6 +14727,15 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -8494,6 +14747,40 @@ "node": ">=0.10.0" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -8518,6 +14805,33 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -8538,6 +14852,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -8573,12 +14907,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", "license": "MIT" }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, "node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", @@ -8588,7 +14943,52 @@ "semver": "bin/semver.js" }, "engines": { - "node": ">=10" + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/set-function-length": { @@ -8640,6 +15040,12 @@ "node": ">= 0.4" } }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/sharp": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", @@ -8683,7 +15089,6 @@ "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" @@ -8696,17 +15101,31 @@ "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/shiki": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.22.0.tgz", + "integrity": "sha512-LBnhsoYEe0Eou4e1VgJACes+O6S6QC0w71fCSp5Oya79inkwkm15gQ1UF6VtQ8j/taMDh79hAB49WUk8ALQW3g==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.22.0", + "@shikijs/engine-javascript": "3.22.0", + "@shikijs/engine-oniguruma": "3.22.0", + "@shikijs/langs": "3.22.0", + "@shikijs/themes": "3.22.0", + "@shikijs/types": "3.22.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, "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", @@ -8726,7 +15145,6 @@ "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", @@ -8743,7 +15161,6 @@ "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", @@ -8762,7 +15179,6 @@ "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", @@ -8787,6 +15203,21 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/simple-wcswidth": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz", + "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==", + "license": "MIT" + }, + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.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", @@ -8806,6 +15237,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", @@ -8813,6 +15253,15 @@ "dev": true, "license": "MIT" }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -8827,6 +15276,61 @@ "node": ">= 0.4" } }, + "node_modules/streamdown": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/streamdown/-/streamdown-1.6.11.tgz", + "integrity": "sha512-Y38fwRx5kCKTluwM+Gf27jbbi9q6Qy+WC9YrC1YbCpMkktT3PsRBJHMWiqYeF8y/JzLpB1IzDoeaB6qkQEDnAA==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1", + "hast": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.3.6", + "html-url-attributes": "^3.0.1", + "katex": "^0.16.22", + "lucide-react": "^0.542.0", + "marked": "^16.2.1", + "mermaid": "^11.11.0", + "rehype-harden": "^1.1.6", + "rehype-katex": "^7.0.1", + "rehype-raw": "^7.0.0", + "rehype-sanitize": "^6.0.0", + "remark-cjk-friendly": "^1.2.3", + "remark-cjk-friendly-gfm-strikethrough": "^1.2.3", + "remark-gfm": "^4.0.1", + "remark-math": "^6.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remend": "1.0.1", + "shiki": "^3.12.2", + "tailwind-merge": "^3.3.1", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/streamdown/node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string.prototype.includes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", @@ -8968,7 +15472,6 @@ "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" @@ -9034,11 +15537,16 @@ } } }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "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" @@ -9060,6 +15568,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "license": "MIT" + }, "node_modules/tailwind-merge": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", @@ -9091,6 +15605,24 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -9152,6 +15684,15 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/token-types": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", @@ -9170,6 +15711,12 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -9203,6 +15750,24 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-deepmerge": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-7.0.3.tgz", + "integrity": "sha512-Du/ZW2RfwV/D4cmA5rXafYjBQVuvu4qGiEEla4EmEHVHgRdx68Gftx7i66jn2bzHPwSVZY36Ae6OuDn9el4ZKA==", + "license": "ISC", + "engines": { + "node": ">=14.13.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -9226,7 +15791,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Wombosvideo" @@ -9245,6 +15809,57 @@ "node": ">= 0.8.0" } }, + "node_modules/type-graphql": { + "version": "2.0.0-rc.1", + "resolved": "https://registry.npmjs.org/type-graphql/-/type-graphql-2.0.0-rc.1.tgz", + "integrity": "sha512-HCu4j3jR0tZvAAoO7DMBT3MRmah0DFRe5APymm9lXUghXA0sbhiMf6SLRafRYfk0R0KiUQYRduuGP3ap1RnF1Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/TypeGraphQL" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/typegraphql" + } + ], + "license": "MIT", + "dependencies": { + "@graphql-yoga/subscription": "^5.0.0", + "@types/node": "*", + "@types/semver": "^7.5.6", + "graphql-query-complexity": "^0.12.0", + "semver": "^7.5.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "peerDependencies": { + "class-validator": ">=0.14.0", + "graphql": "^16.8.1", + "graphql-scalars": "^1.22.4" + }, + "peerDependenciesMeta": { + "class-validator": { + "optional": true + } + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -9337,6 +15952,12 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "license": "MIT" + }, "node_modules/uint8array-extras": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", @@ -9372,7 +15993,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/unified": { @@ -9408,6 +16028,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-is": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", @@ -9434,6 +16064,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", @@ -9476,6 +16120,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unrs-resolver": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", @@ -9511,6 +16164,12 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, + "node_modules/untruncate-json": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/untruncate-json/-/untruncate-json-0.0.1.tgz", + "integrity": "sha512-4W9enDK4X1y1s2S/Rz7ysw6kDuMS3VmRjMFg7GZrNO+98OSe+x5Lh7PKYoVjy3lW/1wmhs6HW0lusnQRHgMarA==", + "license": "MIT" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -9521,6 +16180,26 @@ "punycode": "^2.1.0" } }, + "node_modules/urlpattern-polyfill": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz", + "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==", + "license": "MIT" + }, + "node_modules/urql": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/urql/-/urql-4.2.2.tgz", + "integrity": "sha512-3GgqNa6iF7bC4hY/ImJKN4REQILcSU9VKcKL8gfELZM8mM5BnLH1BsCc8kBdnVGD1LIFOs4W3O2idNHhON1r0w==", + "license": "MIT", + "dependencies": { + "@urql/core": "^5.1.1", + "wonka": "^6.3.2" + }, + "peerDependencies": { + "@urql/core": "^5.0.0", + "react": ">= 16.8.0" + } + }, "node_modules/use-callback-ref": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", @@ -9564,6 +16243,15 @@ } } }, + "node_modules/use-stick-to-bottom": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-stick-to-bottom/-/use-stick-to-bottom-1.1.3.tgz", + "integrity": "sha512-GgRLdeGhxBxpcbrBbEIEoOKUQ9d46/eaSII+wyv1r9Du+NbCn1W/OE+VddefvRP4+5w/1kATN/6g2/BAC/yowQ==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.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", @@ -9573,6 +16261,64 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/validator": { + "version": "13.15.26", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", + "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -9587,6 +16333,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vfile-message": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", @@ -9601,11 +16361,94 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "license": "MIT" + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "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" @@ -9706,6 +16549,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wonka": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz", + "integrity": "sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==", + "license": "MIT" + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -9716,6 +16565,21 @@ "node": ">=0.10.0" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -9738,6 +16602,15 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/components/frontend/package.json b/components/frontend/package.json index acb9a2ca1..f04d6dcc2 100644 --- a/components/frontend/package.json +++ b/components/frontend/package.json @@ -9,6 +9,11 @@ "lint": "eslint" }, "dependencies": { + "@ag-ui/client": "^0.0.44", + "@copilotkit/react-core": "^1.51.2", + "@copilotkit/react-ui": "^1.51.2", + "@copilotkit/runtime": "^1.51.2", + "@copilotkit/runtime-client-gql": "^1.51.2", "@hookform/resolvers": "^5.2.1", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-avatar": "^1.1.10", diff --git a/components/frontend/src/app/api/copilotkit/[project]/[session]/route.ts b/components/frontend/src/app/api/copilotkit/[project]/[session]/route.ts new file mode 100644 index 000000000..5b1f879bb --- /dev/null +++ b/components/frontend/src/app/api/copilotkit/[project]/[session]/route.ts @@ -0,0 +1,42 @@ +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { HttpAgent } from "@ag-ui/client"; +import { NextRequest } from "next/server"; + +/** + * CopilotKit API route for session chat. + * + * Creates an HttpAgent that points at the backend's AG-UI runner proxy. + * The backend handles auth, RBAC, and proxying to the actual runner pod. + */ +export async function POST( + request: NextRequest, + { params }: { params: Promise<{ project: string; session: string }> } +) { + const { project, session } = await params; + + // The backend proxy URL — the backend handles auth forwarding + const backendUrl = + process.env.NEXT_PUBLIC_API_URL || process.env.BACKEND_URL || ""; + const runnerProxyUrl = `${backendUrl}/api/projects/${project}/agentic-sessions/${session}/agui/run`; + + const agent = new HttpAgent({ + url: runnerProxyUrl, + }); + + const runtime = new CopilotRuntime({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + agents: { session: agent as any }, + }); + + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + runtime, + serviceAdapter: new ExperimentalEmptyAdapter(), + endpoint: `/api/copilotkit/${project}/${session}`, + }); + + return handleRequest(request); +} diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx index dd442824b..a64aadc8c 100644 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx @@ -29,6 +29,7 @@ import { cn } from "@/lib/utils"; // Custom components import MessagesTab from "@/components/session/MessagesTab"; +import { CopilotChatPanel } from "@/components/session/CopilotChatPanel"; import { FileTree, type FileTreeNode } from "@/components/file-tree"; import { Button } from "@/components/ui/button"; @@ -2471,47 +2472,12 @@ export default function ProjectSessionDetailPage({ )}
- - Promise.resolve(sendChat())} - onInterrupt={aguiInterrupt} - onGoToResults={() => {}} - onContinue={handleContinue} - workflowMetadata={workflowMetadata} - onCommandClick={handleCommandClick} - isRunActive={isRunActive} - showWelcomeExperience={!["Completed", "Failed", "Stopped", "Stopping"].includes(session?.status?.phase || "")} - activeWorkflow={workflowManagement.activeWorkflow} - userHasInteracted={userHasInteracted} - queuedMessages={sessionQueue.messages} - hasRealMessages={hasRealMessages} - welcomeExperienceComponent={ - setUserHasInteracted(true)} - userHasInteracted={userHasInteracted} - sessionPhase={session?.status?.phase} - hasRealMessages={hasRealMessages} - onLoadWorkflow={() => setCustomWorkflowDialogOpen(true)} - selectedWorkflow={workflowManagement.selectedWorkflow} - /> - } + className="flex-1" /> -
@@ -2537,47 +2503,12 @@ export default function ProjectSessionDetailPage({ )}
- - Promise.resolve(sendChat())} - onInterrupt={aguiInterrupt} - onGoToResults={() => {}} - onContinue={handleContinue} - workflowMetadata={workflowMetadata} - onCommandClick={handleCommandClick} - isRunActive={isRunActive} - showWelcomeExperience={!["Completed", "Failed", "Stopped", "Stopping"].includes(session?.status?.phase || "")} - activeWorkflow={workflowManagement.activeWorkflow} - userHasInteracted={userHasInteracted} - queuedMessages={sessionQueue.messages} - hasRealMessages={hasRealMessages} - welcomeExperienceComponent={ - setUserHasInteracted(true)} - userHasInteracted={userHasInteracted} - sessionPhase={session?.status?.phase} - hasRealMessages={hasRealMessages} - onLoadWorkflow={() => setCustomWorkflowDialogOpen(true)} - selectedWorkflow={workflowManagement.selectedWorkflow} - /> - } - /> - + className="flex-1" + />
diff --git a/components/frontend/src/components/session/CopilotChatPanel.tsx b/components/frontend/src/components/session/CopilotChatPanel.tsx new file mode 100644 index 000000000..8780bd072 --- /dev/null +++ b/components/frontend/src/components/session/CopilotChatPanel.tsx @@ -0,0 +1,51 @@ +"use client"; + +import React from "react"; +import "@copilotkit/react-ui/styles.css"; +import { CopilotKit } from "@copilotkit/react-core"; +import { CopilotChat } from "@copilotkit/react-ui"; + +type CopilotChatPanelProps = { + projectName: string; + sessionName: string; + className?: string; +}; + +/** + * CopilotKit-powered chat panel for session interaction. + * + * Replaces the custom useAGUIStream + MessagesTab combo with + * CopilotKit's built-in chat component. The CopilotRuntime on the + * server (Next.js API route) proxies to the backend's AG-UI runner. + */ +export function CopilotChatPanel({ + projectName, + sessionName, + className = "", +}: CopilotChatPanelProps) { + const runtimeUrl = `/api/copilotkit/${projectName}/${sessionName}`; + + return ( + + + + ); +} + +function ChatContent({ className }: { className: string }) { + return ( +
+ +
+ ); +} diff --git a/components/frontend/src/hooks/use-agui-stream.ts b/components/frontend/src/hooks/use-agui-stream.ts index 0d6835b54..11d52eca6 100644 --- a/components/frontend/src/hooks/use-agui-stream.ts +++ b/components/frontend/src/hooks/use-agui-stream.ts @@ -515,6 +515,20 @@ export function useAGUIStream(options: UseAGUIStreamOptions): UseAGUIStreamRetur return newState } + // Handle CUSTOM events (platform extensions via AG-UI CustomEvent) + if (event.type === AGUIEventType.CUSTOM) { + type CustomEventData = { name?: string; value?: Record } + const customEvent = event as unknown as CustomEventData + + // Langfuse trace ID from tracing middleware + if (customEvent.name === 'ambient:langfuse_trace' && customEvent.value?.traceId) { + onTraceId?.(customEvent.value.traceId as string) + return newState + } + + return newState + } + // Handle RAW events (may contain message data or thinking blocks) if (event.type === AGUIEventType.RAW) { // RAW events use "event" field (AG-UI standard), or "data" field (legacy) @@ -531,7 +545,7 @@ export function useAGUIStream(options: UseAGUIStreamOptions): UseAGUIStreamRetur return newState } - // Handle Langfuse trace_id for feedback association + // Handle Langfuse trace_id for feedback association (legacy RawEvent fallback) if (rawData?.type === 'langfuse_trace' && rawData?.traceId) { const traceId = rawData.traceId as string onTraceId?.(traceId) diff --git a/components/frontend/src/services/api/sessions.ts b/components/frontend/src/services/api/sessions.ts index bd55d9d28..c6ad2633c 100644 --- a/components/frontend/src/services/api/sessions.ts +++ b/components/frontend/src/services/api/sessions.ts @@ -257,3 +257,26 @@ export async function getReposStatus( `/projects/${projectName}/agentic-sessions/${sessionName}/repos/status` ); } + +// --- Capabilities --- + +export type CapabilitiesResponse = { + framework: string; + agent_features: string[]; + platform_features: string[]; + file_system: boolean; + mcp: boolean; + tracing: string | null; + session_persistence: boolean; + model: string | null; + session_id: string | null; +}; + +export async function getCapabilities( + projectName: string, + sessionName: string +): Promise { + return apiClient.get( + `/projects/${projectName}/agentic-sessions/${sessionName}/agui/capabilities` + ); +} diff --git a/components/frontend/src/services/queries/index.ts b/components/frontend/src/services/queries/index.ts index 84c55b558..8a508d304 100644 --- a/components/frontend/src/services/queries/index.ts +++ b/components/frontend/src/services/queries/index.ts @@ -12,3 +12,4 @@ export * from './use-secrets'; export * from './use-repo'; export * from './use-workspace'; export * from './use-auth'; +export * from './use-capabilities'; \ No newline at end of file diff --git a/components/frontend/src/services/queries/use-capabilities.ts b/components/frontend/src/services/queries/use-capabilities.ts new file mode 100644 index 000000000..e4d792364 --- /dev/null +++ b/components/frontend/src/services/queries/use-capabilities.ts @@ -0,0 +1,40 @@ +import { useQuery } from "@tanstack/react-query"; +import * as sessionsApi from "@/services/api/sessions"; + +export const capabilitiesKeys = { + all: ["capabilities"] as const, + session: (projectName: string, sessionName: string) => + [...capabilitiesKeys.all, projectName, sessionName] as const, +}; + +/** + * Fetch the runner's capabilities manifest for a session. + * + * Returns which AG-UI features the framework supports, which platform + * features are available, and runtime config (model, tracing, etc.). + * The frontend uses this to conditionally render UI panels. + */ +export function useCapabilities( + projectName: string, + sessionName: string, + enabled: boolean = true +) { + return useQuery({ + queryKey: capabilitiesKeys.session(projectName, sessionName), + queryFn: () => sessionsApi.getCapabilities(projectName, sessionName), + enabled: enabled && !!projectName && !!sessionName, + staleTime: 60 * 1000, // 1 minute — capabilities rarely change mid-session + retry: 2, + // Poll until runner is ready (returns real data) + refetchInterval: (query) => { + if (query.state.data?.framework && query.state.data.framework !== "unknown") { + return false; + } + // Stop after ~1 min (6 × 10s) + const updatedCount = + (query.state as { dataUpdatedCount?: number }).dataUpdatedCount ?? 0; + if (updatedCount >= 6) return false; + return 10 * 1000; + }, + }); +} diff --git a/components/frontend/src/types/agui.ts b/components/frontend/src/types/agui.ts index fa1dd1609..e2b2d356a 100644 --- a/components/frontend/src/types/agui.ts +++ b/components/frontend/src/types/agui.ts @@ -41,6 +41,9 @@ export const AGUIEventType = { // Raw event RAW: 'RAW', + // Custom events (platform extensions) + CUSTOM: 'CUSTOM', + // Meta events (user feedback, annotations, etc.) META: 'META', } as const @@ -244,6 +247,13 @@ export type AGUIRawEvent = AGUIBaseEvent & { data: unknown } +// Custom event (platform extensions) +export type AGUICustomEvent = AGUIBaseEvent & { + type: typeof AGUIEventType.CUSTOM + name: string + value: unknown +} + // Meta event (user feedback, annotations, etc.) export type AGUIMetaEvent = { type: typeof AGUIEventType.META @@ -272,6 +282,7 @@ export type AGUIEvent = | AGUIActivitySnapshotEvent | AGUIActivityDeltaEvent | AGUIRawEvent + | AGUICustomEvent | AGUIMetaEvent // Run metadata type diff --git a/components/runners/claude-code-runner/adapter.py b/components/runners/claude-code-runner/adapter.py index dbd7414ea..c05d77a31 100644 --- a/components/runners/claude-code-runner/adapter.py +++ b/components/runners/claude-code-runner/adapter.py @@ -1,1026 +1,137 @@ #!/usr/bin/env python3 """ -Claude Code Adapter for AG-UI Server. +Claude Code platform adapter — configures and creates the upstream +``ag_ui_claude_sdk.ClaudeAgentAdapter``. -Core adapter that wraps the Claude Code SDK and produces a stream of -AG-UI protocol events. Business logic is delegated to focused modules: +Orchestrates platform modules (auth, workspace, mcp, prompts) to build +SDK options, then returns a ready-to-use adapter. -- ``auth`` — credential fetching and authentication setup -- ``config`` — ambient.json, MCP, and repos configuration -- ``workspace`` — path setup, validation, prerequisites -- ``prompts`` — system prompt construction and constants -- ``tools`` — MCP tool definitions (session, rubric) -- ``utils`` — general utilities (redaction, URL parsing, subprocesses) +Usage in ``main.py`` follows the standard AG-UI pattern:: + + adapter = build_adapter(context, ...) + async for event in adapter.run(input_data): + yield encoder.encode(event) """ -import json as _json import logging import os -import uuid -from pathlib import Path -from typing import Any, AsyncIterator, Optional +from typing import Any -# Set umask to make files readable by content service container os.umask(0o022) -# AG-UI Protocol Events -from ag_ui.core import ( - BaseEvent, - EventType, - RawEvent, - RunAgentInput, - RunErrorEvent, - RunFinishedEvent, - RunStartedEvent, - StateDeltaEvent, - StepFinishedEvent, - StepStartedEvent, - TextMessageContentEvent, - TextMessageEndEvent, - TextMessageStartEvent, - ToolCallArgsEvent, - ToolCallEndEvent, - ToolCallStartEvent, -) +from ag_ui_claude_sdk import ClaudeAgentAdapter import auth -import config as runner_config +import mcp as mcp_mod import prompts import workspace from context import RunnerContext -from tools import create_restart_session_tool, create_rubric_mcp_tool, load_rubric_content -from utils import redact_secrets, run_cmd, url_with_token, parse_owner_repo -from workspace import PrerequisiteError logger = logging.getLogger(__name__) -class ClaudeCodeAdapter: - """ - Adapter that wraps the Claude Code SDK for AG-UI server. +async def setup_platform(context: RunnerContext) -> tuple[str, dict]: + """Run all platform setup before creating the adapter. - Produces AG-UI events via async generator instead of WebSocket. + Returns: + (configured_model, platform_info) """ - - def __init__(self): - self.context: Optional[RunnerContext] = None - self.last_exit_code = 1 - self._restart_requested = False - self._first_run = True - self._skip_resume_on_restart = False - self._turn_count = 0 - - # AG-UI streaming state (per-run, not instance state) - self._current_run_id: Optional[str] = None - self._current_thread_id: Optional[str] = None - - # Active client reference for interrupt support - self._active_client: Optional[Any] = None - - async def initialize(self, context: RunnerContext): - """Initialize the adapter with context.""" - self.context = context - logger.info( - f"Initialized Claude Code adapter for session {context.session_id}" - ) - - # Credentials are fetched on-demand from backend API - logger.info("Credentials will be fetched on-demand from backend API") - - # Workspace is already prepared by init container (hydrate.sh) - logger.info("Workspace prepared by init container, validating...") - - # Validate prerequisite files for phase-based commands - try: - await workspace.validate_prerequisites(self.context) - except PrerequisiteError as exc: - self.last_exit_code = 2 - logger.error( - "Prerequisite validation failed during initialization: %s", exc - ) - raise - - async def process_run( - self, input_data: RunAgentInput - ) -> AsyncIterator[BaseEvent]: - """Process a run and yield AG-UI events. - - This is the main entry point called by the FastAPI server. - """ - thread_id = input_data.thread_id or self.context.session_id - run_id = input_data.run_id or str(uuid.uuid4()) - - self._current_thread_id = thread_id - self._current_run_id = run_id - + _api_key, _use_vertex, configured_model = await auth.setup_sdk_authentication(context) + await auth.populate_runtime_credentials(context) + cwd_path, add_dirs = workspace.resolve_sdk_paths(context) + + return configured_model, { + "cwd_path": cwd_path, + "add_dirs": add_dirs, + } + + +def build_adapter( + context: RunnerContext, + *, + configured_model: str, + cwd_path: str, + add_dirs: list[str], + first_run: bool = True, + obs=None, +) -> ClaudeAgentAdapter: + """Build and return a configured ``ClaudeAgentAdapter``.""" + mcp_servers = mcp_mod.build_mcp_servers(context, cwd_path, obs) + mcp_mod.log_auth_status(mcp_servers) + + system_prompt_config = prompts.build_sdk_system_prompt(context.workspace_path, cwd_path) + allowed_tools = mcp_mod.build_allowed_tools(mcp_servers) + + def sdk_stderr_handler(line: str): + logger.warning(f"[SDK stderr] {line.rstrip()}") + + options = _build_options( + context=context, + cwd_path=cwd_path, + add_dirs=add_dirs, + configured_model=configured_model, + allowed_tools=allowed_tools, + mcp_servers=mcp_servers, + system_prompt_config=system_prompt_config, + sdk_stderr_handler=sdk_stderr_handler, + first_run=first_run, + ) + + return ClaudeAgentAdapter( + name="claude_code_runner", + description="Ambient Code Platform Claude session", + options=options, + ) + + +def _build_options( + *, + context: RunnerContext, + cwd_path: str, + add_dirs: list[str], + configured_model: str, + allowed_tools: list[str], + mcp_servers: dict, + system_prompt_config: dict, + sdk_stderr_handler, + first_run: bool, +) -> dict[str, Any]: + """Build the options dict for ``ClaudeAgentAdapter``.""" + is_continuation = context.get_env("IS_RESUME", "").strip().lower() == "true" + + options: dict[str, Any] = { + "cwd": cwd_path, + "permission_mode": "acceptEdits", + "allowed_tools": allowed_tools, + "mcp_servers": mcp_servers, + "setting_sources": ["project"], + "system_prompt": system_prompt_config, + "include_partial_messages": True, + "stderr": sdk_stderr_handler, + } + + if add_dirs: + options["add_dirs"] = add_dirs + + if configured_model: + options["model"] = configured_model + + max_tokens_env = context.get_env("LLM_MAX_TOKENS") or context.get_env("MAX_TOKENS") + if max_tokens_env: try: - # Emit RUN_STARTED - yield RunStartedEvent( - type=EventType.RUN_STARTED, - thread_id=thread_id, - run_id=run_id, - ) - - # Echo user messages as events (for history/display) - for msg in input_data.messages or []: - msg_dict = ( - msg - if isinstance(msg, dict) - else ( - msg.model_dump() if hasattr(msg, "model_dump") else {} - ) - ) - role = msg_dict.get("role", "") - - if role == "user": - msg_id = msg_dict.get("id", str(uuid.uuid4())) - content = msg_dict.get("content", "") - msg_metadata = msg_dict.get("metadata", {}) - - is_hidden = isinstance( - msg_metadata, dict - ) and msg_metadata.get("hidden", False) - if is_hidden: - logger.info( - f"Message {msg_id[:8]} marked as hidden " - "(auto-sent initial/workflow prompt)" - ) - yield RawEvent( - type=EventType.RAW, - thread_id=thread_id, - run_id=run_id, - event={ - "type": "message_metadata", - "messageId": msg_id, - "metadata": msg_metadata, - "hidden": True, - }, - ) - - yield TextMessageStartEvent( - type=EventType.TEXT_MESSAGE_START, - thread_id=thread_id, - run_id=run_id, - message_id=msg_id, - role="user", - ) - - if content: - yield TextMessageContentEvent( - type=EventType.TEXT_MESSAGE_CONTENT, - thread_id=thread_id, - run_id=run_id, - message_id=msg_id, - delta=content, - ) - - yield TextMessageEndEvent( - type=EventType.TEXT_MESSAGE_END, - thread_id=thread_id, - run_id=run_id, - message_id=msg_id, - ) - - # Extract user message from input - logger.info( - f"Extracting user message from " - f"{len(input_data.messages)} messages" - ) - user_message = self._extract_user_message(input_data) - logger.info( - f"Extracted user message: " - f"'{user_message[:100] if user_message else '(empty)'}...'" - ) - - if not user_message: - logger.warning("No user message found in input") - yield RawEvent( - type=EventType.RAW, - thread_id=thread_id, - run_id=run_id, - event={ - "type": "system_log", - "message": "No user message provided", - }, - ) - yield RunFinishedEvent( - type=EventType.RUN_FINISHED, - thread_id=thread_id, - run_id=run_id, - ) - return - - # Run Claude SDK and yield events - logger.info( - f"Starting Claude SDK with prompt: '{user_message[:50]}...'" - ) - async for event in self._run_claude_agent_sdk( - user_message, thread_id, run_id - ): - yield event - logger.info(f"Claude SDK processing completed for run {run_id}") - - # Emit RUN_FINISHED - yield RunFinishedEvent( - type=EventType.RUN_FINISHED, - thread_id=thread_id, - run_id=run_id, - ) - - self.last_exit_code = 0 - - except PrerequisiteError as e: - self.last_exit_code = 2 - logger.error(f"Prerequisite validation failed: {e}") - yield RunErrorEvent( - type=EventType.RUN_ERROR, - thread_id=thread_id, - run_id=run_id, - message=str(e), - ) - except Exception as e: - self.last_exit_code = 1 - logger.error(f"Error in process_run: {e}") - yield RunErrorEvent( - type=EventType.RUN_ERROR, - thread_id=thread_id, - run_id=run_id, - message=str(e), - ) - - def _extract_user_message(self, input_data: RunAgentInput) -> str: - """Extract user message text from RunAgentInput.""" - messages = input_data.messages or [] - logger.info( - f"Extracting from {len(messages)} messages, " - f"types: {[type(m).__name__ for m in messages]}" - ) + options["max_tokens"] = int(max_tokens_env) + except (ValueError, TypeError): + pass - for msg in reversed(messages): - if hasattr(msg, "role") and msg.role == "user": - content = getattr(msg, "content", "") - if isinstance(content, str): - return content - elif isinstance(content, list): - for block in content: - if hasattr(block, "text"): - return block.text - elif isinstance(block, dict) and "text" in block: - return block["text"] - elif isinstance(msg, dict): - if msg.get("role") == "user": - content = msg.get("content", "") - if isinstance(content, str): - return content - - logger.warning("No user message found!") - return "" - - # ------------------------------------------------------------------ - # SDK orchestration - # ------------------------------------------------------------------ - - async def _run_claude_agent_sdk( - self, prompt: str, thread_id: str, run_id: str - ) -> AsyncIterator[BaseEvent]: - """Execute the Claude Code SDK with the given prompt and yield AG-UI events.""" - current_message_id: Optional[str] = None - - logger.info( - f"_run_claude_agent_sdk called with prompt length={len(prompt)}, " - "will create fresh client" - ) + temperature_env = context.get_env("LLM_TEMPERATURE") or context.get_env("TEMPERATURE") + if temperature_env: try: - # --- Authentication --- - logger.info("Checking authentication configuration...") - api_key = self.context.get_env("ANTHROPIC_API_KEY", "") - use_vertex = ( - self.context.get_env("CLAUDE_CODE_USE_VERTEX", "").strip() - == "1" - ) - - logger.info( - f"Auth config: api_key={'set' if api_key else 'not set'}, " - f"use_vertex={use_vertex}" - ) - - if not api_key and not use_vertex: - raise RuntimeError( - "Either ANTHROPIC_API_KEY or CLAUDE_CODE_USE_VERTEX=1 " - "must be set" - ) - - if api_key: - os.environ["ANTHROPIC_API_KEY"] = api_key - logger.info("Using Anthropic API key authentication") - - if use_vertex: - vertex_credentials = await auth.setup_vertex_credentials( - self.context - ) - if "ANTHROPIC_API_KEY" in os.environ: - logger.info( - "Clearing ANTHROPIC_API_KEY to force Vertex AI mode" - ) - del os.environ["ANTHROPIC_API_KEY"] - - os.environ["CLAUDE_CODE_USE_VERTEX"] = "1" - os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ( - vertex_credentials.get("credentials_path", "") - ) - os.environ["ANTHROPIC_VERTEX_PROJECT_ID"] = ( - vertex_credentials.get("project_id", "") - ) - os.environ["CLOUD_ML_REGION"] = vertex_credentials.get( - "region", "" - ) - - # --- SDK imports (after env vars are set) --- - from claude_agent_sdk import ( - AssistantMessage, - ClaudeAgentOptions, - ClaudeSDKClient, - ResultMessage, - SystemMessage, - TextBlock, - ThinkingBlock, - ToolResultBlock, - ToolUseBlock, - UserMessage, - create_sdk_mcp_server, - ) - from claude_agent_sdk import tool as sdk_tool - from claude_agent_sdk.types import StreamEvent - - from observability import ObservabilityManager - - # --- Observability --- - raw_user_id = os.getenv("USER_ID", "").strip() - raw_user_name = os.getenv("USER_NAME", "").strip() - user_id, user_name = auth.sanitize_user_context( - raw_user_id, raw_user_name - ) - - model = self.context.get_env("LLM_MODEL") - configured_model = model or "claude-sonnet-4-5@20250929" - - if use_vertex and model: - configured_model = auth.map_to_vertex_model(model) - - obs = ObservabilityManager( - session_id=self.context.session_id, - user_id=user_id, - user_name=user_name, - ) - await obs.initialize( - prompt=prompt, - namespace=self.context.get_env( - "AGENTIC_SESSION_NAMESPACE", "unknown" - ), - model=configured_model, - ) - obs._pending_initial_prompt = prompt - - # --- Workspace paths --- - is_continuation = ( - self.context.get_env("IS_RESUME", "").strip().lower() - == "true" - ) - if is_continuation: - logger.info("IS_RESUME=true - treating as continuation") - - repos_cfg = runner_config.get_repos_config() - cwd_path = self.context.workspace_path - add_dirs = [] - derived_name = None - - active_workflow_url = ( - os.getenv("ACTIVE_WORKFLOW_GIT_URL") or "" - ).strip() - if active_workflow_url: - cwd_path, add_dirs, derived_name = ( - workspace.setup_workflow_paths( - self.context, active_workflow_url, repos_cfg - ) - ) - elif repos_cfg: - cwd_path, add_dirs = workspace.setup_multi_repo_paths( - self.context, repos_cfg - ) - else: - cwd_path = str( - Path(self.context.workspace_path) / "artifacts" - ) - - # --- Config --- - ambient_config = ( - runner_config.load_ambient_config(cwd_path) - if active_workflow_url - else {} - ) - - cwd_path_obj = Path(cwd_path) - if not cwd_path_obj.exists(): - logger.warning( - f"Working directory does not exist, creating: {cwd_path}" - ) - try: - cwd_path_obj.mkdir(parents=True, exist_ok=True) - except Exception as e: - logger.error(f"Failed to create working directory: {e}") - cwd_path = self.context.workspace_path - - logger.info(f"Claude SDK CWD: {cwd_path}") - logger.info(f"Claude SDK additional directories: {add_dirs}") - - # --- Credentials --- - await auth.populate_runtime_credentials(self.context) - - # --- MCP servers --- - mcp_servers = ( - runner_config.load_mcp_config(self.context, cwd_path) or {} - ) - - # Pre-flight check: Validate MCP server authentication - from main import _check_mcp_authentication - - mcp_auth_warnings = [] - if mcp_servers: - for server_name in mcp_servers.keys(): - is_auth, msg = _check_mcp_authentication(server_name) - if is_auth is False: - mcp_auth_warnings.append( - f"⚠️ {server_name}: {msg}" - ) - elif is_auth is None: - mcp_auth_warnings.append( - f"ℹ️ {server_name}: {msg}" - ) - - if mcp_auth_warnings: - warning_msg = ( - "**MCP Server Authentication Issues:**\n\n" - + "\n".join(mcp_auth_warnings) - + "\n\nThese servers may not work correctly " - "until re-authenticated." - ) - logger.warning(warning_msg) - yield RawEvent( - type=EventType.RAW, - thread_id=thread_id, - run_id=run_id, - event={ - "type": "mcp_authentication_warning", - "message": warning_msg, - "servers": [ - s.split(": ")[1] if ": " in s else s - for s in mcp_auth_warnings - ], - }, - ) - - # --- MCP tools --- - # Session control tool - restart_tool = create_restart_session_tool(self, sdk_tool) - session_tools_server = create_sdk_mcp_server( - name="session", version="1.0.0", tools=[restart_tool] - ) - mcp_servers["session"] = session_tools_server - logger.info( - "Added custom session control MCP tools (restart_session)" - ) + options["temperature"] = float(temperature_env) + except (ValueError, TypeError): + pass - # Dynamic rubric evaluation tool - rubric_content, rubric_config = load_rubric_content(cwd_path) - if rubric_content or rubric_config: - rubric_tool = create_rubric_mcp_tool( - rubric_content=rubric_content or "", - rubric_config=rubric_config, - obs=obs, - session_id=self.context.session_id, - sdk_tool_decorator=sdk_tool, - ) - if rubric_tool: - rubric_server = create_sdk_mcp_server( - name="rubric", - version="1.0.0", - tools=[rubric_tool], - ) - mcp_servers["rubric"] = rubric_server - logger.info( - "Added dynamic rubric evaluation MCP tool " - f"(categories: " - f"{list(rubric_config.get('schema', {}).keys())})" - ) + if not first_run or is_continuation: + options["continue_conversation"] = True + logger.info("Enabled continue_conversation (resume from disk state)") - # Tool permissions - allowed_tools = [ - "Read", - "Write", - "Bash", - "Glob", - "Grep", - "Edit", - "MultiEdit", - "WebSearch", - ] - if mcp_servers: - for server_name in mcp_servers.keys(): - allowed_tools.append(f"mcp__{server_name}") - logger.info( - f"MCP tool permissions granted for servers: " - f"{list(mcp_servers.keys())}" - ) - - # --- System prompt --- - workspace_prompt = prompts.build_workspace_context_prompt( - repos_cfg=repos_cfg, - workflow_name=( - derived_name if active_workflow_url else None - ), - artifacts_path="artifacts", - ambient_config=ambient_config, - workspace_path=self.context.workspace_path, - ) - system_prompt_config = { - "type": "preset", - "preset": "claude_code", - "append": workspace_prompt, - } - - # Capture stderr from the SDK - def sdk_stderr_handler(line: str): - logger.warning(f"[SDK stderr] {line.rstrip()}") - - # --- SDK options --- - options = ClaudeAgentOptions( - cwd=cwd_path, - permission_mode="acceptEdits", - allowed_tools=allowed_tools, - mcp_servers=mcp_servers, - setting_sources=["project"], - system_prompt=system_prompt_config, - include_partial_messages=True, - stderr=sdk_stderr_handler, - ) - - if self._skip_resume_on_restart: - self._skip_resume_on_restart = False - - try: - if add_dirs: - options.add_dirs = add_dirs - except Exception: - pass - - if model: - try: - options.model = configured_model - except Exception: - pass - - max_tokens_env = self.context.get_env( - "LLM_MAX_TOKENS" - ) or self.context.get_env("MAX_TOKENS") - if max_tokens_env: - try: - options.max_tokens = int(max_tokens_env) - except Exception: - pass - - temperature_env = self.context.get_env( - "LLM_TEMPERATURE" - ) or self.context.get_env("TEMPERATURE") - if temperature_env: - try: - options.temperature = float(temperature_env) - except Exception: - pass - - # --- Client creation --- - result_payload = None - current_message = None - sdk_session_id = None - - def create_sdk_client(opts, disable_continue=False): - if disable_continue and hasattr(opts, "continue_conversation"): - opts.continue_conversation = False - return ClaudeSDKClient(options=opts) - - logger.info("Creating new ClaudeSDKClient for this run...") - - if not self._first_run or is_continuation: - try: - options.continue_conversation = True - logger.info( - "Enabled continue_conversation " - "(will resume from disk state)" - ) - yield RawEvent( - type=EventType.RAW, - thread_id=thread_id, - run_id=run_id, - event={ - "type": "system_log", - "message": "🔄 Resuming conversation from disk state", - }, - ) - except Exception as e: - logger.warning( - f"Failed to set continue_conversation: {e}" - ) - - try: - logger.info("Creating ClaudeSDKClient...") - client = create_sdk_client(options) - logger.info( - "Connecting ClaudeSDKClient (initializing subprocess)..." - ) - await client.connect() - logger.info("ClaudeSDKClient connected successfully!") - except Exception as resume_error: - error_str = str(resume_error).lower() - if ( - "no conversation found" in error_str - or "session" in error_str - ): - logger.warning( - f"Conversation continuation failed: {resume_error}" - ) - yield RawEvent( - type=EventType.RAW, - thread_id=thread_id, - run_id=run_id, - event={ - "type": "system_log", - "message": "⚠️ Could not continue conversation, starting fresh...", - }, - ) - client = create_sdk_client(options, disable_continue=True) - await client.connect() - else: - raise - - try: - self._active_client = client - - # Process the prompt - step_id = str(uuid.uuid4()) - yield StepStartedEvent( - type=EventType.STEP_STARTED, - thread_id=thread_id, - run_id=run_id, - step_id=step_id, - step_name="processing_prompt", - ) - - logger.info( - f"Sending query to Claude SDK: '{prompt[:100]}...'" - ) - await client.query(prompt) - logger.info("Query sent, waiting for response stream...") - - # --- Process response stream --- - logger.info( - "Starting to consume receive_response() iterator..." - ) - message_count = 0 - - async for message in client.receive_response(): - message_count += 1 - logger.info( - f"[ClaudeSDKClient Message #{message_count}]: " - f"{message}" - ) - - # Handle StreamEvent for real-time streaming chunks - if isinstance(message, StreamEvent): - event_data = message.event - event_type = event_data.get("type") - - if event_type == "message_start": - current_message_id = str(uuid.uuid4()) - yield TextMessageStartEvent( - type=EventType.TEXT_MESSAGE_START, - thread_id=thread_id, - run_id=run_id, - message_id=current_message_id, - role="assistant", - ) - - elif event_type == "content_block_delta": - delta_data = event_data.get("delta", {}) - if delta_data.get("type") == "text_delta": - text_chunk = delta_data.get("text", "") - if text_chunk and current_message_id: - yield TextMessageContentEvent( - type=EventType.TEXT_MESSAGE_CONTENT, - thread_id=thread_id, - run_id=run_id, - message_id=current_message_id, - delta=text_chunk, - ) - continue - - # Capture SDK session ID - if isinstance(message, SystemMessage): - if message.subtype == "init" and message.data.get( - "session_id" - ): - sdk_session_id = message.data.get("session_id") - logger.info( - f"Captured SDK session ID: {sdk_session_id}" - ) - - if isinstance(message, (AssistantMessage, UserMessage)): - if isinstance(message, AssistantMessage): - current_message = message - obs.start_turn( - configured_model, user_input=prompt - ) - - trace_id = obs.get_current_trace_id() - if trace_id: - yield RawEvent( - type=EventType.RAW, - thread_id=thread_id, - run_id=run_id, - event={ - "type": "langfuse_trace", - "traceId": trace_id, - }, - ) - - # Process all blocks in the message - for block in ( - getattr(message, "content", []) or [] - ): - if isinstance(block, TextBlock): - text_piece = getattr(block, "text", None) - if text_piece: - logger.info( - f"TextBlock received (complete), " - f"text length={len(text_piece)}" - ) - - elif isinstance(block, ToolUseBlock): - tool_name = ( - getattr(block, "name", "") or "unknown" - ) - tool_input = ( - getattr(block, "input", {}) or {} - ) - tool_id = getattr( - block, "id", None - ) or str(uuid.uuid4()) - parent_tool_use_id = getattr( - message, "parent_tool_use_id", None - ) - - logger.info( - f"ToolUseBlock detected: {tool_name} " - f"(id={tool_id[:12]})" - ) - - yield ToolCallStartEvent( - type=EventType.TOOL_CALL_START, - thread_id=thread_id, - run_id=run_id, - tool_call_id=tool_id, - tool_call_name=tool_name, - parent_tool_call_id=parent_tool_use_id, - ) - - if tool_input: - args_json = _json.dumps(tool_input) - yield ToolCallArgsEvent( - type=EventType.TOOL_CALL_ARGS, - thread_id=thread_id, - run_id=run_id, - tool_call_id=tool_id, - delta=args_json, - ) - - obs.track_tool_use( - tool_name, tool_id, tool_input - ) - - elif isinstance(block, ToolResultBlock): - tool_use_id = getattr( - block, "tool_use_id", None - ) - content = getattr(block, "content", None) - is_error = getattr(block, "is_error", None) - result_text = getattr(block, "text", None) - result_content = ( - content - if content is not None - else result_text - ) - - if result_content is not None: - try: - result_str = _json.dumps( - result_content - ) - except (TypeError, ValueError): - result_str = str(result_content) - else: - result_str = "" - - if tool_use_id: - yield ToolCallEndEvent( - type=EventType.TOOL_CALL_END, - thread_id=thread_id, - run_id=run_id, - tool_call_id=tool_use_id, - result=( - result_str - if not is_error - else None - ), - error=( - result_str - if is_error - else None - ), - ) - - obs.track_tool_result( - tool_use_id, - result_content, - is_error or False, - ) - - elif isinstance(block, ThinkingBlock): - thinking_text = getattr( - block, "thinking", "" - ) - signature = getattr(block, "signature", "") - yield RawEvent( - type=EventType.RAW, - thread_id=thread_id, - run_id=run_id, - event={ - "type": "thinking_block", - "thinking": thinking_text, - "signature": signature, - }, - ) - - # End text message after processing all blocks - if ( - getattr(message, "content", []) - and current_message_id - ): - yield TextMessageEndEvent( - type=EventType.TEXT_MESSAGE_END, - thread_id=thread_id, - run_id=run_id, - message_id=current_message_id, - ) - current_message_id = None - - elif isinstance(message, SystemMessage): - text = getattr(message, "text", None) - if text: - yield RawEvent( - type=EventType.RAW, - thread_id=thread_id, - run_id=run_id, - event={ - "type": "system_log", - "level": "debug", - "message": str(text), - }, - ) - - elif isinstance(message, ResultMessage): - usage_raw = getattr(message, "usage", None) - sdk_num_turns = getattr(message, "num_turns", None) - - logger.info( - f"ResultMessage: num_turns={sdk_num_turns}, " - f"usage={usage_raw}" - ) - - # Convert usage object to dict if needed - if usage_raw is not None and not isinstance( - usage_raw, dict - ): - try: - if hasattr(usage_raw, "__dict__"): - usage_raw = usage_raw.__dict__ - elif hasattr(usage_raw, "model_dump"): - usage_raw = usage_raw.model_dump() - except Exception as e: - logger.warning( - "Could not convert usage object " - f"to dict: {e}" - ) - - if ( - sdk_num_turns is not None - and sdk_num_turns > self._turn_count - ): - self._turn_count = sdk_num_turns - - if current_message: - obs.end_turn( - self._turn_count, - current_message, - ( - usage_raw - if isinstance(usage_raw, dict) - else None - ), - ) - current_message = None - - result_payload = { - "subtype": getattr(message, "subtype", None), - "duration_ms": getattr( - message, "duration_ms", None - ), - "is_error": getattr(message, "is_error", None), - "num_turns": getattr(message, "num_turns", None), - "total_cost_usd": getattr( - message, "total_cost_usd", None - ), - "usage": usage_raw, - "result": getattr(message, "result", None), - } - - yield StateDeltaEvent( - type=EventType.STATE_DELTA, - thread_id=thread_id, - run_id=run_id, - delta=[ - { - "op": "replace", - "path": "/lastResult", - "value": result_payload, - } - ], - ) - - # End step - yield StepFinishedEvent( - type=EventType.STEP_FINISHED, - thread_id=thread_id, - run_id=run_id, - step_id=step_id, - step_name="processing_prompt", - ) - - logger.info( - f"Response iterator fully consumed " - f"({message_count} messages total)" - ) - - self._first_run = False - - # Check if restart was requested - if self._restart_requested: - logger.info( - "🔄 Restart was requested, emitting restart event" - ) - self._restart_requested = False - yield RawEvent( - type=EventType.RAW, - thread_id=thread_id, - run_id=run_id, - event={ - "type": "session_restart_requested", - "message": "Claude requested a session restart. " - "Reconnecting...", - }, - ) - - finally: - self._active_client = None - if client is not None: - logger.info("Disconnecting client (end of run)") - await client.disconnect() - - # Finalize observability - await obs.finalize() - - except Exception as e: - logger.error(f"Failed to run Claude Code SDK: {e}") - if "obs" in locals(): - await obs.cleanup_on_error(e) - raise - - async def interrupt(self) -> None: - """Interrupt the active Claude SDK execution.""" - if self._active_client is None: - logger.warning("Interrupt requested but no active client") - return - - try: - logger.info("Sending interrupt signal to Claude SDK client...") - await self._active_client.interrupt() - logger.info("Interrupt signal sent successfully") - except Exception as e: - logger.error(f"Failed to interrupt Claude SDK: {e}") + return options diff --git a/components/runners/claude-code-runner/ag_ui_claude_sdk/__init__.py b/components/runners/claude-code-runner/ag_ui_claude_sdk/__init__.py new file mode 100644 index 000000000..20496c21c --- /dev/null +++ b/components/runners/claude-code-runner/ag_ui_claude_sdk/__init__.py @@ -0,0 +1,48 @@ +""" +AG-UI integration for Anthropic Claude Agent SDK. + +This package provides an AG-UI compatible adapter for the Claude Agent SDK, +enabling Claude-powered agents to communicate with AG-UI compatible frontends. + +Example: + from fastapi import FastAPI, Request + from fastapi.responses import StreamingResponse + from ag_ui.core import RunAgentInput + from ag_ui.encoder import EventEncoder + from ag_ui_claude_sdk import ClaudeAgentAdapter + + app = FastAPI() + adapter = ClaudeAgentAdapter( + name="my_agent", + model="claude-sonnet-4-20250514", + permission_mode="acceptEdits", + ) + + @app.post("/") + async def run(input_data: RunAgentInput, request: Request): + encoder = EventEncoder(accept=request.headers.get("accept")) + async def stream(): + async for event in adapter.run(input_data): + yield encoder.encode(event) + return StreamingResponse(stream(), media_type=encoder.get_content_type()) + +For full documentation on ClaudeAgentOptions, see: +https://platform.claude.com/docs/en/agent-sdk/python +""" + +from .adapter import ClaudeAgentAdapter +from .config import ( + ALLOWED_FORWARDED_PROPS, + STATE_MANAGEMENT_TOOL_NAME, + AG_UI_MCP_SERVER_NAME, +) + +__version__ = "0.1.0" +__all__ = [ + "ClaudeAgentAdapter", + # Configuration constants + "ALLOWED_FORWARDED_PROPS", + "STATE_MANAGEMENT_TOOL_NAME", + "AG_UI_MCP_SERVER_NAME", +] + diff --git a/components/runners/claude-code-runner/ag_ui_claude_sdk/adapter.py b/components/runners/claude-code-runner/ag_ui_claude_sdk/adapter.py new file mode 100644 index 000000000..97c1e3324 --- /dev/null +++ b/components/runners/claude-code-runner/ag_ui_claude_sdk/adapter.py @@ -0,0 +1,982 @@ +""" +Claude Agent SDK adapter for AG-UI protocol. + +This adapter wraps the Claude Agent SDK and produces AG-UI protocol events, +enabling Claude-powered agents to work with any AG-UI compatible frontend. +""" + +import os +import logging +import json +import uuid +from typing import AsyncIterator, Optional, List, Dict, Any, Union, TYPE_CHECKING + +# AG-UI Protocol Events +from ag_ui.core import ( + EventType, + RunAgentInput, + BaseEvent, + AssistantMessage as AguiAssistantMessage, + ToolCall as AguiToolCall, + FunctionCall as AguiFunctionCall, + RunStartedEvent, + RunFinishedEvent, + RunErrorEvent, + TextMessageStartEvent, + TextMessageContentEvent, + TextMessageEndEvent, + ToolCallStartEvent, + ToolCallArgsEvent, + ToolCallEndEvent, + StateSnapshotEvent, + MessagesSnapshotEvent, + ThinkingTextMessageStartEvent, + ThinkingTextMessageContentEvent, + ThinkingTextMessageEndEvent, + ThinkingStartEvent, + ThinkingEndEvent, +) + +# Type checking imports for Claude SDK types +if TYPE_CHECKING: + from claude_agent_sdk import ClaudeAgentOptions + +# Import helper functions and constants +from .utils import ( + process_messages, + build_state_context_addendum, + convert_agui_tool_to_claude_sdk, + create_state_management_tool, + apply_forwarded_props, + extract_tool_names, + strip_mcp_prefix, + build_agui_assistant_message, + build_agui_tool_message, +) +from .config import ( + ALLOWED_FORWARDED_PROPS, + STATE_MANAGEMENT_TOOL_NAME, + STATE_MANAGEMENT_TOOL_FULL_NAME, + AG_UI_MCP_SERVER_NAME, +) +from .handlers import ( + handle_tool_use_block, + handle_tool_result_block, + emit_system_message_events, +) + +logger = logging.getLogger(__name__) + +# Configure logger if not already configured +if not logger.handlers: + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) + logger.addHandler(handler) + # Respect LOGLEVEL environment variable (defaults to INFO) + log_level = os.getenv("LOGLEVEL", "INFO").upper() + logger.setLevel(getattr(logging, log_level, logging.INFO)) + + +class ClaudeAgentAdapter: + """ + Adapter that wraps the Claude Agent SDK for AG-UI servers. + + Produces AG-UI protocol events via async generator from Claude SDK responses. + + This adapter accepts Claude SDK configuration via the `options` parameter. + Follows the LangGraph pattern: accepts ClaudeAgentOptions object OR dict for convenience. + + Configuration options: + 1. ClaudeAgentOptions object (fully typed, recommended for production) + 2. dict with option parameters (convenient for examples/prototyping) + 3. None (uses sensible defaults) + + Session Management: + Claude SDK maintains conversation state via session_id mapped to .claude/ directory. + This adapter tracks session_ids per thread_id for proper resumption across runs. + + For production deployment with persistent sessions, mount the .claude/ directory + as a persistent volume. See: https://platform.claude.com/docs/en/agent-sdk/hosting + + RunAgentInput Field Handling: + - thread_id: Mapped to Claude SDK session_id for conversation continuity + - run_id: Used for event correlation in AG-UI protocol + - messages: All validated; last user message sent to SDK (SDK manages history) + - tools: Dynamically added as "ag_ui" MCP server (stub implementations for frontend tools) + - context: Appended to system_prompt for agent awareness + - state: Appended to system_prompt + ag_ui_update_state tool created for bidirectional sync + - parent_run_id: Passed through to RUN_STARTED for branching/lineage tracking + - forwarded_props: Per-run option overrides (see ALLOWED_FORWARDED_PROPS for whitelist) + + Frontend Tool Execution (Human-in-the-Loop Pattern): + When Claude calls a frontend tool (tool name matches input.tools): + 1. Backend emits TOOL_CALL_START/ARGS/END events (streaming arguments) + 2. Backend HALTS stream immediately after TOOL_CALL_END (Strands pattern) + 3. Client executes tool handler with complete arguments + 4. Client sends ToolMessage back in NEXT RunAgentInput.messages + 5. Backend resumes conversation with tool result + + This pause-and-resume pattern enables proper human-in-the-loop workflows + where frontend tools can update UI state, request user input, etc. + + Forwarded Props Support: + Per-run overrides for execution control without changing agent identity. + Whitelisted keys include: resume, fork_session, model, temperature, max_tokens, + max_thinking_tokens, max_turns, max_budget_usd, output_format, etc. + + Example: + RunAgentInput( + forwarded_props={ + "model": "claude-opus-4", + "max_turns": 5, + "temperature": 0.8 + } + ) + + State Management: + When state is provided in RunAgentInput: + 1. Initial state emitted as STATE_SNAPSHOT event + 2. State appended to system_prompt so Claude can see current values + 3. ag_ui_update_state tool created dynamically + 4. When Claude calls ag_ui_update_state, we emit STATE_SNAPSHOT with new values + 5. Client receives STATE_SNAPSHOT and updates UI accordingly + + This enables bidirectional state sync similar to LangGraph/CopilotKit patterns. + + Example: + # Using dict (convenient for examples) + adapter = ClaudeAgentAdapter( + name="my_agent", + description="A helpful assistant", + options={ + "model": "claude-haiku-4-5", + "cwd": "/my/project", + "system_prompt": "You are helpful", + "permission_mode": "acceptEdits", + "allowed_tools": ["Read", "Write", "Bash"], + } + ) + + # Using ClaudeAgentOptions (recommended for production - fully typed!) + from claude_agent_sdk import ClaudeAgentOptions + options = ClaudeAgentOptions( + model="claude-haiku-4-5", + cwd="/my/project", + system_prompt="You are helpful", + permission_mode="acceptEdits", + sandbox={"enabled": True}, + ) + adapter = ClaudeAgentAdapter( + name="my_agent", + options=options + ) + """ + + def __init__( + self, + name: str, + options: Union["ClaudeAgentOptions", dict, None] = None, + description: str = "", + ): + """ + Initialize the Claude Agent adapter. + + Follows the LangGraph pattern: accepts a typed ClaudeAgentOptions object + OR a dict with option parameters (for convenience without losing type safety). + + Args: + name: Name of the agent (for identification and logging). + options: Claude SDK configuration. Can be: + - ClaudeAgentOptions instance (fully typed, recommended) + - dict with ClaudeAgentOptions fields (convenience) + - None (uses defaults) + + Common options (when using dict): + - model: str - Claude model (e.g., "claude-haiku-4-5") + - system_prompt: str | dict - Custom system prompt + - cwd: str - Working directory + - mcp_servers: dict - MCP servers mapping + - allowed_tools: list[str] - Tool names to allow + - permission_mode: str - "default" | "acceptEdits" | etc + - max_tokens: int - Response length limit + - temperature: float - Sampling temperature + + See full ClaudeAgentOptions docs: + https://platform.claude.com/docs/en/agent-sdk/python + + description: Optional description of the agent. + """ + # Agent metadata + self.name = name + self.description = description + + # Store the options (ClaudeAgentOptions object OR dict) + self._options = options + + # Extract api_key for setup + if isinstance(options, dict): + self.api_key = options.get("api_key") or os.getenv("ANTHROPIC_API_KEY", "") + elif options is not None and hasattr(options, "api_key"): + self.api_key = getattr(options, "api_key", None) or os.getenv("ANTHROPIC_API_KEY", "") + else: + self.api_key = os.getenv("ANTHROPIC_API_KEY", "") + + # Track Claude SDK session IDs per thread (for session resumption) + # Maps thread_id -> session_id returned by Claude SDK + self._session_ids_by_thread: Dict[str, str] = {} + + # Active client reference (for interrupt support) + self._active_client: Optional[Any] = None + + # Result data from last run (for RunFinished event) + self._last_result_data: Optional[Dict[str, Any]] = None + + # Current state tracking per run (for state management) + self._current_state: Optional[Any] = None + + async def run(self, input_data: RunAgentInput) -> AsyncIterator[BaseEvent]: + """ + Process a run and yield AG-UI events. + + This is the main entry point that consumes RunAgentInput and produces + a stream of AG-UI protocol events. + + Args: + input_data: RunAgentInput with thread_id, run_id, messages, tools, + context, state, forwarded_props, etc. + + Yields: + AG-UI events (RunStartedEvent, TextMessageContentEvent, etc.) + """ + thread_id = input_data.thread_id or str(uuid.uuid4()) + run_id = input_data.run_id or str(uuid.uuid4()) + + # Clear result data from any previous run + self._last_result_data = None + + # Initialize state tracking for this run + self._current_state = input_data.state + + try: + # Log parent_run_id if provided (for branching/time travel tracking) + if input_data.parent_run_id: + logger.debug( + f"Run {run_id[:8]}... is branched from parent run {input_data.parent_run_id[:8]}..." + ) + + # Emit RUN_STARTED with input capture (following LangGraph pattern) + yield RunStartedEvent( + type=EventType.RUN_STARTED, + thread_id=thread_id, + run_id=run_id, + parent_run_id=input_data.parent_run_id, # Pass through for lineage tracking + input={ + "thread_id": thread_id, + "run_id": run_id, + "parent_run_id": input_data.parent_run_id, + "messages": input_data.messages, + "tools": input_data.tools, + "state": input_data.state, + "context": input_data.context, + "forwarded_props": input_data.forwarded_props, + } + ) + + # Process all messages and extract user message + user_message, _ = process_messages(input_data) + + # Extract frontend tool names for halt detection (like Strands pattern) + frontend_tool_names = set(extract_tool_names(input_data.tools)) if input_data.tools else set() + if frontend_tool_names: + logger.debug(f"Frontend tools detected: {frontend_tool_names}") + + # Log tools from input - these will be dynamically added as MCP server + if input_data.tools: + tool_names = extract_tool_names(input_data.tools) + logger.debug( + f"Client provided {len(input_data.tools)} frontend tools: {tool_names}. " + f"Creating dynamic ag_ui_frontend MCP server." + ) + + # Log state from input (for debugging - Claude SDK manages state internally) + if input_data.state: + logger.debug( + f"Client provided state with keys: {list(input_data.state.keys()) if isinstance(input_data.state, dict) else 'non-dict state'}. " + f"Note: Claude SDK manages state internally via session_id." + ) + + # Log context from input (for debugging - not used by Claude SDK) + if input_data.context: + logger.debug( + f"Client provided {len(input_data.context)} context items. " + f"Note: Claude SDK manages context via session history." + ) + + # Log forwarded_props for debugging + if input_data.forwarded_props: + logger.debug( + f"Received forwarded_props: {input_data.forwarded_props}" + ) + + if not user_message: + logger.warning("No user message found in input") + yield RunFinishedEvent( + type=EventType.RUN_FINISHED, + thread_id=thread_id, + run_id=run_id, + ) + return + + # Emit initial state snapshot if provided + if input_data.state: + yield StateSnapshotEvent( + type=EventType.STATE_SNAPSHOT, + snapshot=input_data.state + ) + + # Run Claude SDK and yield events + async for event in self._stream_claude_sdk( + user_message, thread_id, run_id, input_data, frontend_tool_names + ): + yield event + + # Emit RUN_FINISHED with result data from ResultMessage + yield RunFinishedEvent( + type=EventType.RUN_FINISHED, + thread_id=thread_id, + run_id=run_id, + result=self._last_result_data, + ) + + except Exception as e: + logger.error(f"Error in run: {e}") + yield RunErrorEvent( + type=EventType.RUN_ERROR, + thread_id=thread_id, + run_id=run_id, + message=str(e), + ) + + def _build_options(self, input_data: Optional[RunAgentInput] = None, thread_id: Optional[str] = None) -> "ClaudeAgentOptions": + """ + Build ClaudeAgentOptions from stored options (object/dict/None) plus dynamic tools. + + Follows LangGraph pattern: handles ClaudeAgentOptions | dict | None. + + Args: + input_data: Optional RunAgentInput for extracting dynamic tools + thread_id: Optional thread_id for session resumption lookup + + Returns: + Configured ClaudeAgentOptions instance + """ + from claude_agent_sdk import ClaudeAgentOptions, create_sdk_mcp_server + + # Start with sensible defaults + merged_kwargs: Dict[str, Any] = { + "include_partial_messages": True, + } + + # Merge in provided options + if self._options is not None: + if isinstance(self._options, dict): + # Dict format - merge directly + for key, value in self._options.items(): + if value is not None: + merged_kwargs[key] = value + + else: + # ClaudeAgentOptions object - extract attributes + # Try Pydantic v2 style first + if hasattr(self._options, "model_dump"): + base_dict = self._options.model_dump(exclude_none=True) + merged_kwargs.update(base_dict) + # Fall back to Pydantic v1 style + elif hasattr(self._options, "dict"): + base_dict = self._options.dict(exclude_none=True) + merged_kwargs.update(base_dict) + # Fall back to __dict__ for plain dataclasses/objects + elif hasattr(self._options, "__dict__"): + for key, value in self._options.__dict__.items(): + if not key.startswith("_") and value is not None: + merged_kwargs[key] = value + logger.debug(f"Merged kwargs: {merged_kwargs}") + + # Append state and context to the system prompt (not the user message). + if input_data: + addendum = build_state_context_addendum(input_data) + if addendum: + base = merged_kwargs.get("system_prompt", "") or "" + merged_kwargs["system_prompt"] = f"{base}\n\n{addendum}" if base else addendum + logger.debug(f"Appended state/context ({len(addendum)} chars) to system_prompt") + + # Ensure ag_ui tools are always allowed (frontend tools + state management) + if input_data and (input_data.state or input_data.tools): + allowed_tools = merged_kwargs.get("allowed_tools", []) + tools_to_add = [] + + # Add state management tool if state is provided + if input_data.state and STATE_MANAGEMENT_TOOL_FULL_NAME not in allowed_tools: + tools_to_add.append(STATE_MANAGEMENT_TOOL_FULL_NAME) + + # Add frontend tools (prefixed with mcp__ag_ui__) + if input_data.tools: + for tool_name in extract_tool_names(input_data.tools): + prefixed_name = f"mcp__ag_ui__{tool_name}" + if prefixed_name not in allowed_tools: + tools_to_add.append(prefixed_name) + + if tools_to_add: + merged_kwargs["allowed_tools"] = [*allowed_tools, *tools_to_add] + logger.debug(f"Auto-granted permission to ag_ui tools: {tools_to_add}") + + # Remove api_key from options kwargs (handled via environment variable) + merged_kwargs.pop("api_key", None) + logger.debug(f"Merged kwargs after pop: {merged_kwargs}") + + # Apply forwarded_props as per-run overrides (before adding dynamic tools) + if input_data and input_data.forwarded_props: + merged_kwargs = apply_forwarded_props( + input_data.forwarded_props, + merged_kwargs, + ALLOWED_FORWARDED_PROPS + ) + + # Add dynamic tools from input.tools and state management + if input_data: + # Get existing MCP servers + existing_servers = merged_kwargs.get("mcp_servers", {}) + ag_ui_tools = [] + + # Add frontend tools from input.tools + if input_data.tools: + logger.debug(f"Building dynamic MCP server with {len(input_data.tools)} frontend tools") + + for tool_def in input_data.tools: + try: + claude_tool = convert_agui_tool_to_claude_sdk(tool_def) + ag_ui_tools.append(claude_tool) + except Exception as e: + logger.warning(f"Failed to convert tool: {e}") + + # Add state management tool if state is provided + if input_data.state: + logger.debug("Adding ag_ui_update_state tool for state management") + state_tool = create_state_management_tool() + ag_ui_tools.append(state_tool) + + # Create ag_ui MCP server if we have any tools + if ag_ui_tools: + ag_ui_server = create_sdk_mcp_server( + AG_UI_MCP_SERVER_NAME, + "1.0.0", + tools=ag_ui_tools + ) + + # Merge with existing servers + merged_kwargs["mcp_servers"] = { + **existing_servers, + AG_UI_MCP_SERVER_NAME: ag_ui_server + } + + # Get tool names safely (SdkMcpTool objects don't have __name__) + tool_names = [] + for t in ag_ui_tools: + if hasattr(t, '__name__'): + tool_names.append(t.__name__) + elif hasattr(t, 'name'): + tool_names.append(t.name) + else: + tool_names.append(str(type(t).__name__)) + + logger.debug( + f"Created ag_ui MCP server with {len(ag_ui_tools)} tools: {tool_names}" + ) + + # Add session resumption if we have an existing session for this thread + # resume option tells the Claude SDK to load and continue the previous conversation + # (session_id on client.query() just labels the session directory, doesn't resume) + if thread_id: + existing_session_id = self._session_ids_by_thread.get(thread_id) + if existing_session_id: + merged_kwargs["resume"] = existing_session_id + logger.debug(f"Added resume={existing_session_id[:8]}... for thread {thread_id[:8]}...") + + # Create the options object + logger.debug(f"Creating ClaudeAgentOptions with merged kwargs: {merged_kwargs}") + return ClaudeAgentOptions(**merged_kwargs) + + async def _stream_claude_sdk( + self, + prompt: str, + thread_id: str, + run_id: str, + input_data: RunAgentInput, + frontend_tool_names: set[str], + ) -> AsyncIterator[BaseEvent]: + """ + Execute the Claude SDK with the given prompt and yield AG-UI events. + + Args: + prompt: The user prompt to send to Claude + thread_id: AG-UI thread identifier + run_id: AG-UI run identifier + input_data: Full RunAgentInput for context + frontend_tool_names: Set of frontend tool names for halt detection + """ + # Per-run state (local to this invocation) + current_message_id: Optional[str] = None + in_thinking_block: bool = False # Track if we're inside a thinking content block + has_streamed_text: bool = False # Track if we've streamed any text content + + # Tool call streaming state + current_tool_call_id: Optional[str] = None + current_tool_call_name: Optional[str] = None + current_tool_display_name: Optional[str] = None # Unprefixed name for frontend matching + accumulated_tool_json: str = "" # Accumulate partial JSON for tool arguments + + # Track which tools we've already emitted START for (to avoid duplicates) + processed_tool_ids: set = set() + + # Frontend tool halt flag (like Strands pattern) + halt_event_stream: bool = False # Set to True when frontend tool completes + + # ── MESSAGES_SNAPSHOT accumulation ── + # All message types go here. At the end we emit: + # MESSAGES_SNAPSHOT = [...input_data.messages, ...run_messages] + run_messages: List[Any] = [] + pending_msg: Optional[Dict[str, Any]] = None + accumulated_thinking_text = "" + + def upsert_message(msg): + """Upsert a message: replace if same ID exists, otherwise append.""" + msg_id = getattr(msg, "id", None) + for i, m in enumerate(run_messages): + if getattr(m, "id", None) == msg_id: + run_messages[i] = msg + return + run_messages.append(msg) + + def flush_pending_msg(): + """Flush pendingMsg → run_messages (upsert so streaming version wins over fallback).""" + nonlocal pending_msg + if pending_msg is None: + return + if pending_msg["content"] or pending_msg["tool_calls"]: + upsert_message( + AguiAssistantMessage( + id=pending_msg["id"], + role="assistant", + content=pending_msg["content"] or None, + tool_calls=pending_msg["tool_calls"] or None, + ) + ) + pending_msg = None + + if not self.api_key: + raise RuntimeError("ANTHROPIC_API_KEY must be set") + + # Set environment variable for SDK + os.environ['ANTHROPIC_API_KEY'] = self.api_key + + # Import Claude SDK (after setting env var) + from claude_agent_sdk import ClaudeSDKClient + from claude_agent_sdk import ( + AssistantMessage, + UserMessage, + SystemMessage, + ResultMessage, + TextBlock, + ThinkingBlock, + ToolUseBlock, + ToolResultBlock, + ) + from claude_agent_sdk.types import StreamEvent + + # Build options dynamically from base options + kwargs + input.tools + session resume + options = self._build_options(input_data=input_data, thread_id=thread_id) + + # Create client + logger.debug("Creating ClaudeSDKClient...") + client = ClaudeSDKClient(options=options) + + # Store reference for interrupt support + self._active_client = client + + try: + # Connect to SDK + logger.debug("Connecting to Claude SDK...") + await client.connect() + logger.debug("Connected successfully!") + + # Use existing session_id as label if we have one, otherwise thread_id + existing_session_id = self._session_ids_by_thread.get(thread_id) + session_label = existing_session_id or thread_id + + if existing_session_id: + logger.debug( + f"Resuming session {existing_session_id[:8]}... for thread {thread_id[:8]}..." + ) + else: + logger.debug( + f"Starting new session for thread {thread_id[:8]}... " + f"(message_count={len(input_data.messages)})" + ) + + await client.query(prompt, session_id=session_label) + + logger.debug("Query sent, waiting for response stream...") + + # Process response stream + message_count = 0 + + async for message in client.receive_response(): + message_count += 1 + + # If we've halted due to frontend tool, break out of loop (interrupt already called) + if halt_event_stream: + logger.debug(f"[ClaudeSDKClient Message #{message_count}]: Halted - breaking stream loop") + break # Stop consuming, interrupt() already stopped generation + + logger.debug(f"[ClaudeSDKClient Message #{message_count}]: {message}") + + # Handle StreamEvent for real-time streaming chunks + if isinstance(message, StreamEvent): + event_data = message.event + event_type = event_data.get('type') + + if event_type == 'message_start': + current_message_id = str(uuid.uuid4()) + has_streamed_text = False + pending_msg = {"id": current_message_id, "content": "", "tool_calls": []} + + elif event_type == 'content_block_delta': + delta_data = event_data.get('delta', {}) + delta_type = delta_data.get('type', '') + + if delta_type == 'text_delta': + text_chunk = delta_data.get('text', '') + if text_chunk and current_message_id: + if not has_streamed_text: + yield TextMessageStartEvent( + type=EventType.TEXT_MESSAGE_START, + thread_id=thread_id, + run_id=run_id, + message_id=current_message_id, + role="assistant", + ) + has_streamed_text = True + if pending_msg is not None: + pending_msg["content"] += text_chunk + + yield TextMessageContentEvent( + type=EventType.TEXT_MESSAGE_CONTENT, + thread_id=thread_id, + run_id=run_id, + message_id=current_message_id, + delta=text_chunk, + ) + elif delta_type == 'thinking_delta': + thinking_chunk = delta_data.get('thinking', '') + if thinking_chunk: + accumulated_thinking_text += thinking_chunk + yield ThinkingTextMessageContentEvent( + type=EventType.THINKING_TEXT_MESSAGE_CONTENT, + delta=thinking_chunk, + ) + elif delta_type == 'input_json_delta': + # Handle streaming tool arguments + partial_json = delta_data.get('partial_json', '') + if partial_json and current_tool_call_id: + # Accumulate JSON for potential parsing + accumulated_tool_json += partial_json + + # Emit TOOL_CALL_ARGS with the delta + yield ToolCallArgsEvent( + type=EventType.TOOL_CALL_ARGS, + thread_id=thread_id, + run_id=run_id, + tool_call_id=current_tool_call_id, + delta=partial_json, + ) + + elif event_type == 'content_block_start': + block_data = event_data.get('content_block', {}) + block_type = block_data.get('type', '') + + if block_type == 'thinking': + in_thinking_block = True + yield ThinkingStartEvent(type=EventType.THINKING_START) + yield ThinkingTextMessageStartEvent(type=EventType.THINKING_TEXT_MESSAGE_START) + elif block_type == 'tool_use': + # Tool call starting - emit TOOL_CALL_START + current_tool_call_id = block_data.get('id') + current_tool_call_name = block_data.get('name', 'unknown') + accumulated_tool_json = "" + + if current_tool_call_id: + current_tool_display_name = strip_mcp_prefix(current_tool_call_name) + processed_tool_ids.add(current_tool_call_id) + + yield ToolCallStartEvent( + type=EventType.TOOL_CALL_START, + thread_id=thread_id, + run_id=run_id, + tool_call_id=current_tool_call_id, + tool_call_name=current_tool_display_name, # Use unprefixed name for frontend matching! + parent_message_id=current_message_id, # Link to parent message + ) + + elif event_type == 'content_block_stop': + if in_thinking_block: + in_thinking_block = False + yield ThinkingTextMessageEndEvent(type=EventType.THINKING_TEXT_MESSAGE_END) + yield ThinkingEndEvent(type=EventType.THINKING_END) + + # Persist thinking content + if accumulated_thinking_text: + from ag_ui.core import DeveloperMessage + upsert_message(DeveloperMessage( + id=str(uuid.uuid4()), + role="developer", + content=accumulated_thinking_text, + )) + accumulated_thinking_text = "" + + # Close tool call if we were streaming one + if current_tool_call_id: + # Check if this is the state management tool + if current_tool_call_name in (STATE_MANAGEMENT_TOOL_NAME, STATE_MANAGEMENT_TOOL_FULL_NAME): + # Parse accumulated JSON and emit STATE_SNAPSHOT + try: + state_updates = json.loads(accumulated_tool_json) + + # Extract state_updates from the parsed args + if isinstance(state_updates, dict): + updates = state_updates.get("state_updates", state_updates) + + # Parse nested JSON string if needed + if isinstance(updates, str): + updates = json.loads(updates) + + # Update current state + if isinstance(self._current_state, dict) and isinstance(updates, dict): + self._current_state = {**self._current_state, **updates} + else: + self._current_state = updates + + yield StateSnapshotEvent( + type=EventType.STATE_SNAPSHOT, + snapshot=self._current_state + ) + except (json.JSONDecodeError, ValueError) as e: + logger.warning(f"Failed to parse tool JSON for state update: {e}") + + # Push tool call onto in-flight message (skip state management) + if ( + pending_msg is not None + and current_tool_call_id + and current_tool_display_name + and current_tool_call_name not in (STATE_MANAGEMENT_TOOL_NAME, STATE_MANAGEMENT_TOOL_FULL_NAME) + ): + pending_msg["tool_calls"].append( + AguiToolCall( + id=current_tool_call_id, + type="function", + function=AguiFunctionCall( + name=current_tool_display_name, + arguments=accumulated_tool_json, + ), + ) + ) + + # Check if this is a frontend tool (using unprefixed name for comparison) + # Frontend tools should halt the stream so client can execute handler + is_frontend_tool = current_tool_display_name in frontend_tool_names + + if is_frontend_tool: + # Flush before halt (message_stop won't fire after interrupt) + flush_pending_msg() + + # Emit TOOL_CALL_END for frontend tool (client needs this to know call is complete) + yield ToolCallEndEvent( + type=EventType.TOOL_CALL_END, + thread_id=thread_id, + run_id=run_id, + tool_call_id=current_tool_call_id, + ) + + if current_message_id and has_streamed_text: + yield TextMessageEndEvent( + type=EventType.TEXT_MESSAGE_END, + thread_id=thread_id, + run_id=run_id, + message_id=current_message_id, + ) + current_message_id = None + + logger.debug(f"Frontend tool halt: {current_tool_display_name}") + + if self._active_client: + try: + await self._active_client.interrupt() + except Exception as e: + logger.warning(f"Failed to interrupt stream: {e}") + + halt_event_stream = True + # Continue consuming remaining events for cleanup + continue + + # For regular backend tools, DON'T emit TOOL_CALL_END here + # Backend tools will have ToolResultBlock which emits END + RESULT + + # Reset tool streaming state + current_tool_call_id = None + current_tool_call_name = None + current_tool_display_name = None + accumulated_tool_json = "" + + elif event_type == 'message_stop': + flush_pending_msg() + + if current_message_id and has_streamed_text: + yield TextMessageEndEvent( + type=EventType.TEXT_MESSAGE_END, + thread_id=thread_id, + run_id=run_id, + message_id=current_message_id, + ) + current_message_id = None + + elif event_type == 'message_delta': + # Handle message-level delta (e.g., stop_reason, usage) + delta_data = event_data.get('delta', {}) + stop_reason = delta_data.get('stop_reason') + if stop_reason: + logger.debug(f"Message stop_reason: {stop_reason}") + + continue + + # Handle complete messages + if isinstance(message, (AssistantMessage, UserMessage)): + # Accumulate from complete SDK message (fallback path). + # Uses the streaming ID so flush_pending_msg() can replace it + # with the richer streaming version (which has tool_calls). + if isinstance(message, AssistantMessage): + msg_id = current_message_id or str(uuid.uuid4()) + agui_msg = build_agui_assistant_message(message, msg_id) + if agui_msg: + upsert_message(agui_msg) + + # Process non-streamed blocks (fallback for tools not seen via stream events) + for block in getattr(message, 'content', []) or []: + if isinstance(block, ToolUseBlock): + tool_id = getattr(block, 'id', None) + if tool_id and tool_id in processed_tool_ids: + continue + updated_state, tool_events = await handle_tool_use_block( + block, message, thread_id, run_id, self._current_state + ) + if tool_id: + processed_tool_ids.add(tool_id) + if updated_state is not None: + self._current_state = updated_state + async for event in tool_events: + yield event + + elif isinstance(block, ToolResultBlock): + tool_use_id = getattr(block, 'tool_use_id', None) + block_content = getattr(block, 'content', None) + if tool_use_id: + upsert_message(build_agui_tool_message(tool_use_id, block_content)) + parent_id = getattr(message, 'parent_tool_use_id', None) + async for event in handle_tool_result_block(block, thread_id, run_id, parent_id): + yield event + + elif isinstance(message, SystemMessage): + subtype = getattr(message, 'subtype', '') + data = getattr(message, 'data', {}) or {} + + if subtype == 'init' and data: + returned_session_id = data.get('session_id') + if returned_session_id: + self._session_ids_by_thread[thread_id] = returned_session_id + + msg_text = (data.get('message') or data.get('text') or '') if data else '' + + if msg_text: + sys_msg_id = str(uuid.uuid4()) + for evt in emit_system_message_events(thread_id, run_id, msg_text): + yield evt + + from ag_ui.core import SystemMessage as AguiSystemMessage + upsert_message(AguiSystemMessage( + id=sys_msg_id, + role="system", + content=msg_text, + )) + + elif isinstance(message, ResultMessage): + is_error = getattr(message, 'is_error', None) + result_text = getattr(message, 'result', None) + + # Capture metadata for RunFinished event + self._last_result_data = { + "is_error": is_error, + "duration_ms": getattr(message, 'duration_ms', None), + "duration_api_ms": getattr(message, 'duration_api_ms', None), + "num_turns": getattr(message, 'num_turns', None), + "total_cost_usd": getattr(message, 'total_cost_usd', None), + "usage": getattr(message, 'usage', None), + "structured_output": getattr(message, 'structured_output', None), + } + + if not has_streamed_text and result_text: + result_msg_id = str(uuid.uuid4()) + yield TextMessageStartEvent(type=EventType.TEXT_MESSAGE_START, thread_id=thread_id, run_id=run_id, message_id=result_msg_id, role="assistant") + yield TextMessageContentEvent(type=EventType.TEXT_MESSAGE_CONTENT, thread_id=thread_id, run_id=run_id, message_id=result_msg_id, delta=result_text) + yield TextMessageEndEvent(type=EventType.TEXT_MESSAGE_END, thread_id=thread_id, run_id=run_id, message_id=result_msg_id) + + upsert_message(AguiAssistantMessage( + id=result_msg_id, + role="assistant", + content=result_text, + )) + + # Emit MESSAGES_SNAPSHOT with input messages + new messages from this run + if run_messages: + all_messages = list(input_data.messages or []) + run_messages + logger.debug( + f"MESSAGES_SNAPSHOT: {len(all_messages)} msgs ({message_count} SDK messages processed)" + ) + yield MessagesSnapshotEvent( + type=EventType.MESSAGES_SNAPSHOT, + messages=all_messages, + ) + + # Errors propagate to run() which emits RunErrorEvent + + finally: + # Clear active client reference + self._active_client = None + + # Always disconnect client + if client is not None: + logger.debug("Disconnecting Claude SDK client") + await client.disconnect() + + async def interrupt(self) -> None: + """ + Interrupt the active Claude SDK execution. + """ + if self._active_client is None: + logger.warning("Interrupt requested but no active client") + return + + try: + logger.debug("Sending interrupt signal to Claude SDK...") + await self._active_client.interrupt() + logger.debug("Interrupt signal sent successfully") + except Exception as e: + logger.error(f"Failed to interrupt Claude SDK: {e}") + diff --git a/components/runners/claude-code-runner/ag_ui_claude_sdk/config.py b/components/runners/claude-code-runner/ag_ui_claude_sdk/config.py new file mode 100644 index 000000000..3b908b990 --- /dev/null +++ b/components/runners/claude-code-runner/ag_ui_claude_sdk/config.py @@ -0,0 +1,40 @@ +""" +Configuration constants for Claude Agent SDK adapter. + +Defines whitelists, defaults, and configuration options. +""" + +# Whitelist of forwarded_props keys that can be applied as per-run option overrides +# These are runtime execution controls, not agent identity/security settings +ALLOWED_FORWARDED_PROPS = { + # Session control + "resume", # Session ID to resume + "fork_session", # Fork vs continue session + "resume_session_at", # Time travel to specific message + + # Model control + "model", # Per-run model override + "fallback_model", # Fallback if primary fails + "temperature", # Sampling temperature + "max_tokens", # Response length limit + "max_thinking_tokens", # Reasoning depth limit + "max_turns", # Conversation turn limit + "max_budget_usd", # Cost limit per run + + # Output control + "output_format", # Structured output schema + "include_partial_messages", # Streaming granularity + + # Optional features + "enable_file_checkpointing", # File change tracking + "strict_mcp_config", # MCP validation strictness + "betas", # Beta feature flags +} + +# Special tool name for state management +STATE_MANAGEMENT_TOOL_NAME = "ag_ui_update_state" +# Full prefixed name as it appears from Claude SDK +STATE_MANAGEMENT_TOOL_FULL_NAME = "mcp__ag_ui__ag_ui_update_state" + +# MCP server name for dynamic AG-UI tools +AG_UI_MCP_SERVER_NAME = "ag_ui" diff --git a/components/runners/claude-code-runner/ag_ui_claude_sdk/handlers.py b/components/runners/claude-code-runner/ag_ui_claude_sdk/handlers.py new file mode 100644 index 000000000..6207294fa --- /dev/null +++ b/components/runners/claude-code-runner/ag_ui_claude_sdk/handlers.py @@ -0,0 +1,296 @@ +""" +Event handlers for Claude SDK stream processing. + +Breaks down stream processing into focused handler functions. +""" + +import json +import logging +import uuid +from typing import AsyncIterator, Any, Dict, Optional + +from ag_ui.core import ( + EventType, + BaseEvent, + TextMessageStartEvent, + TextMessageContentEvent, + TextMessageEndEvent, + ToolCallStartEvent, + ToolCallArgsEvent, + ToolCallEndEvent, + ToolCallResultEvent, + StateSnapshotEvent, + CustomEvent, + ThinkingTextMessageStartEvent, + ThinkingTextMessageContentEvent, + ThinkingTextMessageEndEvent, + ThinkingStartEvent, + ThinkingEndEvent, +) + +from .config import STATE_MANAGEMENT_TOOL_NAME, STATE_MANAGEMENT_TOOL_FULL_NAME +from .utils import strip_mcp_prefix + +logger = logging.getLogger(__name__) + + +async def handle_tool_use_block( + block: Any, + message: Any, + thread_id: str, + run_id: str, + current_state: Optional[Any], +) -> tuple[Optional[Any], AsyncIterator[BaseEvent]]: + """ + Handle ToolUseBlock from Claude SDK. + + Intercepts state management tool calls and emits STATE_SNAPSHOT. + For regular tools, emits TOOL_CALL_START/ARGS events. + + Args: + block: ToolUseBlock from Claude SDK + message: Parent message containing the block + thread_id: Thread identifier + run_id: Run identifier + current_state: Current state for state management tools + + Returns: + Tuple of (updated_state, event_generator) + """ + tool_name = getattr(block, 'name', '') or 'unknown' + tool_input = getattr(block, 'input', {}) or {} + tool_id = getattr(block, 'id', None) or str(uuid.uuid4()) + parent_tool_use_id = getattr(message, 'parent_tool_use_id', None) + + # Strip MCP prefix for client matching (same as streaming path) + tool_display_name = strip_mcp_prefix(tool_name) + if tool_display_name != tool_name: + logger.debug(f"Stripped MCP prefix in handler: {tool_name} -> {tool_display_name}") + + logger.debug(f"ToolUseBlock detected: {tool_name}") + + async def event_gen(): + nonlocal current_state + + # Intercept state management tool calls (check both prefixed and unprefixed names) + if tool_name in (STATE_MANAGEMENT_TOOL_NAME, STATE_MANAGEMENT_TOOL_FULL_NAME): + logger.debug("Intercepting ag_ui_update_state tool call") + + # Extract state updates from tool input + state_updates = tool_input.get("state_updates", {}) + + # Parse if it's a JSON string + if isinstance(state_updates, str): + try: + state_updates = json.loads(state_updates) + logger.debug("Parsed state_updates from JSON string") + except json.JSONDecodeError as e: + logger.warning(f"Failed to parse state_updates JSON: {e}") + state_updates = {} + + # Update current state + if isinstance(current_state, dict) and isinstance(state_updates, dict): + current_state = {**current_state, **state_updates} + else: + current_state = state_updates + + # Emit STATE_SNAPSHOT with updated state + yield StateSnapshotEvent( + type=EventType.STATE_SNAPSHOT, + snapshot=current_state + ) + + logger.debug(f"Emitted STATE_SNAPSHOT with updated state") + return # Skip normal tool call events + + # Regular tool handling for non-state tools + yield ToolCallStartEvent( + type=EventType.TOOL_CALL_START, + thread_id=thread_id, + run_id=run_id, + tool_call_id=tool_id, + tool_call_name=tool_display_name, # Use unprefixed name + parent_message_id=parent_tool_use_id, + ) + + if tool_input: + args_json = json.dumps(tool_input) + yield ToolCallArgsEvent( + type=EventType.TOOL_CALL_ARGS, + thread_id=thread_id, + run_id=run_id, + tool_call_id=tool_id, + delta=args_json, + ) + + return current_state, event_gen() + + +async def handle_tool_result_block( + block: Any, + thread_id: str, + run_id: str, + parent_tool_use_id: Optional[str] = None, +) -> AsyncIterator[BaseEvent]: + """ + Handle ToolResultBlock from Claude SDK. + + Emits TOOL_CALL_END and TOOL_CALL_RESULT events. + Nested tool results (with parent_tool_use_id) are also emitted - they represent + sub-agent calls (e.g., Task calling WebSearch). + + Args: + block: ToolResultBlock from Claude SDK + thread_id: Thread identifier + run_id: Run identifier + parent_tool_use_id: Parent tool ID if this is a nested result + + Yields: + AG-UI tool result events + """ + tool_use_id = getattr(block, 'tool_use_id', None) + content = getattr(block, 'content', None) + is_error = getattr(block, 'is_error', None) + + # Parse tool result content for frontend rendering + # Claude SDK tools return: [{"type": "text", "text": "{json_data}"}] + # Frontend expects just the parsed json_data + result_str = "" + if content is not None: + try: + # If content is a list of content blocks (Claude SDK format) + if isinstance(content, list) and len(content) > 0: + first_block = content[0] + if isinstance(first_block, dict) and first_block.get("type") == "text": + # Extract the text content + text_content = first_block.get("text", "") + # Try to parse as JSON (tools often return JSON strings) + try: + parsed_json = json.loads(text_content) + # Use the parsed JSON directly so frontend can access fields + result_str = json.dumps(parsed_json) + except (json.JSONDecodeError, ValueError): + # Not JSON, use as-is + result_str = text_content + else: + # Fallback: stringify the whole content + result_str = json.dumps(content) + else: + # Fallback: stringify as-is + result_str = json.dumps(content) + except (TypeError, ValueError): + result_str = str(content) + + if tool_use_id: + # Emit ToolCallEnd to signal completion + yield ToolCallEndEvent( + type=EventType.TOOL_CALL_END, + thread_id=thread_id, + run_id=run_id, + tool_call_id=tool_use_id, + ) + + # Emit ToolCallResult with the actual result content + result_message_id = f"{tool_use_id}-result" + yield ToolCallResultEvent( + type=EventType.TOOL_CALL_RESULT, + thread_id=thread_id, + run_id=run_id, + message_id=result_message_id, + tool_call_id=tool_use_id, + content=result_str, + role="tool", + ) + + +async def handle_thinking_block( + block: Any, + thread_id: str, + run_id: str, +) -> AsyncIterator[BaseEvent]: + """ + Handle ThinkingBlock from Claude SDK. + + Emits THINKING_TEXT_MESSAGE events and optional signature custom event. + + Args: + block: ThinkingBlock from Claude SDK + thread_id: Thread identifier + run_id: Run identifier + + Yields: + AG-UI thinking events + """ + thinking_text = getattr(block, 'thinking', '') + signature = getattr(block, 'signature', '') + + # Emit proper ThinkingTextMessage events for thinking blocks + if thinking_text: + # Emit THINKING_START/END wrappers (like LangGraph pattern) + yield ThinkingStartEvent( + type=EventType.THINKING_START, + ) + yield ThinkingTextMessageStartEvent( + type=EventType.THINKING_TEXT_MESSAGE_START, + ) + yield ThinkingTextMessageContentEvent( + type=EventType.THINKING_TEXT_MESSAGE_CONTENT, + delta=thinking_text, + ) + yield ThinkingTextMessageEndEvent( + type=EventType.THINKING_TEXT_MESSAGE_END, + ) + yield ThinkingEndEvent( + type=EventType.THINKING_END, + ) + + # Also emit signature as custom event if present + if signature: + yield CustomEvent( + type=EventType.CUSTOM, + thread_id=thread_id, + run_id=run_id, + name="thinking_signature", + value={"signature": signature}, + ) + + +def emit_system_message_events( + thread_id: str, + run_id: str, + message: str +) -> list[BaseEvent]: + """ + Create system message events. + + Args: + thread_id: Thread identifier + run_id: Run identifier + message: System message text + + Returns: + List of events to yield + """ + msg_id = str(uuid.uuid4()) + return [ + TextMessageStartEvent( + type=EventType.TEXT_MESSAGE_START, + thread_id=thread_id, + run_id=run_id, + message_id=msg_id, + role="system", + ), + TextMessageContentEvent( + type=EventType.TEXT_MESSAGE_CONTENT, + thread_id=thread_id, + run_id=run_id, + message_id=msg_id, + delta=message, + ), + TextMessageEndEvent( + type=EventType.TEXT_MESSAGE_END, + thread_id=thread_id, + run_id=run_id, + message_id=msg_id, + ), + ] diff --git a/components/runners/claude-code-runner/ag_ui_claude_sdk/types.py b/components/runners/claude-code-runner/ag_ui_claude_sdk/types.py new file mode 100644 index 000000000..163113b51 --- /dev/null +++ b/components/runners/claude-code-runner/ag_ui_claude_sdk/types.py @@ -0,0 +1,6 @@ +""" +Type definitions for AG-UI Claude SDK integration. + +All types are provided by ag_ui.core or Claude SDK directly. +This module is reserved for future custom type definitions. +""" diff --git a/components/runners/claude-code-runner/ag_ui_claude_sdk/utils.py b/components/runners/claude-code-runner/ag_ui_claude_sdk/utils.py new file mode 100644 index 000000000..e7dcd45b1 --- /dev/null +++ b/components/runners/claude-code-runner/ag_ui_claude_sdk/utils.py @@ -0,0 +1,413 @@ +""" +Utility functions for Claude Agent SDK adapter. + +Helper functions for message processing, tool conversion, and prompt building. +""" + +import json +import logging +from typing import Any, Dict, List, Optional, Tuple +from ag_ui.core import RunAgentInput, AssistantMessage, ToolCall, FunctionCall, ToolMessage + +from .config import STATE_MANAGEMENT_TOOL_NAME, STATE_MANAGEMENT_TOOL_FULL_NAME + +logger = logging.getLogger(__name__) + + +def extract_tool_names(tools: List[Any]) -> List[str]: + """ + Extract tool names from AG-UI tool definitions. + + Handles both dict format and object format consistently. + + Args: + tools: List of AG-UI Tool definitions (dict or Tool objects) + + Returns: + List of tool name strings + """ + names = [] + for tool_def in tools: + name = tool_def.get("name") if isinstance(tool_def, dict) else getattr(tool_def, "name", None) + if name: + names.append(name) + return names + + +def strip_mcp_prefix(tool_name: str) -> str: + """ + Strip mcp__servername__ prefix from Claude SDK tool names. + + Claude SDK prefixes all MCP tools: mcp__weather__get_weather, mcp__ag_ui__generate_haiku + Frontend registers unprefixed: get_weather, generate_haiku + + Args: + tool_name: Full MCP-prefixed tool name + + Returns: + Unprefixed tool name for client matching + + Examples: + "mcp__weather__get_weather" -> "get_weather" + "mcp__ag_ui__generate_haiku" -> "generate_haiku" + "local_tool" -> "local_tool" (unchanged) + """ + if tool_name.startswith("mcp__"): + parts = tool_name.split("__") + if len(parts) >= 3: # mcp__servername__toolname + return "__".join(parts[2:]) # Keep just toolname (handles double underscores in names) + return tool_name + + +def process_messages(input_data: RunAgentInput) -> Tuple[str, bool]: + """ + Process and validate all messages from RunAgentInput. + + Similar to AWS Strands pattern: validates full message history even though + Claude SDK manages conversation via session_id. + + Args: + input_data: RunAgentInput with messages array + + Returns: + Tuple of (user_message: str, has_pending_tool_result: bool) + """ + messages = input_data.messages or [] + + # Check if last message is a tool result (for re-submission handling) + has_pending_tool_result = False + if messages: + last_msg = messages[-1] + if hasattr(last_msg, 'role') and last_msg.role == 'tool': + has_pending_tool_result = True + logger.debug( + f"Pending tool result detected: tool_call_id={getattr(last_msg, 'tool_call_id', 'unknown')}, " + f"thread_id={input_data.thread_id}" + ) + + # Log message counts for debugging + logger.debug( + f"Processing {len(messages)} messages for thread_id={input_data.thread_id}" + ) + + # Validate and log all messages (even though we only use the last one) + for i, msg in enumerate(messages): + role = getattr(msg, 'role', msg.get('role') if isinstance(msg, dict) else 'unknown') + has_tool_calls = hasattr(msg, 'tool_calls') and bool(msg.tool_calls) + tool_call_id = getattr(msg, 'tool_call_id', None) + + logger.debug( + f"Message [{i}]: role={role}, has_tool_calls={has_tool_calls}, " + f"tool_call_id={tool_call_id}" + ) + + # Extract content from the LAST message (any role - user, tool, or assistant) + # Claude SDK manages conversation history via session_id, we just need the latest input + user_message = "" + if messages: + last_msg = messages[-1] + + # Extract content based on message structure + if hasattr(last_msg, 'content'): + content = last_msg.content + elif isinstance(last_msg, dict): + content = last_msg.get('content', '') + else: + content = '' + + # Handle different content formats + if isinstance(content, str): + user_message = content + elif isinstance(content, list): + # Content blocks format - extract text from first text block + for block in content: + if hasattr(block, 'text'): + user_message = block.text + break + elif isinstance(block, dict) and 'text' in block: + user_message = block['text'] + break + + if not user_message: + logger.warning(f"No user message found in {len(messages)} messages") + + return user_message, has_pending_tool_result + + +def build_state_context_addendum(input_data: RunAgentInput) -> str: + """ + Build state and context addendum for injection into the system prompt. + + Returns the formatted text block describing current state and application + context, or an empty string if neither is present. + + This keeps state/context in the system prompt (where it belongs) rather + than polluting the user message. + + Args: + input_data: RunAgentInput containing state and context + + Returns: + Formatted addendum string, or empty string if nothing to add + """ + parts = [] + + # Add context if provided + if input_data.context: + parts.append("## Context from the application") + for ctx in input_data.context: + parts.append(f"- {ctx.description}: {ctx.value}") + parts.append("") + + # Add current state if provided + if input_data.state: + parts.append("## Current Shared State") + parts.append("This state is shared with the frontend UI and can be updated.") + try: + state_json = json.dumps(input_data.state, indent=2) + parts.append(f"```json\n{state_json}\n```") + except (TypeError, ValueError) as e: + logger.warning(f"Failed to serialize state: {e}") + parts.append(f"State: {str(input_data.state)}") + + parts.append("") + parts.append("To update this state, use the `ag_ui_update_state` tool with your changes.") + parts.append("") + + return "\n".join(parts) + + +def convert_agui_tool_to_claude_sdk(tool_def: Any) -> Any: + """ + Convert an AG-UI tool definition to a Claude SDK MCP tool. + + Creates a proxy tool that Claude can "see" and call, but with stub implementation + since actual execution happens on the client side. + + Args: + tool_def: AG-UI Tool definition (dict or Tool object) + + Returns: + Claude SDK tool definition + """ + from claude_agent_sdk import tool + + # Extract tool properties + if isinstance(tool_def, dict): + tool_name = tool_def.get("name", "unknown") + tool_description = tool_def.get("description", "") + tool_parameters = tool_def.get("parameters", {}) + else: + tool_name = getattr(tool_def, "name", "unknown") + tool_description = getattr(tool_def, "description", "") + tool_parameters = getattr(tool_def, "parameters", {}) + + # Claude SDK @tool decorator accepts FULL JSON Schema format! + # From docs: input_schema can be either: + # 1. Simple type mapping: {"param": str, "count": int} + # 2. Full JSON Schema: {"type": "object", "properties": {...}, "required": [...]} + # + # For frontend tools with complex schemas (arrays, enums, nested objects), + # we pass the COMPLETE JSON Schema (option 2) which includes: + # - type: "object" + # - properties: {...} + # - required: [...] + # - items for arrays, enum constraints, etc. + # + # This gives Claude proper understanding of nested structures! + param_schema = tool_parameters if tool_parameters else {} + + # Create stub tool with empty implementation (execution happens client-side) + @tool(tool_name, tool_description, param_schema) + async def frontend_tool_stub(args: dict) -> dict: + """ + Stub implementation - actual execution happens on client side. + When Claude calls this tool, we emit TOOL_CALL events and client executes. + """ + return { + "content": [{"type": "text", "text": "Tool call forwarded to client"}] + } + + return frontend_tool_stub + + +def create_state_management_tool() -> Any: + """ + Create ag_ui_update_state tool for bidirectional state sync. + + This tool allows Claude to update the shared application state, + which is then emitted to the client via STATE_SNAPSHOT events. + + Returns: + Claude SDK tool definition for state updates + """ + from claude_agent_sdk import tool + + @tool( + "ag_ui_update_state", + "Update the shared application state. Use this to persist changes that should be visible in the UI. " + "Pass the complete updated state object.", + {"state_updates": dict} + ) + async def update_state_tool(args: dict) -> dict: + """ + Stub implementation - actual state emission happens in stream processing. + When Claude calls this, we intercept and emit STATE_SNAPSHOT events. + """ + return { + "content": [{"type": "text", "text": "State updated successfully"}] + } + + return update_state_tool + + +def apply_forwarded_props( + forwarded_props: Any, + merged_kwargs: Dict[str, Any], + allowed_keys: set +) -> Dict[str, Any]: + """ + Apply forwarded_props as per-run Claude SDK option overrides. + + Only whitelisted keys are applied for security. forwarded_props enables + runtime control (model selection, limits, session control) without + changing agent identity or security boundaries. + + Args: + forwarded_props: Client-provided runtime options + merged_kwargs: Current merged options dict + allowed_keys: Set of allowed forwarded_props keys + + Returns: + Updated merged_kwargs dict + """ + if not forwarded_props or not isinstance(forwarded_props, dict): + return merged_kwargs + + applied_count = 0 + for key, value in forwarded_props.items(): + # Only apply whitelisted keys + if key in allowed_keys and value is not None: + merged_kwargs[key] = value + applied_count += 1 + logger.debug(f"Applied forwarded_prop: {key} = {value}") + elif key not in allowed_keys: + logger.warning( + f"Ignoring non-whitelisted forwarded_prop: {key}. " + f"See ALLOWED_FORWARDED_PROPS for supported keys." + ) + + if applied_count > 0: + logger.debug(f"Applied {applied_count} forwarded_props as option overrides") + + return merged_kwargs + + +def _is_state_management_tool(name: str) -> bool: + """Check whether a tool name is the internal state management tool.""" + return name in (STATE_MANAGEMENT_TOOL_NAME, STATE_MANAGEMENT_TOOL_FULL_NAME) + + +def build_agui_assistant_message( + sdk_message: Any, + message_id: str, +) -> Optional[AssistantMessage]: + """ + Convert a complete Claude SDK AssistantMessage into an AG-UI AssistantMessage. + + Extracts text from TextBlocks and builds ToolCall objects from ToolUseBlocks. + Filters out internal state management tool calls and thinking blocks since + they are not part of the user-visible conversation history. + + Args: + sdk_message: Complete AssistantMessage from Claude SDK + message_id: ID to assign to the AG-UI message (matches streamed ID) + + Returns: + AG-UI AssistantMessage, or None if no user-visible content. + """ + content_blocks = getattr(sdk_message, "content", []) or [] + + text_content = "" + tool_calls: List[ToolCall] = [] + + for block in content_blocks: + block_type = getattr(block, "type", None) + + if block_type == "text": + text_content += getattr(block, "text", "") + + elif block_type == "tool_use": + raw_name = getattr(block, "name", "unknown") + + # Skip internal state management tool — not conversation history + if _is_state_management_tool(raw_name): + continue + + tool_id = getattr(block, "id", None) or "" + tool_input = getattr(block, "input", {}) or {} + + tool_calls.append( + ToolCall( + id=tool_id, + type="function", + function=FunctionCall( + name=strip_mcp_prefix(raw_name), + arguments=json.dumps(tool_input), + ), + ) + ) + # ThinkingBlocks are intentionally skipped — not conversation history + + # Nothing user-visible (e.g. thinking-only message) + if not text_content and not tool_calls: + return None + + return AssistantMessage( + id=message_id, + role="assistant", + content=text_content or None, + tool_calls=tool_calls if tool_calls else None, + ) + + +def build_agui_tool_message( + tool_use_id: str, + content: Any, +) -> ToolMessage: + """ + Build an AG-UI ToolMessage from a Claude SDK tool result block. + + Extracts the text content from the SDK's content block format and + normalises it into a simple string for the AG-UI message. + + Args: + tool_use_id: ID of the tool call this result belongs to + content: Raw content from the ToolResultBlock + + Returns: + AG-UI ToolMessage + """ + result_str = "" + try: + if isinstance(content, list) and len(content) > 0: + first_block = content[0] + if isinstance(first_block, dict) and first_block.get("type") == "text": + text = first_block.get("text", "") + try: + result_str = json.dumps(json.loads(text)) + except (json.JSONDecodeError, ValueError): + result_str = text + else: + result_str = json.dumps(content) + elif content is not None: + result_str = json.dumps(content) + except (TypeError, ValueError): + result_str = str(content or "") + + return ToolMessage( + id=f"{tool_use_id}-result", + role="tool", + content=result_str, + tool_call_id=tool_use_id, + ) diff --git a/components/runners/claude-code-runner/ambient_runner/__init__.py b/components/runners/claude-code-runner/ambient_runner/__init__.py new file mode 100644 index 000000000..5e643c0f5 --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/__init__.py @@ -0,0 +1,22 @@ +""" +Ambient Runner SDK — reusable platform package for AG-UI agent runners. + +Public API:: + + from ambient_runner import add_ambient_endpoints, PlatformBridge + from ambient_runner.bridge import PlatformContext, FrameworkCapabilities +""" + +from ambient_runner.app import add_ambient_endpoints +from ambient_runner.bridge import ( + FrameworkCapabilities, + PlatformBridge, + PlatformContext, +) + +__all__ = [ + "add_ambient_endpoints", + "PlatformBridge", + "PlatformContext", + "FrameworkCapabilities", +] diff --git a/components/runners/claude-code-runner/ambient_runner/app.py b/components/runners/claude-code-runner/ambient_runner/app.py new file mode 100644 index 000000000..0ef7ea227 --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/app.py @@ -0,0 +1,86 @@ +""" +``add_ambient_endpoints(app, bridge)`` — wire Ambient platform endpoints +into a FastAPI application. + +This is the public API of the ambient_runner package. Framework authors +call this once to get all platform features (run, interrupt, health, +capabilities, feedback, repos, workflows, MCP diagnostics). + +Usage:: + + from ambient_runner import add_ambient_endpoints + from ambient_runner.bridges.claude import ClaudeBridge + + app = FastAPI() + add_ambient_endpoints(app, bridge=ClaudeBridge()) +""" + +import logging +from typing import Optional + +from fastapi import FastAPI + +from ambient_runner.bridge import PlatformBridge + +logger = logging.getLogger(__name__) + + +def add_ambient_endpoints( + app: FastAPI, + bridge: PlatformBridge, + *, + enable_repos: bool = True, + enable_workflows: bool = True, + enable_feedback: bool = True, + enable_mcp_status: bool = True, + enable_capabilities: bool = True, +) -> None: + """Register Ambient platform endpoints on a FastAPI app. + + Args: + app: The FastAPI application. + bridge: A ``PlatformBridge`` implementation for the chosen framework. + enable_repos: Include /repos/* endpoints. + enable_workflows: Include /workflow endpoint. + enable_feedback: Include /feedback endpoint. + enable_mcp_status: Include /mcp/status endpoint. + enable_capabilities: Include /capabilities endpoint. + """ + # Store bridge on app state so endpoints can access it + app.state.bridge = bridge + + # Core endpoints (always registered) + from ambient_runner.endpoints.run import router as run_router + from ambient_runner.endpoints.interrupt import router as interrupt_router + from ambient_runner.endpoints.health import router as health_router + + app.include_router(run_router) + app.include_router(interrupt_router) + app.include_router(health_router) + + # Optional platform endpoints + if enable_capabilities: + from ambient_runner.endpoints.capabilities import router as cap_router + app.include_router(cap_router) + + if enable_feedback: + from ambient_runner.endpoints.feedback import router as fb_router + app.include_router(fb_router) + + if enable_repos: + from ambient_runner.endpoints.repos import router as repos_router + app.include_router(repos_router) + + if enable_workflows: + from ambient_runner.endpoints.workflow import router as wf_router + app.include_router(wf_router) + + if enable_mcp_status: + from ambient_runner.endpoints.mcp_status import router as mcp_router + app.include_router(mcp_router) + + caps = bridge.capabilities() + logger.info( + f"Ambient endpoints registered: framework={caps.framework}, " + f"features={caps.agent_features}" + ) diff --git a/components/runners/claude-code-runner/ambient_runner/bridge.py b/components/runners/claude-code-runner/ambient_runner/bridge.py new file mode 100644 index 000000000..e4aea678b --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/bridge.py @@ -0,0 +1,108 @@ +""" +PlatformBridge — abstract base class for framework-specific bridges. + +Each framework (Claude Agent SDK, LangGraph, Cursor SDK, etc.) provides a +bridge implementation that translates platform concepts into framework +config and returns a ready-to-use AG-UI adapter. + +The bridge is the single integration point between the Ambient platform +and any AG-UI-compatible framework adapter. +""" + +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from typing import Any, AsyncIterator, Optional + +from ag_ui.core import BaseEvent, RunAgentInput + + +@dataclass +class PlatformContext: + """Platform context passed to the bridge. + + Contains all resolved platform state: auth credentials, workspace paths, + MCP servers, system prompts, etc. The bridge maps these into framework- + specific configuration. + """ + + session_id: str + workspace_path: str + cwd_path: str = "" + add_dirs: list[str] = field(default_factory=list) + model: str = "" + mcp_servers: dict[str, Any] = field(default_factory=dict) + allowed_tools: list[str] = field(default_factory=list) + system_prompt: dict[str, Any] = field(default_factory=dict) + first_run: bool = True + is_resume: bool = False + environment: dict[str, str] = field(default_factory=dict) + extra: dict[str, Any] = field(default_factory=dict) + + +@dataclass +class FrameworkCapabilities: + """Declares what a framework adapter supports. + + Used by the capabilities endpoint and the frontend to determine which + UI panels and features to show. + """ + + framework: str + agent_features: list[str] = field(default_factory=list) + file_system: bool = False + mcp: bool = False + tracing: Optional[str] = None + session_persistence: bool = False + + +class PlatformBridge(ABC): + """Abstract bridge between the Ambient platform and a framework adapter. + + Subclasses must implement: + - ``capabilities()`` — declares what the framework supports + - ``create_adapter()`` — creates an AG-UI adapter from platform context + - ``run()`` — runs the adapter and yields AG-UI events + - ``interrupt()`` — interrupts the current run + """ + + @abstractmethod + def capabilities(self) -> FrameworkCapabilities: + """Return the capabilities of this framework.""" + ... + + @abstractmethod + def create_adapter(self, ctx: PlatformContext) -> Any: + """Create the framework's AG-UI adapter from platform context. + + Args: + ctx: Resolved platform context with all config. + + Returns: + An AG-UI adapter instance (framework-specific type). + """ + ... + + @abstractmethod + async def run(self, input_data: RunAgentInput) -> AsyncIterator[BaseEvent]: + """Run the adapter and yield AG-UI events. + + Args: + input_data: The AG-UI run input. + + Yields: + AG-UI ``BaseEvent`` instances. + """ + ... + + @abstractmethod + async def interrupt(self) -> None: + """Interrupt the current run.""" + ... + + def needs_rebuild(self, ctx: PlatformContext) -> bool: + """Return True if the adapter should be rebuilt for a new context. + + Default: always returns False (adapter is reused). + Override for frameworks that need rebuilding on config changes. + """ + return False diff --git a/components/runners/claude-code-runner/ambient_runner/bridges/__init__.py b/components/runners/claude-code-runner/ambient_runner/bridges/__init__.py new file mode 100644 index 000000000..1322bc09a --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/bridges/__init__.py @@ -0,0 +1 @@ +"""Framework-specific bridge implementations.""" diff --git a/components/runners/claude-code-runner/ambient_runner/bridges/claude.py b/components/runners/claude-code-runner/ambient_runner/bridges/claude.py new file mode 100644 index 000000000..170553c27 --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/bridges/claude.py @@ -0,0 +1,137 @@ +""" +ClaudeBridge — PlatformBridge implementation for the Claude Agent SDK. + +Maps Ambient platform concepts to ``ClaudeAgentAdapter`` options and +handles the adapter lifecycle (create, run, interrupt). +""" + +import logging +from typing import Any, AsyncIterator + +from ag_ui.core import BaseEvent, RunAgentInput +from ag_ui_claude_sdk import ClaudeAgentAdapter + +from ambient_runner.bridge import ( + FrameworkCapabilities, + PlatformBridge, + PlatformContext, +) + +logger = logging.getLogger(__name__) + + +class ClaudeBridge(PlatformBridge): + """Bridge between the Ambient platform and the Claude Agent SDK. + + Creates and manages a ``ClaudeAgentAdapter`` instance. The adapter + is created once and reused across runs unless ``needs_rebuild()`` + returns True. + """ + + def __init__(self) -> None: + self._adapter: ClaudeAgentAdapter | None = None + self._last_ctx: PlatformContext | None = None + + def capabilities(self) -> FrameworkCapabilities: + return FrameworkCapabilities( + framework="claude-agent-sdk", + agent_features=[ + "agentic_chat", + "backend_tool_rendering", + "shared_state", + "human_in_the_loop", + "thinking", + ], + file_system=True, + mcp=True, + tracing="langfuse", + session_persistence=True, + ) + + def create_adapter(self, ctx: PlatformContext) -> ClaudeAgentAdapter: + """Build a ``ClaudeAgentAdapter`` from platform context.""" + options = self._build_options(ctx) + + adapter = ClaudeAgentAdapter( + name="claude_code_runner", + description="Ambient Code Platform Claude session", + options=options, + ) + + self._adapter = adapter + self._last_ctx = ctx + logger.info(f"ClaudeBridge: adapter created (model={ctx.model})") + return adapter + + async def run(self, input_data: RunAgentInput) -> AsyncIterator[BaseEvent]: + """Run the Claude adapter and yield AG-UI events.""" + if self._adapter is None: + raise RuntimeError("ClaudeBridge: adapter not created — call create_adapter() first") + + async for event in self._adapter.run(input_data): + yield event + + async def interrupt(self) -> None: + """Interrupt the current Claude SDK execution.""" + if self._adapter is None: + raise RuntimeError("ClaudeBridge: no adapter to interrupt") + await self._adapter.interrupt() + + def needs_rebuild(self, ctx: PlatformContext) -> bool: + """Rebuild if CWD, MCP servers, or model changed.""" + if self._last_ctx is None: + return True + return ( + ctx.cwd_path != self._last_ctx.cwd_path + or ctx.model != self._last_ctx.model + or ctx.mcp_servers != self._last_ctx.mcp_servers + ) + + # ------------------------------------------------------------------ + # Private + # ------------------------------------------------------------------ + + @staticmethod + def _build_options(ctx: PlatformContext) -> dict[str, Any]: + """Build the options dict for ``ClaudeAgentAdapter``.""" + + def _stderr_handler(line: str) -> None: + logger.warning(f"[SDK stderr] {line.rstrip()}") + + options: dict[str, Any] = { + "cwd": ctx.cwd_path, + "permission_mode": "acceptEdits", + "allowed_tools": ctx.allowed_tools, + "mcp_servers": ctx.mcp_servers, + "setting_sources": ["project"], + "system_prompt": ctx.system_prompt, + "include_partial_messages": True, + "stderr": _stderr_handler, + } + + if ctx.add_dirs: + options["add_dirs"] = ctx.add_dirs + + if ctx.model: + options["model"] = ctx.model + + # Optional max_tokens / temperature from environment + max_tokens = ctx.environment.get("LLM_MAX_TOKENS") or ctx.environment.get("MAX_TOKENS") + if max_tokens: + try: + options["max_tokens"] = int(max_tokens) + except (ValueError, TypeError): + pass + + temperature = ctx.environment.get("LLM_TEMPERATURE") or ctx.environment.get("TEMPERATURE") + if temperature: + try: + options["temperature"] = float(temperature) + except (ValueError, TypeError): + pass + + if not ctx.first_run or ctx.is_resume: + options["continue_conversation"] = True + logger.info("ClaudeBridge: enabled continue_conversation") + + return options diff --git a/components/runners/claude-code-runner/ambient_runner/bridges/langgraph.py b/components/runners/claude-code-runner/ambient_runner/bridges/langgraph.py new file mode 100644 index 000000000..b4969426c --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/bridges/langgraph.py @@ -0,0 +1,119 @@ +""" +LangGraphBridge — PlatformBridge implementation for LangGraph. + +Validates that the PlatformBridge abstraction works with a fundamentally +different framework: + +- No filesystem access (LangGraph agents are stateless functions) +- No native MCP support (tools are LangChain-style) +- Different tracing (LangSmith instead of Langfuse) +- No CWD concept (workspace context goes in system prompt only) + +This bridge demonstrates that a different framework can plug into the +same Ambient platform with different capabilities. The frontend +capabilities system automatically hides UI panels that don't apply. +""" + +import logging +from typing import Any, AsyncIterator + +from ag_ui.core import BaseEvent, RunAgentInput + +from ambient_runner.bridge import ( + FrameworkCapabilities, + PlatformBridge, + PlatformContext, +) + +logger = logging.getLogger(__name__) + + +class LangGraphBridge(PlatformBridge): + """Bridge between the Ambient platform and LangGraph. + + Requires ``ag_ui_langgraph`` to be installed. The adapter translates + LangGraph's graph execution into AG-UI events. + + This bridge differs from ClaudeBridge in several ways: + - ``file_system=False`` — LangGraph agents don't have filesystem access + - ``mcp=False`` — no native MCP; tools are defined in the graph + - ``tracing="langsmith"`` — uses LangSmith instead of Langfuse + - No CWD, add_dirs, or allowed_tools + """ + + def __init__(self) -> None: + self._adapter: Any = None + self._last_ctx: PlatformContext | None = None + + def capabilities(self) -> FrameworkCapabilities: + return FrameworkCapabilities( + framework="langgraph", + agent_features=[ + "agentic_chat", + "shared_state", + "human_in_the_loop", + ], + file_system=False, + mcp=False, + tracing="langsmith", + session_persistence=False, + ) + + def create_adapter(self, ctx: PlatformContext) -> Any: + """Build a LangGraph AG-UI adapter from platform context. + + The adapter wraps a LangGraph ``CompiledGraph`` and translates + its execution into AG-UI events. + """ + try: + from ag_ui_langgraph import LangGraphAgent + except ImportError: + raise RuntimeError( + "LangGraphBridge requires ag_ui_langgraph. " + "Install it: pip install ag-ui-langgraph" + ) + + # LangGraph config from platform context + graph_url = ctx.environment.get("LANGGRAPH_URL", "") + graph_id = ctx.environment.get("LANGGRAPH_GRAPH_ID", "agent") + api_key = ctx.environment.get("LANGSMITH_API_KEY", "") + + if not graph_url: + raise RuntimeError("LANGGRAPH_URL must be set for LangGraph bridge") + + self._adapter = LangGraphAgent( + url=graph_url, + graph_id=graph_id, + api_key=api_key, + ) + + self._last_ctx = ctx + logger.info(f"LangGraphBridge: adapter created (url={graph_url}, graph={graph_id})") + return self._adapter + + async def run(self, input_data: RunAgentInput) -> AsyncIterator[BaseEvent]: + """Run the LangGraph adapter and yield AG-UI events.""" + if self._adapter is None: + raise RuntimeError("LangGraphBridge: adapter not created") + + async for event in self._adapter.run(input_data): + yield event + + async def interrupt(self) -> None: + """Interrupt the current LangGraph execution.""" + if self._adapter is None: + raise RuntimeError("LangGraphBridge: no adapter to interrupt") + + if hasattr(self._adapter, "interrupt"): + await self._adapter.interrupt() + else: + logger.warning("LangGraphBridge: adapter does not support interrupt") + + def needs_rebuild(self, ctx: PlatformContext) -> bool: + """Rebuild if the graph URL or graph ID changed.""" + if self._last_ctx is None: + return True + return ( + ctx.environment.get("LANGGRAPH_URL") != self._last_ctx.environment.get("LANGGRAPH_URL") + or ctx.environment.get("LANGGRAPH_GRAPH_ID") != self._last_ctx.environment.get("LANGGRAPH_GRAPH_ID") + ) diff --git a/components/runners/claude-code-runner/ambient_runner/endpoints/__init__.py b/components/runners/claude-code-runner/ambient_runner/endpoints/__init__.py new file mode 100644 index 000000000..34f5c6c6b --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/endpoints/__init__.py @@ -0,0 +1 @@ +"""Ambient Runner SDK endpoint routers.""" diff --git a/components/runners/claude-code-runner/ambient_runner/endpoints/capabilities.py b/components/runners/claude-code-runner/ambient_runner/endpoints/capabilities.py new file mode 100644 index 000000000..45d424862 --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/endpoints/capabilities.py @@ -0,0 +1,26 @@ +"""GET /capabilities — reports framework and platform capabilities.""" + +import logging + +from fastapi import APIRouter, Request + +logger = logging.getLogger(__name__) + +router = APIRouter() + + +@router.get("/capabilities") +async def get_capabilities(request: Request): + """Return the capabilities manifest from the bridge.""" + bridge = request.app.state.bridge + caps = bridge.capabilities() + + return { + "framework": caps.framework, + "agent_features": caps.agent_features, + "platform_features": [], # Filled dynamically from registered routers + "file_system": caps.file_system, + "mcp": caps.mcp, + "tracing": caps.tracing, + "session_persistence": caps.session_persistence, + } diff --git a/components/runners/claude-code-runner/ambient_runner/endpoints/feedback.py b/components/runners/claude-code-runner/ambient_runner/endpoints/feedback.py new file mode 100644 index 000000000..bed1c3aab --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/endpoints/feedback.py @@ -0,0 +1,7 @@ +"""POST /feedback — Langfuse thumbs-up/down scoring (SDK-provided).""" + +# Re-export the existing endpoint router for now. +# In a future extraction to a standalone package, this will be self-contained. +from endpoints.feedback import router + +__all__ = ["router"] diff --git a/components/runners/claude-code-runner/ambient_runner/endpoints/health.py b/components/runners/claude-code-runner/ambient_runner/endpoints/health.py new file mode 100644 index 000000000..2797d31e5 --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/endpoints/health.py @@ -0,0 +1,11 @@ +"""GET /health — health check endpoint.""" + +from fastapi import APIRouter + +router = APIRouter() + + +@router.get("/health") +async def health(): + """Health check.""" + return {"status": "healthy"} diff --git a/components/runners/claude-code-runner/ambient_runner/endpoints/interrupt.py b/components/runners/claude-code-runner/ambient_runner/endpoints/interrupt.py new file mode 100644 index 000000000..75d15a711 --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/endpoints/interrupt.py @@ -0,0 +1,23 @@ +"""POST /interrupt — interrupt the current run.""" + +import logging + +from fastapi import APIRouter, HTTPException, Request + +logger = logging.getLogger(__name__) + +router = APIRouter() + + +@router.post("/interrupt") +async def interrupt_run(request: Request): + """Interrupt the current agent execution.""" + bridge = request.app.state.bridge + + logger.info("Interrupt request received") + try: + await bridge.interrupt() + return {"message": "Interrupt signal sent"} + except Exception as e: + logger.error(f"Interrupt failed: {e}") + raise HTTPException(status_code=500, detail=str(e)) diff --git a/components/runners/claude-code-runner/ambient_runner/endpoints/mcp_status.py b/components/runners/claude-code-runner/ambient_runner/endpoints/mcp_status.py new file mode 100644 index 000000000..e7557ede2 --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/endpoints/mcp_status.py @@ -0,0 +1,5 @@ +"""MCP status endpoint (SDK-provided).""" + +from endpoints.mcp_status import router + +__all__ = ["router"] diff --git a/components/runners/claude-code-runner/ambient_runner/endpoints/repos.py b/components/runners/claude-code-runner/ambient_runner/endpoints/repos.py new file mode 100644 index 000000000..a7d4254c1 --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/endpoints/repos.py @@ -0,0 +1,5 @@ +"""Repository management endpoints (SDK-provided).""" + +from endpoints.repos import router + +__all__ = ["router"] diff --git a/components/runners/claude-code-runner/ambient_runner/endpoints/run.py b/components/runners/claude-code-runner/ambient_runner/endpoints/run.py new file mode 100644 index 000000000..f4494e94f --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/endpoints/run.py @@ -0,0 +1,83 @@ +"""POST / — AG-UI run endpoint (SDK-provided, delegates to bridge).""" + +import logging +import uuid +from typing import Any, Dict, List, Optional, Union + +from ag_ui.core import EventType, RunAgentInput, RunErrorEvent +from ag_ui.encoder import EventEncoder +from fastapi import APIRouter, HTTPException, Request +from fastapi.responses import StreamingResponse +from pydantic import BaseModel + +logger = logging.getLogger(__name__) + +router = APIRouter() + + +class RunnerInput(BaseModel): + """Input model with optional AG-UI fields.""" + + threadId: Optional[str] = None + thread_id: Optional[str] = None + runId: Optional[str] = None + run_id: Optional[str] = None + parentRunId: Optional[str] = None + parent_run_id: Optional[str] = None + messages: List[Dict[str, Any]] + state: Optional[Dict[str, Any]] = None + tools: Optional[List[Any]] = None + context: Optional[Union[List[Any], Dict[str, Any]]] = None + forwardedProps: Optional[Dict[str, Any]] = None + environment: Optional[Dict[str, str]] = None + metadata: Optional[Dict[str, Any]] = None + + def to_run_agent_input(self) -> RunAgentInput: + thread_id = self.threadId or self.thread_id + run_id = self.runId or self.run_id or str(uuid.uuid4()) + parent_run_id = self.parentRunId or self.parent_run_id + context_list = self.context if isinstance(self.context, list) else [] + + return RunAgentInput( + thread_id=thread_id, + run_id=run_id, + parent_run_id=parent_run_id, + messages=self.messages, + state=self.state or {}, + tools=self.tools or [], + context=context_list, + forwarded_props=self.forwardedProps or {}, + ) + + +@router.post("/") +async def run_agent(input_data: RunnerInput, request: Request): + """AG-UI run endpoint — delegates to the bridge.""" + bridge = request.app.state.bridge + + run_agent_input = input_data.to_run_agent_input() + accept_header = request.headers.get("accept", "text/event-stream") + encoder = EventEncoder(accept=accept_header) + + logger.info( + f"Run: thread_id={run_agent_input.thread_id}, run_id={run_agent_input.run_id}" + ) + + async def event_stream(): + try: + async for event in bridge.run(run_agent_input): + yield encoder.encode(event) + except Exception as e: + logger.error(f"Error in event stream: {e}") + yield encoder.encode( + RunErrorEvent( + type=EventType.RUN_ERROR, + message=str(e), + ) + ) + + return StreamingResponse( + event_stream(), + media_type=encoder.get_content_type(), + headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}, + ) diff --git a/components/runners/claude-code-runner/ambient_runner/endpoints/workflow.py b/components/runners/claude-code-runner/ambient_runner/endpoints/workflow.py new file mode 100644 index 000000000..e3b3a4998 --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/endpoints/workflow.py @@ -0,0 +1,5 @@ +"""Workflow management endpoint (SDK-provided).""" + +from endpoints.workflow import router + +__all__ = ["router"] diff --git a/components/runners/claude-code-runner/ambient_runner/middleware/__init__.py b/components/runners/claude-code-runner/ambient_runner/middleware/__init__.py new file mode 100644 index 000000000..c3289423c --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/middleware/__init__.py @@ -0,0 +1,6 @@ +"""AG-UI middleware for the Ambient Runner SDK.""" + +from middleware.tracing import tracing_middleware +from middleware.developer_events import emit_developer_message + +__all__ = ["tracing_middleware", "emit_developer_message"] diff --git a/components/runners/claude-code-runner/ambient_runner/platform/__init__.py b/components/runners/claude-code-runner/ambient_runner/platform/__init__.py new file mode 100644 index 000000000..664544056 --- /dev/null +++ b/components/runners/claude-code-runner/ambient_runner/platform/__init__.py @@ -0,0 +1,5 @@ +"""Platform modules (auth, workspace, mcp, prompts, observability). + +These are re-exported from the top-level modules for now. +In a future standalone package these will be self-contained. +""" diff --git a/components/runners/claude-code-runner/auth.py b/components/runners/claude-code-runner/auth.py index 27d4cc77d..b80920317 100644 --- a/components/runners/claude-code-runner/auth.py +++ b/components/runners/claude-code-runner/auth.py @@ -297,6 +297,47 @@ async def populate_runtime_credentials(context: RunnerContext) -> None: logger.info("Runtime credentials populated successfully") +async def setup_sdk_authentication(context: RunnerContext) -> tuple[str, bool, str]: + """Set up SDK auth env vars for the Claude Agent SDK. + + Configures ANTHROPIC_API_KEY (or Vertex AI placeholders) and returns + the resolved model name. + + Returns: + (api_key, use_vertex, configured_model) + """ + api_key = context.get_env("ANTHROPIC_API_KEY", "") + use_vertex = context.get_env("CLAUDE_CODE_USE_VERTEX", "").strip() == "1" + + if not api_key and not use_vertex: + raise RuntimeError( + "Either ANTHROPIC_API_KEY or CLAUDE_CODE_USE_VERTEX=1 must be set" + ) + + model = context.get_env("LLM_MODEL") + configured_model = model or "claude-sonnet-4-5@20250929" + + if api_key: + os.environ["ANTHROPIC_API_KEY"] = api_key + logger.info("Using Anthropic API key authentication") + + if use_vertex: + vertex_credentials = await setup_vertex_credentials(context) + os.environ["ANTHROPIC_API_KEY"] = "vertex-auth-mode" + os.environ["CLAUDE_CODE_USE_VERTEX"] = "1" + os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = vertex_credentials.get( + "credentials_path", "" + ) + os.environ["ANTHROPIC_VERTEX_PROJECT_ID"] = vertex_credentials.get( + "project_id", "" + ) + os.environ["CLOUD_ML_REGION"] = vertex_credentials.get("region", "") + if model: + configured_model = map_to_vertex_model(model) + + return api_key, use_vertex, configured_model + + async def fetch_github_token_legacy(context: RunnerContext) -> str: """Legacy method — kept for backward compatibility.""" base = os.getenv("BACKEND_API_URL", "").rstrip("/") diff --git a/components/runners/claude-code-runner/endpoints/__init__.py b/components/runners/claude-code-runner/endpoints/__init__.py new file mode 100644 index 000000000..53d19181f --- /dev/null +++ b/components/runners/claude-code-runner/endpoints/__init__.py @@ -0,0 +1,5 @@ +""" +Endpoint routers for the Claude Code AG-UI server. + +Each module provides a FastAPI ``APIRouter`` that gets included in the main app. +""" diff --git a/components/runners/claude-code-runner/endpoints/capabilities.py b/components/runners/claude-code-runner/endpoints/capabilities.py new file mode 100644 index 000000000..e0069f4c1 --- /dev/null +++ b/components/runners/claude-code-runner/endpoints/capabilities.py @@ -0,0 +1,52 @@ +"""GET /capabilities — reports what this runner/framework supports. + +The capabilities response tells the frontend which UI panels to show +(repos, workflows, MCP diagnostics, feedback, etc.) and what AG-UI +features the underlying framework adapter provides. +""" + +import logging +import os + +from fastapi import APIRouter + +from endpoints import state + +logger = logging.getLogger(__name__) + +router = APIRouter() + +# Framework-specific capabilities for Claude Agent SDK +_CLAUDE_AGENT_FEATURES = [ + "agentic_chat", + "backend_tool_rendering", + "shared_state", + "human_in_the_loop", + "thinking", +] + +# Platform features exposed by our endpoints +_PLATFORM_FEATURES = [ + "repos", + "workflows", + "feedback", + "mcp_diagnostics", +] + + +@router.get("/capabilities") +async def get_capabilities(): + """Return the full capabilities manifest for this runner session.""" + has_langfuse = state._obs is not None and state._obs.langfuse_client is not None + + return { + "framework": "claude-agent-sdk", + "agent_features": _CLAUDE_AGENT_FEATURES, + "platform_features": _PLATFORM_FEATURES, + "file_system": True, + "mcp": True, + "tracing": "langfuse" if has_langfuse else None, + "session_persistence": True, + "model": state._configured_model or None, + "session_id": state.context.session_id if state.context else None, + } diff --git a/components/runners/claude-code-runner/endpoints/feedback.py b/components/runners/claude-code-runner/endpoints/feedback.py new file mode 100644 index 000000000..3647b1dd7 --- /dev/null +++ b/components/runners/claude-code-runner/endpoints/feedback.py @@ -0,0 +1,117 @@ +"""POST /feedback — Langfuse thumbs-up/down scoring.""" + +import logging +import os +from typing import Any, Dict, Optional + +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel + +logger = logging.getLogger(__name__) + +router = APIRouter() + + +class FeedbackEvent(BaseModel): + """AG-UI META event for user feedback (thumbs up/down).""" + type: str + metaType: str + payload: Dict[str, Any] + threadId: Optional[str] = None + ts: Optional[int] = None + + +@router.post("/feedback") +async def handle_feedback(event: FeedbackEvent): + """Handle user feedback META events and send to Langfuse.""" + logger.info( + f"Feedback received: {event.metaType} from {event.payload.get('userId', 'unknown')}" + ) + + if event.type != "META": + raise HTTPException(status_code=400, detail="Expected META event type") + if event.metaType not in ("thumbs_up", "thumbs_down"): + raise HTTPException(status_code=400, detail="metaType must be 'thumbs_up' or 'thumbs_down'") + + try: + payload = event.payload + user_id = payload.get("userId", "unknown") + project_name = payload.get("projectName", "") + session_name = payload.get("sessionName", "") + message_id = payload.get("messageId", "") + trace_id = payload.get("traceId", "") + comment = payload.get("comment", "") + reason = payload.get("reason", "") + workflow = payload.get("workflow", "") + context_str = payload.get("context", "") + include_transcript = payload.get("includeTranscript", False) + transcript = payload.get("transcript", []) + + value = event.metaType == "thumbs_up" + + comment_parts = [] + if comment: + comment_parts.append(comment) + if reason: + comment_parts.append(f"Reason: {reason}") + if context_str: + comment_parts.append(f"\nMessage:\n{context_str}") + if include_transcript and transcript: + transcript_text = "\n".join( + f"[{m.get('role', 'unknown')}]: {m.get('content', '')}" + for m in transcript + ) + comment_parts.append(f"\nFull Transcript:\n{transcript_text}") + + feedback_comment = "\n".join(comment_parts) if comment_parts else None + + langfuse_enabled = os.getenv("LANGFUSE_ENABLED", "").strip().lower() in ("1", "true", "yes") + + if langfuse_enabled: + try: + from langfuse import Langfuse + + public_key = os.getenv("LANGFUSE_PUBLIC_KEY", "").strip() + secret_key = os.getenv("LANGFUSE_SECRET_KEY", "").strip() + host = os.getenv("LANGFUSE_HOST", "").strip() + + if public_key and secret_key and host: + langfuse = Langfuse(public_key=public_key, secret_key=secret_key, host=host) + + metadata = { + "project": project_name, + "session": session_name, + "user": user_id, + "feedbackType": event.metaType, + } + if workflow: + metadata["workflow"] = workflow + if message_id: + metadata["messageId"] = message_id + + langfuse.create_score( + name="user-feedback", + value=value, + trace_id=trace_id, + data_type="BOOLEAN", + comment=feedback_comment, + metadata=metadata, + ) + langfuse.flush() + + target = f"trace_id={trace_id}" if trace_id else f"session={session_name}" + logger.info(f"Langfuse: Feedback score sent ({target}, value={value})") + else: + logger.warning("Langfuse enabled but missing credentials") + except ImportError: + logger.warning("Langfuse not available - feedback will not be recorded") + except Exception as e: + logger.error(f"Failed to send feedback to Langfuse: {e}", exc_info=True) + else: + logger.info("Langfuse not enabled - feedback logged but not sent") + + return {"message": "Feedback received", "metaType": event.metaType, "recorded": langfuse_enabled} + + except Exception as e: + logger.error(f"Error processing feedback: {e}") + raise HTTPException(status_code=500, detail=str(e)) diff --git a/components/runners/claude-code-runner/endpoints/mcp_status.py b/components/runners/claude-code-runner/endpoints/mcp_status.py new file mode 100644 index 000000000..e8b9f2acf --- /dev/null +++ b/components/runners/claude-code-runner/endpoints/mcp_status.py @@ -0,0 +1,205 @@ +"""GET /mcp/status — MCP server connection diagnostics. + +Also contains the ``_check_mcp_authentication`` helper used by +``mcp.log_auth_status`` (imported from here to avoid circular deps with main). +""" + +import json +import logging +import os +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Dict + +from fastapi import APIRouter + +from endpoints import state + +logger = logging.getLogger(__name__) + +router = APIRouter() + + +# ------------------------------------------------------------------ +# MCP authentication helpers (used by mcp.py via import) +# ------------------------------------------------------------------ + + +def _read_google_credentials(workspace_path: Path, secret_path: Path) -> Dict[str, Any] | None: + cred_path = workspace_path if workspace_path.exists() else secret_path + if not cred_path.exists(): + return None + try: + if cred_path.stat().st_size == 0: + return None + with open(cred_path, "r") as f: + return json.load(f) + except (json.JSONDecodeError, OSError) as e: + logger.warning(f"Failed to read Google credentials: {e}") + return None + + +def _parse_token_expiry(expiry_str: str) -> datetime | None: + try: + expiry_str = expiry_str.replace("Z", "+00:00") + dt = datetime.fromisoformat(expiry_str) + if dt.tzinfo is None: + dt = dt.replace(tzinfo=timezone.utc) + return dt + except (ValueError, TypeError) as e: + logger.warning(f"Could not parse token expiry '{expiry_str}': {e}") + return None + + +def _validate_google_token(user_creds: Dict[str, Any], user_email: str) -> tuple[bool | None, str]: + if not user_creds.get("access_token") or not user_creds.get("refresh_token"): + return False, "Google OAuth credentials incomplete - missing or empty tokens" + + if "token_expiry" in user_creds and user_creds["token_expiry"]: + expiry = _parse_token_expiry(user_creds["token_expiry"]) + if expiry is None: + return None, f"Google OAuth authenticated as {user_email} (token expiry format invalid)" + + now = datetime.now(timezone.utc) + if expiry <= now and not user_creds.get("refresh_token"): + return False, "Google OAuth token expired - re-authenticate" + if expiry <= now: + return None, f"Google OAuth authenticated as {user_email} (token refresh needed)" + + return True, f"Google OAuth authenticated as {user_email}" + + +def check_mcp_authentication(server_name: str) -> tuple[bool | None, str | None]: + """Check if credentials are available and valid for known MCP servers.""" + if server_name == "google-workspace": + workspace_path = Path("/workspace/.google_workspace_mcp/credentials/credentials.json") + secret_path = Path("/app/.google_workspace_mcp/credentials/credentials.json") + creds = _read_google_credentials(workspace_path, secret_path) + if creds is None: + return False, "Google OAuth not configured - authenticate via Integrations page" + + try: + user_email = os.environ.get("USER_GOOGLE_EMAIL", "") + if not user_email or user_email == "user@example.com": + return False, "Google OAuth not configured - USER_GOOGLE_EMAIL not set" + + user_creds = { + "access_token": creds.get("token", ""), + "refresh_token": creds.get("refresh_token", ""), + "token_expiry": creds.get("expiry", ""), + } + return _validate_google_token(user_creds, user_email) + except KeyError as e: + return False, f"Google OAuth credentials corrupted: {str(e)}" + + if server_name in ("mcp-atlassian", "jira"): + jira_url = os.getenv("JIRA_URL", "").strip() + jira_token = os.getenv("JIRA_API_TOKEN", "").strip() + if jira_url and jira_token: + return True, "Jira credentials configured" + + try: + import urllib.request as _urllib_request + + base = os.getenv("BACKEND_API_URL", "").rstrip("/") + project = os.getenv("PROJECT_NAME") or os.getenv("AGENTIC_SESSION_NAMESPACE", "") + session_id = os.getenv("SESSION_ID", "") + + if base and project and session_id: + url = f"{base}/projects/{project.strip()}/agentic-sessions/{session_id}/credentials/jira" + req = _urllib_request.Request(url, method="GET") + bot = (os.getenv("BOT_TOKEN") or "").strip() + if bot: + req.add_header("Authorization", f"Bearer {bot}") + try: + with _urllib_request.urlopen(req, timeout=3) as resp: + data = json.loads(resp.read()) + if data.get("apiToken"): + return True, "Jira credentials available (not yet loaded in session)" + except Exception: + pass + except Exception: + pass + + return False, "Jira not configured - connect on Integrations page" + + return None, None + + +# ------------------------------------------------------------------ +# Endpoint +# ------------------------------------------------------------------ + + +@router.get("/mcp/status") +async def get_mcp_status(): + """Returns MCP server connection status via the SDK's get_mcp_status().""" + try: + if not state.context: + return {"servers": [], "totalCount": 0, "message": "Context not initialized yet"} + + from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient + import config as runner_config + + workspace_path = state.context.workspace_path or "/workspace" + active_workflow_url = os.getenv("ACTIVE_WORKFLOW_GIT_URL", "").strip() + cwd_path = workspace_path + + if active_workflow_url: + workflow_name = active_workflow_url.split("/")[-1].removesuffix(".git") + workflow_path = os.path.join(workspace_path, "workflows", workflow_name) + if os.path.exists(workflow_path): + cwd_path = workflow_path + + mcp_servers = runner_config.load_mcp_config(state.context, cwd_path) or {} + + options = ClaudeAgentOptions( + cwd=cwd_path, + permission_mode="acceptEdits", + mcp_servers=mcp_servers, + ) + + client = ClaudeSDKClient(options=options) + try: + logger.info("MCP Status: Connecting ephemeral SDK client...") + await client.connect() + + sdk_status = await client.get_mcp_status() + logger.info("MCP Status: SDK returned:\n%s", json.dumps(sdk_status, indent=2, default=str)) + + raw_servers = [] + if isinstance(sdk_status, dict): + raw_servers = sdk_status.get("mcpServers", []) + elif isinstance(sdk_status, list): + raw_servers = sdk_status + + servers_list = [] + for srv in raw_servers: + if not isinstance(srv, dict): + continue + server_info = srv.get("serverInfo") or {} + raw_tools = srv.get("tools") or [] + tools = [ + { + "name": t.get("name", ""), + "annotations": {k: v for k, v in (t.get("annotations") or {}).items()}, + } + for t in raw_tools + if isinstance(t, dict) + ] + servers_list.append({ + "name": srv.get("name", ""), + "displayName": server_info.get("name", srv.get("name", "")), + "status": srv.get("status", "unknown"), + "version": server_info.get("version", ""), + "tools": tools, + }) + + return {"servers": servers_list, "totalCount": len(servers_list)} + finally: + logger.info("MCP Status: Disconnecting ephemeral SDK client...") + await client.disconnect() + + except Exception as e: + logger.error(f"Failed to get MCP status: {e}", exc_info=True) + return {"servers": [], "totalCount": 0, "error": str(e)} diff --git a/components/runners/claude-code-runner/endpoints/repos.py b/components/runners/claude-code-runner/endpoints/repos.py new file mode 100644 index 000000000..e2e00cf66 --- /dev/null +++ b/components/runners/claude-code-runner/endpoints/repos.py @@ -0,0 +1,364 @@ +"""Repository management endpoints: /repos/add, /repos/remove, /repos/status.""" + +import asyncio +import json +import logging +import os +import re +import shutil +import tempfile +import uuid +from pathlib import Path + +import aiohttp +from fastapi import APIRouter, HTTPException, Request + +from endpoints import state + +logger = logging.getLogger(__name__) + +router = APIRouter() + + +# ------------------------------------------------------------------ +# Endpoints +# ------------------------------------------------------------------ + + +@router.post("/repos/add") +async def add_repo(request: Request): + """Clone a repo into the workspace and trigger adapter reinit.""" + if not state.context: + raise HTTPException(status_code=503, detail="Context not initialized") + + body = await request.json() + url = body.get("url", "") + branch = body.get("branch", "main") + name = body.get("name", "") + + github_token = request.headers.get("X-GitHub-Token", "").strip() or None + gitlab_token = request.headers.get("X-GitLab-Token", "").strip() or None + + if github_token: + logger.info("Using GitHub authentication from request header") + elif gitlab_token: + logger.info("Using GitLab authentication from request header") + + logger.info(f"Add repo request: url={url}, branch={branch}, name={name}") + + if not url: + raise HTTPException(status_code=400, detail="Repository URL is required") + + if not name: + name = url.split("/")[-1].removesuffix(".git") + + success, repo_path, was_newly_cloned = await clone_repo_at_runtime( + url, branch, name, github_token, gitlab_token + ) + if not success: + raise HTTPException(status_code=500, detail=f"Failed to clone repository: {url}") + + if was_newly_cloned: + repos_json = os.getenv("REPOS_JSON", "[]") + try: + repos = json.loads(repos_json) if repos_json else [] + except Exception: + repos = [] + repos.append({"name": name, "input": {"url": url, "branch": branch}}) + os.environ["REPOS_JSON"] = json.dumps(repos) + + state._platform_ready = False + state._first_run = True + state._adapter_dirty = True + logger.info(f"Repo '{name}' added and cloned, adapter will reinitialize on next run") + asyncio.create_task(_trigger_repo_added_notification(name, url)) + else: + logger.info(f"Repo '{name}' already existed, skipping notification (idempotent call)") + + return {"message": "Repository added", "name": name, "path": repo_path, "newly_cloned": was_newly_cloned} + + +@router.post("/repos/remove") +async def remove_repo(request: Request): + """Remove a repo from the workspace.""" + if not state.context: + raise HTTPException(status_code=503, detail="Context not initialized") + + body = await request.json() + repo_name = body.get("name", "") + logger.info(f"Remove repo request: {repo_name}") + + workspace_path = os.getenv("WORKSPACE_PATH", "/workspace") + repo_path = Path(workspace_path) / "repos" / repo_name + + if repo_path.exists(): + try: + shutil.rmtree(repo_path) + logger.info(f"Deleted repository directory: {repo_path}") + except Exception as e: + logger.error(f"Failed to delete repository directory {repo_path}: {e}") + raise HTTPException(status_code=500, detail=f"Failed to delete repository: {e}") + else: + logger.warning(f"Repository directory not found: {repo_path}") + + repos_json = os.getenv("REPOS_JSON", "[]") + try: + repos = json.loads(repos_json) if repos_json else [] + except Exception: + repos = [] + repos = [r for r in repos if r.get("name") != repo_name] + os.environ["REPOS_JSON"] = json.dumps(repos) + + state._platform_ready = False + state._first_run = True + state._adapter_dirty = True + logger.info("Repo removed, adapter will reinitialize on next run") + + return {"message": "Repository removed"} + + +@router.get("/repos/status") +async def get_repos_status(): + """Get current status of all repositories in the workspace.""" + workspace_path = os.getenv("WORKSPACE_PATH", "/workspace") + repos_dir = Path(workspace_path) / "repos" + + if not repos_dir.exists(): + return {"repos": []} + + repos_status = [] + for repo_path in repos_dir.iterdir(): + if not repo_path.is_dir() or not (repo_path / ".git").exists(): + continue + + try: + repo_name = repo_path.name + + process = await asyncio.create_subprocess_exec( + "git", "-C", str(repo_path), "config", "--get", "remote.origin.url", + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await process.communicate() + repo_url = stdout.decode().strip() if process.returncode == 0 else "" + repo_url = re.sub(r"https://[^:]+:[^@]+@", "https://", repo_url) + + process = await asyncio.create_subprocess_exec( + "git", "-C", str(repo_path), "rev-parse", "--abbrev-ref", "HEAD", + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await process.communicate() + current_branch = stdout.decode().strip() if process.returncode == 0 else "unknown" + + process = await asyncio.create_subprocess_exec( + "git", "-C", str(repo_path), "branch", "--format=%(refname:short)", + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await process.communicate() + branches = ( + [b.strip() for b in stdout.decode().split("\n") if b.strip()] + if process.returncode == 0 else [] + ) + + default_branch = await get_default_branch(str(repo_path)) + + repos_status.append({ + "url": repo_url, + "name": repo_name, + "branches": branches, + "currentActiveBranch": current_branch, + "defaultBranch": default_branch, + }) + except Exception as e: + logger.error(f"Error getting status for repo {repo_path}: {e}") + continue + + return {"repos": repos_status} + + +# ------------------------------------------------------------------ +# Git helpers +# ------------------------------------------------------------------ + + +async def get_default_branch(repo_path: str) -> str: + """Get the default branch with robust fallback.""" + process = await asyncio.create_subprocess_exec( + "git", "-C", str(repo_path), "symbolic-ref", "refs/remotes/origin/HEAD", + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + stdout, _ = await process.communicate() + if process.returncode == 0: + branch = stdout.decode().strip().split("/")[-1] + if branch: + return branch + + process = await asyncio.create_subprocess_exec( + "git", "-C", str(repo_path), "remote", "show", "origin", + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + stdout, _ = await process.communicate() + if process.returncode == 0: + for line in stdout.decode().split("\n"): + if "HEAD branch:" in line: + branch = line.split(":")[-1].strip() + if branch and branch != "(unknown)": + return branch + + for candidate in ["main", "master", "develop"]: + process = await asyncio.create_subprocess_exec( + "git", "-C", str(repo_path), "rev-parse", "--verify", f"origin/{candidate}", + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + await process.communicate() + if process.returncode == 0: + return candidate + + return "main" + + +async def clone_repo_at_runtime( + git_url: str, branch: str, name: str, + github_token_override: str | None = None, + gitlab_token_override: str | None = None, +) -> tuple[bool, str, bool]: + """Clone a repository at runtime or add a new branch to existing repo.""" + if not git_url: + return False, "", False + + if not name: + name = git_url.split("/")[-1].removesuffix(".git") + + if not branch or branch.strip() == "": + session_id = os.getenv("AGENTIC_SESSION_NAME", "").strip() or os.getenv("SESSION_ID", "unknown") + branch = f"ambient/{session_id}" + logger.info(f"No branch specified, auto-generated: {branch}") + + workspace_path = os.getenv("WORKSPACE_PATH", "/workspace") + repos_dir = Path(workspace_path) / "repos" + repos_dir.mkdir(parents=True, exist_ok=True) + repo_final = repos_dir / name + + github_token = github_token_override or os.getenv("GITHUB_TOKEN", "").strip() + gitlab_token = gitlab_token_override or os.getenv("GITLAB_TOKEN", "").strip() + clone_url = git_url + if github_token and "github" in git_url.lower(): + clone_url = git_url.replace("https://", f"https://x-access-token:{github_token}@") + elif gitlab_token and "gitlab" in git_url.lower(): + clone_url = git_url.replace("https://", f"https://oauth2:{gitlab_token}@") + + # Case 1: Repo already exists + if repo_final.exists(): + logger.info(f"Repo '{name}' already exists, adding branch '{branch}'") + try: + await asyncio.create_subprocess_exec( + "git", "-C", str(repo_final), "fetch", "origin", + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + + for checkout_args in ( + ["git", "-C", str(repo_final), "checkout", branch], + ["git", "-C", str(repo_final), "checkout", "-b", branch, f"origin/{branch}"], + ): + p = await asyncio.create_subprocess_exec( + *checkout_args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + await p.communicate() + if p.returncode == 0: + return True, str(repo_final), False + + default_branch = await get_default_branch(str(repo_final)) + await asyncio.create_subprocess_exec( + "git", "-C", str(repo_final), "checkout", default_branch, + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + p = await asyncio.create_subprocess_exec( + "git", "-C", str(repo_final), "checkout", "-b", branch, + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + await p.communicate() + if p.returncode == 0: + return True, str(repo_final), False + return False, "", False + except Exception as e: + logger.error(f"Error adding branch to existing repo: {e}") + return False, "", False + + # Case 2: Clone fresh + logger.info(f"Cloning repo '{name}' from {git_url}@{branch}") + temp_dir = Path(tempfile.mkdtemp(prefix="repo-clone-")) + + try: + process = await asyncio.create_subprocess_exec( + "git", "clone", clone_url, str(temp_dir), + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await process.communicate() + if process.returncode != 0: + error_msg = stderr.decode() + for tok in (github_token, gitlab_token): + if tok: + error_msg = error_msg.replace(tok, "***REDACTED***") + logger.error(f"Failed to clone repo: {error_msg}") + return False, "", False + + p = await asyncio.create_subprocess_exec( + "git", "-C", str(temp_dir), "checkout", branch, + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + await p.communicate() + if p.returncode != 0: + await asyncio.create_subprocess_exec( + "git", "-C", str(temp_dir), "checkout", "-b", branch, + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + + repo_final.parent.mkdir(parents=True, exist_ok=True) + shutil.move(str(temp_dir), str(repo_final)) + logger.info(f"Repo '{name}' ready at {repo_final} on branch '{branch}'") + return True, str(repo_final), True + + except Exception as e: + logger.error(f"Error cloning repo: {e}") + return False, "", False + finally: + if temp_dir.exists(): + shutil.rmtree(temp_dir, ignore_errors=True) + + +async def _trigger_repo_added_notification(repo_name: str, repo_url: str): + """Notify Claude that a repository has been added.""" + await asyncio.sleep(1) + + try: + backend_url = os.getenv("BACKEND_API_URL", "").rstrip("/") + project_name = os.getenv("AGENTIC_SESSION_NAMESPACE", "").strip() + session_id = state.context.session_id if state.context else "unknown" + + if not backend_url or not project_name: + return + + url = f"{backend_url}/projects/{project_name}/agentic-sessions/{session_id}/agui/run" + payload = { + "threadId": session_id, + "runId": str(uuid.uuid4()), + "messages": [{ + "id": str(uuid.uuid4()), + "role": "user", + "content": f"The repository '{repo_name}' has been added to your workspace. You can now access it at the path 'repos/{repo_name}/'. Please acknowledge this to the user.", + "metadata": {"hidden": True, "autoSent": True, "source": "repo_added"}, + }], + } + + bot_token = os.getenv("BOT_TOKEN", "").strip() + headers = {"Content-Type": "application/json"} + if bot_token: + headers["Authorization"] = f"Bearer {bot_token}" + + async with aiohttp.ClientSession() as session: + async with session.post(url, json=payload, headers=headers) as resp: + if resp.status == 200: + logger.info(f"Repo notification sent: {await resp.json()}") + else: + logger.error(f"Repo notification failed: {resp.status} - {await resp.text()}") + except Exception as e: + logger.error(f"Failed to trigger repo notification: {e}") diff --git a/components/runners/claude-code-runner/endpoints/state.py b/components/runners/claude-code-runner/endpoints/state.py new file mode 100644 index 000000000..a28a6caef --- /dev/null +++ b/components/runners/claude-code-runner/endpoints/state.py @@ -0,0 +1,23 @@ +""" +Shared mutable state for all endpoint routers. + +Centralises the globals that were previously scattered across main.py. +Endpoints import from here rather than reaching into main. +""" + +import asyncio +from typing import Any, Dict, Optional + +from context import RunnerContext + +# --- Mutable server state --- + +context: Optional[RunnerContext] = None +adapter = None # Current ClaudeAgentAdapter instance (persistent) +_obs = None # ObservabilityManager (or None) +_platform_ready = False # One-time platform setup done? +_platform_info: Dict[str, Any] = {} # cwd_path, add_dirs from setup_platform +_configured_model: str = "" # Resolved model name +_first_run = True # Controls conversation continuation +_adapter_dirty = True # True = adapter needs (re)building +_workflow_change_lock = asyncio.Lock() diff --git a/components/runners/claude-code-runner/endpoints/workflow.py b/components/runners/claude-code-runner/endpoints/workflow.py new file mode 100644 index 000000000..73c7335eb --- /dev/null +++ b/components/runners/claude-code-runner/endpoints/workflow.py @@ -0,0 +1,174 @@ +"""POST /workflow — Change active workflow at runtime.""" + +import asyncio +import logging +import os +import shutil +import tempfile +import uuid +from pathlib import Path + +import aiohttp +from fastapi import APIRouter, HTTPException, Request + +from endpoints import state + +logger = logging.getLogger(__name__) + +router = APIRouter() + + +@router.post("/workflow") +async def change_workflow(request: Request): + """Change active workflow — triggers adapter reinit and greeting.""" + if not state.context: + raise HTTPException(status_code=503, detail="Context not initialized") + + body = await request.json() + git_url = (body.get("gitUrl") or "").strip() + branch = (body.get("branch") or "main").strip() or "main" + path = (body.get("path") or "").strip() + + logger.info(f"Workflow change request: {git_url}@{branch} (path: {path})") + + async with state._workflow_change_lock: + current_git_url = os.getenv("ACTIVE_WORKFLOW_GIT_URL", "").strip() + current_branch = os.getenv("ACTIVE_WORKFLOW_BRANCH", "main").strip() or "main" + current_path = os.getenv("ACTIVE_WORKFLOW_PATH", "").strip() + + if current_git_url == git_url and current_branch == branch and current_path == path: + logger.info("Workflow unchanged; skipping reinit and greeting") + return {"message": "Workflow already active", "gitUrl": git_url, "branch": branch, "path": path} + + if git_url: + success, _wf_path = await clone_workflow_at_runtime(git_url, branch, path) + if not success: + logger.warning("Failed to clone workflow, will use default workflow directory") + + os.environ["ACTIVE_WORKFLOW_GIT_URL"] = git_url + os.environ["ACTIVE_WORKFLOW_BRANCH"] = branch + os.environ["ACTIVE_WORKFLOW_PATH"] = path + + state._platform_ready = False + state._first_run = True + state._adapter_dirty = True + + logger.info("Workflow updated, adapter will reinitialize on next run") + asyncio.create_task(_trigger_workflow_greeting(git_url, branch, path)) + + return {"message": "Workflow updated", "gitUrl": git_url, "branch": branch, "path": path} + + +# ------------------------------------------------------------------ +# Helpers +# ------------------------------------------------------------------ + + +async def clone_workflow_at_runtime(git_url: str, branch: str, subpath: str) -> tuple[bool, str]: + """Clone a workflow repository at runtime.""" + if not git_url: + return False, "" + + workflow_name = git_url.split("/")[-1].removesuffix(".git") + workspace_path = os.getenv("WORKSPACE_PATH", "/workspace") + workflow_final = Path(workspace_path) / "workflows" / workflow_name + + logger.info(f"Cloning workflow '{workflow_name}' from {git_url}@{branch}") + if subpath: + logger.info(f" Subpath: {subpath}") + + temp_dir = Path(tempfile.mkdtemp(prefix="workflow-clone-")) + + try: + github_token = os.getenv("GITHUB_TOKEN", "").strip() + gitlab_token = os.getenv("GITLAB_TOKEN", "").strip() + + clone_url = git_url + if github_token and "github" in git_url.lower(): + clone_url = git_url.replace("https://", f"https://x-access-token:{github_token}@") + elif gitlab_token and "gitlab" in git_url.lower(): + clone_url = git_url.replace("https://", f"https://oauth2:{gitlab_token}@") + + process = await asyncio.create_subprocess_exec( + "git", "clone", "--branch", branch, "--single-branch", "--depth", "1", + clone_url, str(temp_dir), + stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await process.communicate() + + if process.returncode != 0: + error_msg = stderr.decode() + for tok in (github_token, gitlab_token): + if tok: + error_msg = error_msg.replace(tok, "***REDACTED***") + logger.error(f"Failed to clone workflow: {error_msg}") + return False, "" + + if subpath: + subpath_full = temp_dir / subpath + if subpath_full.exists() and subpath_full.is_dir(): + if workflow_final.exists(): + shutil.rmtree(workflow_final) + workflow_final.parent.mkdir(parents=True, exist_ok=True) + shutil.copytree(subpath_full, workflow_final) + else: + logger.warning(f"Subpath '{subpath}' not found, using entire repo") + if workflow_final.exists(): + shutil.rmtree(workflow_final) + shutil.move(str(temp_dir), str(workflow_final)) + else: + if workflow_final.exists(): + shutil.rmtree(workflow_final) + shutil.move(str(temp_dir), str(workflow_final)) + + logger.info(f"Workflow '{workflow_name}' ready at {workflow_final}") + return True, str(workflow_final) + + except Exception as e: + logger.error(f"Error cloning workflow: {e}") + return False, "" + finally: + if temp_dir.exists(): + shutil.rmtree(temp_dir, ignore_errors=True) + + +async def _trigger_workflow_greeting(git_url: str, branch: str, path: str): + """POST a greeting prompt to the backend after a workflow change.""" + try: + backend_url = os.getenv("BACKEND_API_URL", "").rstrip("/") + project_name = os.getenv("AGENTIC_SESSION_NAMESPACE", "").strip() + session_id = state.context.session_id if state.context else "unknown" + + if not backend_url or not project_name: + logger.error("Cannot trigger workflow greeting: BACKEND_API_URL or PROJECT_NAME not set") + return + + url = f"{backend_url}/projects/{project_name}/agentic-sessions/{session_id}/agui/run" + workflow_name = git_url.split("/")[-1].removesuffix(".git") + if path: + workflow_name = path.split("/")[-1] + + payload = { + "threadId": session_id, + "runId": str(uuid.uuid4()), + "messages": [{ + "id": str(uuid.uuid4()), + "role": "user", + "content": f"Greet the user and explain that the {workflow_name} workflow is now active. Briefly describe what this workflow helps with. Keep it concise and friendly.", + "metadata": {"hidden": True, "autoSent": True, "source": "workflow_activation"}, + }], + } + + bot_token = os.getenv("BOT_TOKEN", "").strip() + headers = {"Content-Type": "application/json"} + if bot_token: + headers["Authorization"] = f"Bearer {bot_token}" + + async with aiohttp.ClientSession() as session: + async with session.post(url, json=payload, headers=headers) as resp: + if resp.status == 200: + logger.info(f"Workflow greeting started: {await resp.json()}") + else: + logger.error(f"Workflow greeting failed: {resp.status} - {await resp.text()}") + except Exception as e: + logger.error(f"Failed to trigger workflow greeting: {e}") diff --git a/components/runners/claude-code-runner/main.py b/components/runners/claude-code-runner/main.py index d609e1550..30d0c389c 100644 --- a/components/runners/claude-code-runner/main.py +++ b/components/runners/claude-code-runner/main.py @@ -4,12 +4,9 @@ """ import asyncio -import json import logging import os from contextlib import asynccontextmanager -from datetime import datetime, timezone -from pathlib import Path from typing import Any, Dict, List, Optional, Union import uvicorn @@ -20,17 +17,21 @@ from pydantic import BaseModel from context import RunnerContext +from endpoints import state logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) -# Flexible input model that matches what our frontend actually sends +# ------------------------------------------------------------------ +# Input model +# ------------------------------------------------------------------ + + class RunnerInput(BaseModel): """Input model for runner with optional AG-UI fields.""" - threadId: Optional[str] = None - thread_id: Optional[str] = None # Support both camelCase and snake_case + thread_id: Optional[str] = None runId: Optional[str] = None run_id: Optional[str] = None parentRunId: Optional[str] = None @@ -38,9 +39,7 @@ class RunnerInput(BaseModel): messages: List[Dict[str, Any]] state: Optional[Dict[str, Any]] = None tools: Optional[List[Any]] = None - context: Optional[Union[List[Any], Dict[str, Any]]] = ( - None # Accept both list and dict, convert to list - ) + context: Optional[Union[List[Any], Dict[str, Any]]] = None forwardedProps: Optional[Dict[str, Any]] = None environment: Optional[Dict[str, str]] = None metadata: Optional[Dict[str, Any]] = None @@ -49,17 +48,14 @@ def to_run_agent_input(self) -> RunAgentInput: """Convert to official RunAgentInput model.""" import uuid - # Normalize field names (prefer camelCase for AG-UI) thread_id = self.threadId or self.thread_id run_id = self.runId or self.run_id parent_run_id = self.parentRunId or self.parent_run_id - # Generate runId if not provided if not run_id: run_id = str(uuid.uuid4()) logger.info(f"Generated run_id: {run_id}") - # Context should be a list, not a dict context_list = self.context if isinstance(self.context, list) else [] return RunAgentInput( @@ -74,47 +70,30 @@ def to_run_agent_input(self) -> RunAgentInput: ) -# Global context and adapter -context: Optional[RunnerContext] = None -adapter = None # Will be ClaudeCodeAdapter after initialization +# ------------------------------------------------------------------ +# Lifespan +# ------------------------------------------------------------------ @asynccontextmanager async def lifespan(app: FastAPI): """Initialize and cleanup application resources.""" - global context, adapter - - # Import adapter here to avoid circular imports - from adapter import ClaudeCodeAdapter - - # Initialize context from environment session_id = os.getenv("SESSION_ID", "unknown") workspace_path = os.getenv("WORKSPACE_PATH", "/workspace") logger.info(f"Initializing AG-UI server for session {session_id}") - context = RunnerContext( + state.context = RunnerContext( session_id=session_id, workspace_path=workspace_path, ) - adapter = ClaudeCodeAdapter() - adapter.context = context - - logger.info("Adapter initialized - fresh client will be created for each run") - - # Check if this is a resume session via IS_RESUME env var - # This is set by the operator when restarting a stopped/completed/failed session is_resume = os.getenv("IS_RESUME", "").strip().lower() == "true" if is_resume: logger.info("IS_RESUME=true - this is a resumed session") - - # Check if session is interactive is_interactive = os.getenv("INTERACTIVE", "true").strip().lower() == "true" - - # For non-interactive sessions, auto-execute INITIAL_PROMPT on startup - # For interactive sessions, user must explicitly send the first message + initial_prompt = os.getenv("INITIAL_PROMPT", "").strip() if initial_prompt: if not is_interactive and not is_resume: @@ -122,39 +101,26 @@ async def lifespan(app: FastAPI): asyncio.create_task(auto_execute_initial_prompt(initial_prompt, session_id)) else: mode = "resumed" if is_resume else "interactive" - logger.info(f"INITIAL_PROMPT detected ({len(initial_prompt)} chars) but not auto-executing ({mode} session - user will send first message)") - + logger.info(f"INITIAL_PROMPT detected ({len(initial_prompt)} chars) but not auto-executing ({mode} session)") + logger.info(f"AG-UI server ready for session {session_id}") yield - # Cleanup + if state._obs: + await state._obs.finalize() logger.info("Shutting down AG-UI server...") async def auto_execute_initial_prompt(prompt: str, session_id: str): - """Auto-execute INITIAL_PROMPT by POSTing to backend after short delay. - - The delay gives the runner service time to register in DNS. Backend has retry - logic to handle if Service DNS isn't ready yet, so this can be short. - - Only called for fresh sessions (no hydrated state in .claude/). - """ + """Auto-execute INITIAL_PROMPT for non-interactive sessions.""" import uuid - import aiohttp - # Configurable delay (default 1s, was 3s) - # Backend has retry logic, so we don't need to wait long delay_seconds = float(os.getenv("INITIAL_PROMPT_DELAY_SECONDS", "1")) - logger.info( - f"Waiting {delay_seconds}s before auto-executing INITIAL_PROMPT (allow Service DNS to propagate)..." - ) + logger.info(f"Waiting {delay_seconds}s before auto-executing INITIAL_PROMPT...") await asyncio.sleep(delay_seconds) - logger.info("Auto-executing INITIAL_PROMPT via backend POST...") - - # Get backend URL from environment backend_url = os.getenv("BACKEND_API_URL", "").rstrip("/") project_name = ( os.getenv("PROJECT_NAME", "").strip() @@ -162,35 +128,22 @@ async def auto_execute_initial_prompt(prompt: str, session_id: str): ) if not backend_url or not project_name: - logger.error( - "Cannot auto-execute INITIAL_PROMPT: BACKEND_API_URL or PROJECT_NAME not set" - ) + logger.error("Cannot auto-execute INITIAL_PROMPT: BACKEND_API_URL or PROJECT_NAME not set") return - # BACKEND_API_URL already includes /api suffix from operator - url = ( - f"{backend_url}/projects/{project_name}/agentic-sessions/{session_id}/agui/run" - ) - logger.info(f"Auto-execution URL: {url}") + url = f"{backend_url}/projects/{project_name}/agentic-sessions/{session_id}/agui/run" payload = { "threadId": session_id, "runId": str(uuid.uuid4()), - "messages": [ - { - "id": str(uuid.uuid4()), - "role": "user", - "content": prompt, - "metadata": { - "hidden": True, - "autoSent": True, - "source": "runner_initial_prompt", - }, - } - ], + "messages": [{ + "id": str(uuid.uuid4()), + "role": "user", + "content": prompt, + "metadata": {"hidden": True, "autoSent": True, "source": "runner_initial_prompt"}, + }], } - # Get BOT_TOKEN for auth bot_token = os.getenv("BOT_TOKEN", "").strip() headers = {"Content-Type": "application/json"} if bot_token: @@ -198,1431 +151,185 @@ async def auto_execute_initial_prompt(prompt: str, session_id: str): try: async with aiohttp.ClientSession() as session: - async with session.post( - url, - json=payload, - headers=headers, - timeout=aiohttp.ClientTimeout(total=30), - ) as resp: + async with session.post(url, json=payload, headers=headers, timeout=aiohttp.ClientTimeout(total=30)) as resp: if resp.status == 200: - result = await resp.json() - logger.info(f"INITIAL_PROMPT auto-execution started: {result}") + logger.info(f"INITIAL_PROMPT auto-execution started: {await resp.json()}") else: - error_text = await resp.text() - logger.warning( - f"INITIAL_PROMPT failed with status {resp.status}: {error_text[:200]}" - ) + logger.warning(f"INITIAL_PROMPT failed with status {resp.status}: {(await resp.text())[:200]}") except Exception as e: logger.warning(f"INITIAL_PROMPT auto-execution error (backend will retry): {e}") -app = FastAPI(title="Claude Code AG-UI Server", version="0.2.0", lifespan=lifespan) - - -# Track if adapter has been initialized -_adapter_initialized = False -# Prevent duplicate workflow updates/greetings from concurrent calls -_workflow_change_lock = asyncio.Lock() - - -@app.post("/") -async def run_agent(input_data: RunnerInput, request: Request): - """ - AG-UI compatible run endpoint. - - Accepts flexible input with thread_id, run_id, messages. - Optional fields: state, tools, context, forwardedProps. - Returns SSE stream of AG-UI events. - """ - global _adapter_initialized - - if not adapter: - raise HTTPException(status_code=503, detail="Adapter not initialized") - - # Convert to official RunAgentInput - run_agent_input = input_data.to_run_agent_input() - - # Get Accept header for encoder - accept_header = request.headers.get("accept", "text/event-stream") - encoder = EventEncoder(accept=accept_header) - - logger.info( - f"Processing run: thread_id={run_agent_input.thread_id}, run_id={run_agent_input.run_id}" - ) - - async def event_generator(): - """Generate AG-UI events from adapter.""" - global _adapter_initialized - - try: - logger.info("Event generator started") - - # Initialize adapter on first run - if not _adapter_initialized: - logger.info( - "First run - initializing adapter with workspace preparation" - ) - await adapter.initialize(context) - logger.info("Adapter initialization complete") - _adapter_initialized = True - - logger.info("Starting adapter.process_run()...") - - # Process the run (creates fresh client each time) - async for event in adapter.process_run(run_agent_input): - logger.debug(f"Yielding run event: {event.type}") - yield encoder.encode(event) - logger.info("adapter.process_run() completed") - except Exception as e: - logger.error(f"Error in event generator: {e}") - # Yield error event - from ag_ui.core import EventType, RunErrorEvent - - error_event = RunErrorEvent( - type=EventType.RUN_ERROR, - thread_id=run_agent_input.thread_id or context.session_id, - run_id=run_agent_input.run_id or "unknown", - message=str(e), - ) - yield encoder.encode(error_event) +# ------------------------------------------------------------------ +# App + routers +# ------------------------------------------------------------------ - return StreamingResponse( - event_generator(), - media_type=encoder.get_content_type(), - headers={ - "Cache-Control": "no-cache", - "X-Accel-Buffering": "no", - }, - ) +app = FastAPI(title="Claude Code AG-UI Server", version="0.2.0", lifespan=lifespan) -@app.post("/interrupt") -async def interrupt_run(): - """ - Interrupt the current Claude SDK execution. - - Sends interrupt signal to Claude subprocess to stop mid-execution. - See: https://platform.claude.com/docs/en/agent-sdk/python#methods - """ - if not adapter: - raise HTTPException(status_code=503, detail="Adapter not initialized") - - logger.info("Interrupt request received") - - try: - # Call adapter's interrupt method which signals the active Claude SDK client - await adapter.interrupt() - - return {"message": "Interrupt signal sent to Claude SDK"} - except Exception as e: - logger.error(f"Interrupt failed: {e}") - raise HTTPException(status_code=500, detail=str(e)) - +# Include endpoint routers +from endpoints.capabilities import router as capabilities_router +from endpoints.feedback import router as feedback_router +from endpoints.mcp_status import router as mcp_status_router +from endpoints.repos import router as repos_router +from endpoints.workflow import router as workflow_router -class FeedbackEvent(BaseModel): - """AG-UI META event for user feedback (thumbs up/down).""" +app.include_router(capabilities_router) +app.include_router(feedback_router) +app.include_router(mcp_status_router) +app.include_router(repos_router) +app.include_router(workflow_router) - type: str # "META" - metaType: str # "thumbs_up" or "thumbs_down" - payload: Dict[str, Any] - threadId: Optional[str] = None - ts: Optional[int] = None +# ------------------------------------------------------------------ +# Platform setup helper +# ------------------------------------------------------------------ -@app.post("/feedback") -async def handle_feedback(event: FeedbackEvent): - """ - Handle user feedback META events and send to Langfuse. - This endpoint receives thumbs up/down feedback from the frontend (via backend) - and logs it to Langfuse for observability tracking. - - See: https://docs.ag-ui.com/drafts/meta-events#user-feedback - """ - logger.info( - f"Feedback received: {event.metaType} from {event.payload.get('userId', 'unknown')}" - ) +async def _ensure_platform_ready(): + """Run one-time platform setup (auth, workspace, prerequisites).""" + if state._platform_ready: + return - if event.type != "META": - raise HTTPException(status_code=400, detail="Expected META event type") + import adapter as adapter_mod + import workspace - if event.metaType not in ("thumbs_up", "thumbs_down"): - raise HTTPException( - status_code=400, detail="metaType must be 'thumbs_up' or 'thumbs_down'" - ) + await workspace.validate_prerequisites(state.context) + state._configured_model, state._platform_info = await adapter_mod.setup_platform(state.context) try: - # Extract payload fields - payload = event.payload - user_id = payload.get("userId", "unknown") - project_name = payload.get("projectName", "") - session_name = payload.get("sessionName", "") - message_id = payload.get("messageId", "") - trace_id = payload.get( - "traceId", "" - ) # Langfuse trace ID for specific turn association - comment = payload.get("comment", "") - reason = payload.get("reason", "") - workflow = payload.get("workflow", "") - context_str = payload.get("context", "") - include_transcript = payload.get("includeTranscript", False) - transcript = payload.get("transcript", []) - - # Map metaType to boolean value (True = positive, False = negative) - value = True if event.metaType == "thumbs_up" else False - - # Build comment string with context - comment_parts = [] - if comment: - comment_parts.append(comment) - if reason: - comment_parts.append(f"Reason: {reason}") - if context_str: - comment_parts.append(f"\nMessage:\n{context_str}") - if include_transcript and transcript: - transcript_text = "\n".join( - f"[{m.get('role', 'unknown')}]: {m.get('content', '')}" - for m in transcript - ) - comment_parts.append(f"\nFull Transcript:\n{transcript_text}") + from observability import ObservabilityManager + import auth - feedback_comment = "\n".join(comment_parts) if comment_parts else None + raw_user_id = os.getenv("USER_ID", "").strip() + raw_user_name = os.getenv("USER_NAME", "").strip() + user_id, user_name = auth.sanitize_user_context(raw_user_id, raw_user_name) - # Send to Langfuse if configured - langfuse_enabled = os.getenv("LANGFUSE_ENABLED", "").strip().lower() in ( - "1", - "true", - "yes", + obs = ObservabilityManager( + session_id=state.context.session_id, + user_id=user_id, + user_name=user_name, ) - - if langfuse_enabled: - try: - from langfuse import Langfuse - - public_key = os.getenv("LANGFUSE_PUBLIC_KEY", "").strip() - secret_key = os.getenv("LANGFUSE_SECRET_KEY", "").strip() - host = os.getenv("LANGFUSE_HOST", "").strip() - - if public_key and secret_key and host: - langfuse = Langfuse( - public_key=public_key, - secret_key=secret_key, - host=host, - ) - - # Build metadata for structured filtering in Langfuse UI - metadata = { - "project": project_name, - "session": session_name, - "user": user_id, - "feedbackType": event.metaType, - } - if workflow: - metadata["workflow"] = workflow - if message_id: - metadata["messageId"] = message_id - - # Create score directly using create_score() API - # Prefer trace_id (specific turn) over session_id (whole session) - # Langfuse expects trace_id OR session_id, not both - langfuse.create_score( - name="user-feedback", - value=value, - trace_id=trace_id, - data_type="BOOLEAN", - comment=feedback_comment, - metadata=metadata, - ) - - # Flush immediately to ensure feedback is sent - langfuse.flush() - - # Log success after flush completes - if trace_id: - logger.info( - f"Langfuse: Feedback score sent successfully (trace_id={trace_id}, value={value})" - ) - else: - logger.info( - f"Langfuse: Feedback score sent successfully (session={session_name}, value={value})" - ) - else: - logger.warning("Langfuse enabled but missing credentials") - except ImportError: - logger.warning("Langfuse not available - feedback will not be recorded") - except Exception as e: - logger.error(f"Failed to send feedback to Langfuse: {e}", exc_info=True) - else: - logger.info( - "Langfuse not enabled - feedback logged but not sent to Langfuse" - ) - - return { - "message": "Feedback received", - "metaType": event.metaType, - "recorded": langfuse_enabled, - } - - except Exception as e: - logger.error(f"Error processing feedback: {e}") - raise HTTPException(status_code=500, detail=str(e)) - - -def _read_google_credentials(workspace_path: Path, secret_path: Path) -> Dict[str, Any] | None: - """ - Read Google credentials from workspace or secret mount location. - - Args: - workspace_path: Path to writable workspace credentials - secret_path: Path to read-only secret mount credentials - - Returns: - Credentials dict if found and parseable, None otherwise - """ - import json as _json - - cred_path = workspace_path if workspace_path.exists() else secret_path - - if not cred_path.exists(): - return None - - try: - # Check file has content - if cred_path.stat().st_size == 0: - return None - - # Load and validate credentials structure - with open(cred_path, 'r') as f: - return _json.load(f) - - except (_json.JSONDecodeError, OSError) as e: - logger.warning(f"Failed to read Google credentials: {e}") - return None - - -def _parse_token_expiry(expiry_str: str) -> datetime | None: - """ - Parse token expiry timestamp string to datetime. - - Args: - expiry_str: ISO 8601 timestamp string (may include Z suffix or be timezone-naive) - - Returns: - Parsed timezone-aware datetime object or None if parsing fails - """ - try: - # Handle Z suffix - expiry_str = expiry_str.replace('Z', '+00:00') - dt = datetime.fromisoformat(expiry_str) - # If timezone-naive, assume UTC - if dt.tzinfo is None: - dt = dt.replace(tzinfo=timezone.utc) - return dt - except (ValueError, TypeError) as e: - logger.warning(f"Could not parse token expiry '{expiry_str}': {e}") - return None - - -def _validate_google_token(user_creds: Dict[str, Any], user_email: str) -> tuple[bool | None, str]: - """ - Validate Google OAuth token structure and expiry. - - Args: - user_creds: Credential dict for a specific user - user_email: Email address of the user - - Returns: - Tuple of (is_authenticated, auth_message) - - True: Valid and unexpired token - - False: Invalid or expired without refresh token - - None: Needs refresh or uncertain state - """ - from datetime import datetime, timezone - - # Check for required fields and that they're non-empty - if not user_creds.get("access_token") or not user_creds.get("refresh_token"): - return False, "Google OAuth credentials incomplete - missing or empty tokens" - - # Check token expiry if available - if "token_expiry" in user_creds and user_creds["token_expiry"]: - expiry_str = user_creds["token_expiry"] - expiry = _parse_token_expiry(expiry_str) - - if expiry is None: - # Can't parse expiry - treat as uncertain rather than valid - return None, f"Google OAuth authenticated as {user_email} (token expiry format invalid)" - - now = datetime.now(timezone.utc) - - # If expired and no refresh token, authentication failed - if expiry <= now and not user_creds.get("refresh_token"): - return False, "Google OAuth token expired - re-authenticate" - - # If expired but have refresh token, mark as needs refresh - if expiry <= now: - return None, f"Google OAuth authenticated as {user_email} (token refresh needed)" - - # Valid credentials found - return True, f"Google OAuth authenticated as {user_email}" - - -def _check_mcp_authentication(server_name: str) -> tuple[bool | None, str | None]: - """ - Check if credentials are available AND VALID for known MCP servers. - - Args: - server_name: Name of the MCP server to check (e.g., 'google-workspace', 'jira') - - Returns: - Tuple of (is_authenticated, auth_message) where: - - (True, message): Valid authentication with user email in message - - (False, error): No authentication or invalid (error describes reason) - - (None, message): Authentication uncertain/needs refresh - - (None, None): Server type not recognized for auth checking - """ - from pathlib import Path - - # Google Workspace MCP - we know how to check this - if server_name == "google-workspace": - # Check workspace location first (writable copy), then mounted secret - workspace_path = Path("/workspace/.google_workspace_mcp/credentials/credentials.json") - secret_path = Path("/app/.google_workspace_mcp/credentials/credentials.json") - - creds = _read_google_credentials(workspace_path, secret_path) - - if creds is None: - return False, "Google OAuth not configured - authenticate via Integrations page" - - try: - # workspace-mcp credentials format (flat structure): - # { - # "token": "access_token_value", - # "refresh_token": "...", - # "token_uri": "https://oauth2.googleapis.com/token", - # "client_id": "...", - # "client_secret": "...", - # "scopes": [...], - # "expiry": "2026-01-23T12:00:00" - # } - - # Get user email from environment (set by operator) - user_email = os.environ.get("USER_GOOGLE_EMAIL", "") - if not user_email or user_email == "user@example.com": - return False, "Google OAuth not configured - USER_GOOGLE_EMAIL not set" - - # Map new flat format to expected field names - user_creds = { - "access_token": creds.get("token", ""), - "refresh_token": creds.get("refresh_token", ""), - "token_expiry": creds.get("expiry", ""), - } - - return _validate_google_token(user_creds, user_email) - - except KeyError as e: - return False, f"Google OAuth credentials corrupted: {str(e)}" - - # Jira/Atlassian MCP - check both local env and backend availability - if server_name in ("mcp-atlassian", "jira"): - jira_url = os.getenv("JIRA_URL", "").strip() - jira_token = os.getenv("JIRA_API_TOKEN", "").strip() - - if jira_url and jira_token: - return True, "Jira credentials configured" - - # Check if credentials available in backend (before first run) - try: - import urllib.request as _urllib_request - import json as _json - - base = os.getenv("BACKEND_API_URL", "").rstrip("/") - project = os.getenv("PROJECT_NAME") or os.getenv("AGENTIC_SESSION_NAMESPACE", "") - session_id = os.getenv("SESSION_ID", "") - - if base and project and session_id: - url = f"{base}/projects/{project.strip()}/agentic-sessions/{session_id}/credentials/jira" - req = _urllib_request.Request(url, method="GET") - bot = (os.getenv("BOT_TOKEN") or "").strip() - if bot: - req.add_header("Authorization", f"Bearer {bot}") - - try: - with _urllib_request.urlopen(req, timeout=3) as resp: - data = _json.loads(resp.read()) - if data.get("apiToken"): - return True, "Jira credentials available (not yet loaded in session)" - except: - pass - except: - pass - - return False, "Jira not configured - connect on Integrations page" - - # For all other servers (webfetch, unknown) - don't claim to know auth status - return None, None - - -@app.get("/mcp/status") -async def get_mcp_status(): - """ - Returns MCP server connection status by using the SDK's get_mcp_status() method. - Spins up a minimal ClaudeSDKClient, queries MCP status, then tears it down. - """ - try: - global adapter - - if not adapter or not adapter.context: - return { - "servers": [], - "totalCount": 0, - "message": "Adapter not initialized yet", - } - - from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient - import config as runner_config - - # Resolve working directory (same logic as adapter) - workspace_path = adapter.context.workspace_path or "/workspace" - active_workflow_url = os.getenv("ACTIVE_WORKFLOW_GIT_URL", "").strip() - cwd_path = workspace_path - - if active_workflow_url: - workflow_name = active_workflow_url.split("/")[-1].removesuffix(".git") - workflow_path = os.path.join(workspace_path, "workflows", workflow_name) - if os.path.exists(workflow_path): - cwd_path = workflow_path - - # Load MCP server config (same config the adapter uses for runs) - mcp_servers = runner_config.load_mcp_config(adapter.context, cwd_path) or {} - - # Build minimal options — just enough to initialise MCP servers - options = ClaudeAgentOptions( - cwd=cwd_path, - permission_mode="acceptEdits", - mcp_servers=mcp_servers, + await obs.initialize( + prompt="(pending)", + namespace=state.context.get_env("AGENTIC_SESSION_NAMESPACE", "unknown"), + model=state._configured_model, ) - - client = ClaudeSDKClient(options=options) - try: - logger.info("MCP Status: Connecting ephemeral SDK client...") - await client.connect() - - # Use the SDK's public get_mcp_status() method (added in v0.1.23) - sdk_status = await client.get_mcp_status() - logger.info("MCP Status: SDK returned:\n%s", json.dumps(sdk_status, indent=2, default=str)) - - # SDK returns: { mcpServers: [{ name, status, serverInfo: { name, version }, scope, tools }] } - raw_servers = [] - if isinstance(sdk_status, dict): - raw_servers = sdk_status.get("mcpServers", []) - elif isinstance(sdk_status, list): - raw_servers = sdk_status - - servers_list = [] - for srv in raw_servers: - if not isinstance(srv, dict): - continue - server_info = srv.get("serverInfo") or {} - raw_tools = srv.get("tools") or [] - tools = [ - { - "name": t.get("name", ""), - "annotations": { - k: v for k, v in (t.get("annotations") or {}).items() - }, - } - for t in raw_tools - if isinstance(t, dict) - ] - servers_list.append({ - "name": srv.get("name", ""), - "displayName": server_info.get("name", srv.get("name", "")), - "status": srv.get("status", "unknown"), - "version": server_info.get("version", ""), - "tools": tools, - }) - - return { - "servers": servers_list, - "totalCount": len(servers_list), - } - finally: - logger.info("MCP Status: Disconnecting ephemeral SDK client...") - await client.disconnect() - + state._obs = obs except Exception as e: - logger.error(f"Failed to get MCP status: {e}", exc_info=True) - return {"servers": [], "totalCount": 0, "error": str(e)} + logger.warning(f"Failed to initialize observability: {e}") + state._platform_ready = True + logger.info("Platform setup complete") -async def clone_workflow_at_runtime( - git_url: str, branch: str, subpath: str -) -> tuple[bool, str]: - """ - Clone a workflow repository at runtime. - This mirrors the logic in hydrate.sh but runs when workflows are changed - after the pod has started. +def _ensure_adapter(): + """Build or rebuild the adapter if the config has changed.""" + if state.adapter is not None and not state._adapter_dirty: + return - Returns: - (success, workflow_dir_path) tuple - """ - import shutil - import tempfile - from pathlib import Path + import adapter as adapter_mod - if not git_url: - return False, "" + state.adapter = adapter_mod.build_adapter( + state.context, + configured_model=state._configured_model, + cwd_path=state._platform_info["cwd_path"], + add_dirs=state._platform_info["add_dirs"], + first_run=state._first_run, + obs=state._obs, + ) + state._adapter_dirty = False + logger.info("Adapter built (persistent, will be reused across runs)") - # Derive workflow name from URL - workflow_name = git_url.split("/")[-1].removesuffix(".git") - workspace_path = os.getenv("WORKSPACE_PATH", "/workspace") - workflow_final = Path(workspace_path) / "workflows" / workflow_name - logger.info(f"Cloning workflow '{workflow_name}' from {git_url}@{branch}") - if subpath: - logger.info(f" Subpath: {subpath}") +# ------------------------------------------------------------------ +# Core endpoints +# ------------------------------------------------------------------ - # Create temp directory for clone - temp_dir = Path(tempfile.mkdtemp(prefix="workflow-clone-")) - try: - # Build git clone command with optional auth token - github_token = os.getenv("GITHUB_TOKEN", "").strip() - gitlab_token = os.getenv("GITLAB_TOKEN", "").strip() - - # Determine which token to use based on URL - clone_url = git_url - if github_token and "github" in git_url.lower(): - clone_url = git_url.replace( - "https://", f"https://x-access-token:{github_token}@" - ) - logger.info("Using GITHUB_TOKEN for workflow authentication") - elif gitlab_token and "gitlab" in git_url.lower(): - clone_url = git_url.replace("https://", f"https://oauth2:{gitlab_token}@") - logger.info("Using GITLAB_TOKEN for workflow authentication") - - # Clone the repository - process = await asyncio.create_subprocess_exec( - "git", - "clone", - "--branch", - branch, - "--single-branch", - "--depth", - "1", - clone_url, - str(temp_dir), - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() - - if process.returncode != 0: - # Redact tokens from error message - error_msg = stderr.decode() - if github_token: - error_msg = error_msg.replace(github_token, "***REDACTED***") - if gitlab_token: - error_msg = error_msg.replace(gitlab_token, "***REDACTED***") - logger.error(f"Failed to clone workflow: {error_msg}") - return False, "" - - logger.info("Clone successful, processing...") - - # Handle subpath extraction - if subpath: - subpath_full = temp_dir / subpath - if subpath_full.exists() and subpath_full.is_dir(): - logger.info(f"Extracting subpath: {subpath}") - # Remove existing workflow dir if exists - if workflow_final.exists(): - shutil.rmtree(workflow_final) - # Create parent dirs and copy subpath - workflow_final.parent.mkdir(parents=True, exist_ok=True) - shutil.copytree(subpath_full, workflow_final) - logger.info(f"Workflow extracted to {workflow_final}") - else: - logger.warning(f"Subpath '{subpath}' not found, using entire repo") - if workflow_final.exists(): - shutil.rmtree(workflow_final) - shutil.move(str(temp_dir), str(workflow_final)) - else: - # No subpath - use entire repo - if workflow_final.exists(): - shutil.rmtree(workflow_final) - shutil.move(str(temp_dir), str(workflow_final)) +@app.post("/") +async def run_agent(input_data: RunnerInput, request: Request): + """AG-UI run endpoint — standard pattern: adapter.run() → stream events.""" + if not state.context: + raise HTTPException(status_code=503, detail="Context not initialized") - logger.info(f"Workflow '{workflow_name}' ready at {workflow_final}") - return True, str(workflow_final) + run_agent_input = input_data.to_run_agent_input() + accept_header = request.headers.get("accept", "text/event-stream") + encoder = EventEncoder(accept=accept_header) - except Exception as e: - logger.error(f"Error cloning workflow: {e}") - return False, "" - finally: - # Cleanup temp directory if it still exists - if temp_dir.exists(): - shutil.rmtree(temp_dir, ignore_errors=True) - - -@app.post("/workflow") -async def change_workflow(request: Request): - """ - Change active workflow - triggers Claude SDK client restart and new greeting. - - Accepts: {"gitUrl": "...", "branch": "...", "path": "..."} - """ - global _adapter_initialized - - if not adapter: - raise HTTPException(status_code=503, detail="Adapter not initialized") - - body = await request.json() - git_url = (body.get("gitUrl") or "").strip() - branch = (body.get("branch") or "main").strip() or "main" - path = (body.get("path") or "").strip() - - logger.info(f"Workflow change request: {git_url}@{branch} (path: {path})") - - async with _workflow_change_lock: - current_git_url = os.getenv("ACTIVE_WORKFLOW_GIT_URL", "").strip() - current_branch = os.getenv("ACTIVE_WORKFLOW_BRANCH", "main").strip() or "main" - current_path = os.getenv("ACTIVE_WORKFLOW_PATH", "").strip() - - if ( - current_git_url == git_url - and current_branch == branch - and current_path == path - ): - logger.info("Workflow unchanged; skipping reinit and greeting") - return { - "message": "Workflow already active", - "gitUrl": git_url, - "branch": branch, - "path": path, - } - - # Clone the workflow repository at runtime - # This is needed because the init container only runs once at pod startup - if git_url: - success, workflow_path = await clone_workflow_at_runtime( - git_url, branch, path - ) - if not success: - logger.warning( - "Failed to clone workflow, will use default workflow directory" - ) - - # Update environment variables - os.environ["ACTIVE_WORKFLOW_GIT_URL"] = git_url - os.environ["ACTIVE_WORKFLOW_BRANCH"] = branch - os.environ["ACTIVE_WORKFLOW_PATH"] = path - - # Reset adapter state to force reinitialization on next run - _adapter_initialized = False - adapter._first_run = True - - logger.info("Workflow updated, adapter will reinitialize on next run") - - # Trigger a new run to greet user with workflow context - # This runs in background via backend POST - asyncio.create_task(trigger_workflow_greeting(git_url, branch, path)) - - return { - "message": "Workflow updated", - "gitUrl": git_url, - "branch": branch, - "path": path, - } - - -async def get_default_branch(repo_path: str) -> str: - """ - Get the default branch of a repository with robust fallback. - - Tries multiple methods in order: - 1. symbolic-ref on origin/HEAD - 2. git remote show origin (more reliable but slower) - 3. Fallback to common defaults: main, master, develop - - Args: - repo_path: Path to the git repository - - Returns: - The default branch name - """ - # Method 1: symbolic-ref (fast but may not be set) - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(repo_path), - "symbolic-ref", - "refs/remotes/origin/HEAD", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() - if process.returncode == 0: - # Output is like "refs/remotes/origin/main" - default_branch = stdout.decode().strip().split("/")[-1] - if default_branch: - logger.info(f"Default branch from symbolic-ref: {default_branch}") - return default_branch - - # Method 2: remote show origin (more reliable) - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(repo_path), - "remote", - "show", - "origin", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() - if process.returncode == 0: - # Look for line like " HEAD branch: main" - for line in stdout.decode().split("\n"): - if "HEAD branch:" in line: - default_branch = line.split(":")[-1].strip() - if default_branch and default_branch != "(unknown)": - logger.info(f"Default branch from remote show: {default_branch}") - return default_branch - - # Method 3: Try common default branch names - for candidate in ["main", "master", "develop"]: - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(repo_path), - "rev-parse", - "--verify", - f"origin/{candidate}", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - await process.communicate() - if process.returncode == 0: - logger.info(f"Default branch found by trying common names: {candidate}") - return candidate - - # Final fallback - logger.warning("Could not determine default branch, falling back to 'main'") - return "main" - - -async def clone_repo_at_runtime( - git_url: str, - branch: str, - name: str, - github_token_override: str | None = None, - gitlab_token_override: str | None = None, -) -> tuple[bool, str, bool]: - """ - Clone a repository at runtime or add a new branch to existing repo. - - Behavior: - - If repo doesn't exist: clone it (no --single-branch to support multi-branch) - - If repo exists: fetch and checkout the new branch (idempotent) - - If branch is empty/None: auto-generate unique ambient/ branch - - If branch doesn't exist remotely: create it from default branch - - - Args: - git_url: Git repository URL - branch: Branch to checkout (or empty/None to auto-generate) - name: Name for the cloned directory (derived from URL if empty) - github_token_override: Optional GitHub token from request header (takes precedence over env var) - gitlab_token_override: Optional GitLab token from request header (takes precedence over env var) - - Returns: - (success, repo_dir_path, was_newly_cloned) tuple - - success: True if repo is available (either newly cloned or already existed) - - repo_dir_path: Path to the repo directory - - was_newly_cloned: True only if the repo was actually cloned this time - """ - import shutil - import tempfile - from pathlib import Path - - if not git_url: - return False, "", False - - # Derive repo name from URL if not provided - if not name: - name = git_url.split("/")[-1].removesuffix(".git") - - # Generate unique branch name if not specified (only if user didn't provide one) - # IMPORTANT: Keep in sync with backend (sessions.go) and frontend (add-context-modal.tsx) - if not branch or branch.strip() == "": - session_id = os.getenv("AGENTIC_SESSION_NAME", "").strip() or os.getenv( - "SESSION_ID", "unknown" - ) - branch = f"ambient/{session_id}" - logger.info(f"No branch specified, auto-generated: {branch}") + logger.info(f"Processing run: thread_id={run_agent_input.thread_id}, run_id={run_agent_input.run_id}") - # Repos are stored in /workspace/repos/{name} (matching hydrate.sh) - workspace_path = os.getenv("WORKSPACE_PATH", "/workspace") - repos_dir = Path(workspace_path) / "repos" - repos_dir.mkdir(parents=True, exist_ok=True) - repo_final = repos_dir / name - - # Build clone URL with auth token (header tokens take precedence over env vars) - github_token = github_token_override or os.getenv("GITHUB_TOKEN", "").strip() - gitlab_token = gitlab_token_override or os.getenv("GITLAB_TOKEN", "").strip() - # SECURITY: clone_url contains embedded token - never log it - clone_url = git_url - if github_token and "github" in git_url.lower(): - clone_url = git_url.replace( - "https://", f"https://x-access-token:{github_token}@" - ) - logger.info("Using GitHub token for authentication") - elif gitlab_token and "gitlab" in git_url.lower(): - clone_url = git_url.replace("https://", f"https://oauth2:{gitlab_token}@") - logger.info("Using GitLab token for authentication") - - # Case 1: Repo already exists - add new branch - if repo_final.exists(): - logger.info( - f"Repo '{name}' already exists at {repo_final}, adding branch '{branch}'" - ) + async def event_stream(): try: - # Fetch latest refs - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(repo_final), - "fetch", - "origin", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - await process.communicate() - - # Try to checkout the branch - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(repo_final), - "checkout", - branch, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() - - if process.returncode == 0: - logger.info(f"Checked out existing branch '{branch}'") - return True, str(repo_final), False # Already existed, not newly cloned - - # Branch doesn't exist locally, try to checkout from remote - logger.info(f"Branch '{branch}' not found locally, trying origin/{branch}") - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(repo_final), - "checkout", - "-b", - branch, - f"origin/{branch}", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() - - if process.returncode == 0: - logger.info(f"Checked out branch '{branch}' from origin") - return True, str(repo_final), False # Already existed, not newly cloned - - # Branch doesn't exist remotely, create from default branch - logger.info( - f"Branch '{branch}' not found on remote, creating from default branch" + from middleware import tracing_middleware, emit_developer_message + + # --- One-time platform setup --- + if not state._platform_ready: + await _ensure_platform_ready() + + # Emit developer events for setup progress + async for evt in emit_developer_message( + f"Platform ready — model: {state._configured_model}, " + f"cwd: {state._platform_info.get('cwd_path', 'N/A')}" + ): + yield encoder.encode(evt) + + # --- Build or reuse adapter --- + _ensure_adapter() + + # Extract user prompt for observability + user_msg = "" + for msg in reversed(run_agent_input.messages or []): + role = getattr(msg, "role", msg.get("role") if isinstance(msg, dict) else "") + if role == "user": + content = getattr(msg, "content", msg.get("content", "") if isinstance(msg, dict) else "") + if isinstance(content, str): + user_msg = content + break + + # Wrap adapter stream with tracing middleware + wrapped_stream = tracing_middleware( + state.adapter.run(run_agent_input), + obs=state._obs, + model=state._configured_model, + prompt=user_msg, ) - # Get default branch using robust detection - default_branch = await get_default_branch(str(repo_final)) - - # Checkout default branch first - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(repo_final), - "checkout", - default_branch, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - await process.communicate() - - # Create new branch from default - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(repo_final), - "checkout", - "-b", - branch, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() + async for event in wrapped_stream: + yield encoder.encode(event) - if process.returncode == 0: - logger.info(f"Created new branch '{branch}' from '{default_branch}'") - return True, str(repo_final), False # Already existed, not newly cloned - else: - logger.error(f"Failed to create branch: {stderr.decode()}") - return False, "", False + state._first_run = False except Exception as e: - logger.error(f"Error adding branch to existing repo: {e}") - return False, "", False - - # Case 2: Repo doesn't exist - clone it - logger.info(f"Cloning repo '{name}' from {git_url}@{branch}") - - # Create temp directory for clone - temp_dir = Path(tempfile.mkdtemp(prefix="repo-clone-")) - - try: - # Clone without --single-branch to support multi-branch workflows - # No --depth=1 to allow full branch operations - process = await asyncio.create_subprocess_exec( - "git", - "clone", - clone_url, - str(temp_dir), - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() - - if process.returncode != 0: - error_msg = stderr.decode() - if github_token: - error_msg = error_msg.replace(github_token, "***REDACTED***") - if gitlab_token: - error_msg = error_msg.replace(gitlab_token, "***REDACTED***") - logger.error(f"Failed to clone repo: {error_msg}") - return False, "", False - - logger.info("Clone successful, checking out requested branch...") - - # Try to checkout requested/auto-generated branch - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(temp_dir), - "checkout", - branch, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() - - if process.returncode != 0: - # Branch doesn't exist, create it from default branch - logger.info(f"Branch '{branch}' not found, creating from default branch") - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(temp_dir), - "checkout", - "-b", - branch, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - await process.communicate() - - # Move to final location - logger.info("Moving to final location...") - repo_final.parent.mkdir(parents=True, exist_ok=True) - shutil.move(str(temp_dir), str(repo_final)) - - logger.info(f"Repo '{name}' ready at {repo_final} on branch '{branch}'") - return True, str(repo_final), True # Newly cloned - - except Exception as e: - logger.error(f"Error cloning repo: {e}") - return False, "", False - finally: - if temp_dir.exists(): - shutil.rmtree(temp_dir, ignore_errors=True) - - -async def trigger_workflow_greeting(git_url: str, branch: str, path: str): - """Trigger workflow greeting after workflow change.""" - import uuid - - import aiohttp - - logger.info("Triggering workflow greeting...") - - try: - backend_url = os.getenv("BACKEND_API_URL", "").rstrip("/") - project_name = os.getenv("AGENTIC_SESSION_NAMESPACE", "").strip() - session_id = context.session_id if context else "unknown" - - if not backend_url or not project_name: - logger.error( - "Cannot trigger workflow greeting: BACKEND_API_URL or PROJECT_NAME not set" - ) - return - - url = f"{backend_url}/projects/{project_name}/agentic-sessions/{session_id}/agui/run" - - # Extract workflow name for greeting - workflow_name = git_url.split("/")[-1].removesuffix(".git") - if path: - workflow_name = path.split("/")[-1] - - greeting = f"Greet the user and explain that the {workflow_name} workflow is now active. Briefly describe what this workflow helps with. Keep it concise and friendly." - - payload = { - "threadId": session_id, - "runId": str(uuid.uuid4()), - "messages": [ - { - "id": str(uuid.uuid4()), - "role": "user", - "content": greeting, - "metadata": { - "hidden": True, - "autoSent": True, - "source": "workflow_activation", - }, - } - ], - } - - bot_token = os.getenv("BOT_TOKEN", "").strip() - headers = {"Content-Type": "application/json"} - if bot_token: - headers["Authorization"] = f"Bearer {bot_token}" - - async with aiohttp.ClientSession() as session: - async with session.post(url, json=payload, headers=headers) as resp: - if resp.status == 200: - result = await resp.json() - logger.info(f"Workflow greeting started: {result}") - else: - error_text = await resp.text() - logger.error( - f"Workflow greeting failed: {resp.status} - {error_text}" - ) - - except Exception as e: - logger.error(f"Failed to trigger workflow greeting: {e}") - - -@app.post("/repos/add") -async def add_repo(request: Request): - """ - Add repository - clones repo and triggers Claude SDK client restart. - - Accepts: {"url": "...", "branch": "...", "name": "..."} - Headers: X-GitHub-Token, X-GitLab-Token (optional, override env vars) - """ - global _adapter_initialized - - if not adapter: - raise HTTPException(status_code=503, detail="Adapter not initialized") - - body = await request.json() - url = body.get("url", "") - branch = body.get("branch", "main") - name = body.get("name", "") - - # Read tokens from headers (passed by backend for authenticated clones) - github_token = request.headers.get("X-GitHub-Token", "").strip() or None - gitlab_token = request.headers.get("X-GitLab-Token", "").strip() or None - - # Log authentication source for debugging (without revealing token values) - if github_token: - logger.info("Using GitHub authentication from request header") - elif gitlab_token: - logger.info("Using GitLab authentication from request header") - - logger.info(f"Add repo request: url={url}, branch={branch}, name={name}") - - if not url: - raise HTTPException(status_code=400, detail="Repository URL is required") - - # Derive name from URL if not provided - if not name: - name = url.split("/")[-1].removesuffix(".git") + logger.error(f"Error in event stream: {e}") + from ag_ui.core import EventType, RunErrorEvent + yield encoder.encode(RunErrorEvent( + type=EventType.RUN_ERROR, + thread_id=run_agent_input.thread_id or (state.context.session_id if state.context else ""), + run_id=run_agent_input.run_id or "unknown", + message=str(e), + )) - # Clone the repository at runtime - success, repo_path, was_newly_cloned = await clone_repo_at_runtime( - url, branch, name, github_token, gitlab_token + return StreamingResponse( + event_stream(), + media_type=encoder.get_content_type(), + headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}, ) - if not success: - raise HTTPException( - status_code=500, detail=f"Failed to clone repository: {url}" - ) - # Only update state and trigger notification if repo was newly cloned - # This prevents duplicate notifications when both backend and operator call this endpoint - if was_newly_cloned: - # Update REPOS_JSON env var - repos_json = os.getenv("REPOS_JSON", "[]") - try: - repos = json.loads(repos_json) if repos_json else [] - except: - repos = [] - - # Add new repo - repos.append({"name": name, "input": {"url": url, "branch": branch}}) - - os.environ["REPOS_JSON"] = json.dumps(repos) - - # Reset adapter state to force reinitialization on next run - _adapter_initialized = False - adapter._first_run = True - - logger.info( - f"Repo '{name}' added and cloned, adapter will reinitialize on next run" - ) - - # Trigger a notification to Claude about the new repository - asyncio.create_task(trigger_repo_added_notification(name, url)) - else: - logger.info( - f"Repo '{name}' already existed, skipping notification (idempotent call)" - ) - return { - "message": "Repository added", - "name": name, - "path": repo_path, - "newly_cloned": was_newly_cloned, - } - - -async def trigger_repo_added_notification(repo_name: str, repo_url: str): - """Notify Claude that a repository has been added.""" - import uuid - - import aiohttp - - # Wait a moment for repo to be fully ready - await asyncio.sleep(1) - - logger.info(f"Triggering repo added notification for: {repo_name}") +@app.post("/interrupt") +async def interrupt_run(): + """Interrupt the current Claude SDK execution.""" + if not state.adapter: + raise HTTPException(status_code=503, detail="No active adapter") + logger.info("Interrupt request received") try: - backend_url = os.getenv("BACKEND_API_URL", "").rstrip("/") - project_name = os.getenv("AGENTIC_SESSION_NAMESPACE", "").strip() - session_id = context.session_id if context else "unknown" - - if not backend_url or not project_name: - logger.error( - "Cannot trigger repo notification: BACKEND_API_URL or PROJECT_NAME not set" - ) - return - - url = f"{backend_url}/projects/{project_name}/agentic-sessions/{session_id}/agui/run" - - notification = f"The repository '{repo_name}' has been added to your workspace. You can now access it at the path 'repos/{repo_name}/'. Please acknowledge this to the user and let them know you can now read and work with files in this repository." - - payload = { - "threadId": session_id, - "runId": str(uuid.uuid4()), - "messages": [ - { - "id": str(uuid.uuid4()), - "role": "user", - "content": notification, - "metadata": { - "hidden": True, - "autoSent": True, - "source": "repo_added", - }, - } - ], - } - - bot_token = os.getenv("BOT_TOKEN", "").strip() - headers = {"Content-Type": "application/json"} - if bot_token: - headers["Authorization"] = f"Bearer {bot_token}" - - async with aiohttp.ClientSession() as session: - async with session.post(url, json=payload, headers=headers) as resp: - if resp.status == 200: - result = await resp.json() - logger.info(f"Repo notification sent: {result}") - else: - error_text = await resp.text() - logger.error( - f"Repo notification failed: {resp.status} - {error_text}" - ) - + await state.adapter.interrupt() + return {"message": "Interrupt signal sent to Claude SDK"} except Exception as e: - logger.error(f"Failed to trigger repo notification: {e}") - - -@app.post("/repos/remove") -async def remove_repo(request: Request): - """ - Remove repository - triggers Claude SDK client restart. - - Accepts: {"name": "..."} - """ - import shutil - from pathlib import Path - - global _adapter_initialized - - if not adapter: - raise HTTPException(status_code=503, detail="Adapter not initialized") - - body = await request.json() - repo_name = body.get("name", "") - logger.info(f"Remove repo request: {repo_name}") - - # Delete repository from filesystem - workspace_path = os.getenv("WORKSPACE_PATH", "/workspace") - repo_path = Path(workspace_path) / "repos" / repo_name - - if repo_path.exists(): - try: - shutil.rmtree(repo_path) - logger.info(f"Deleted repository directory: {repo_path}") - except Exception as e: - logger.error(f"Failed to delete repository directory {repo_path}: {e}") - raise HTTPException( - status_code=500, detail=f"Failed to delete repository: {e}" - ) - else: - logger.warning(f"Repository directory not found: {repo_path}") - - # Update REPOS_JSON env var - repos_json = os.getenv("REPOS_JSON", "[]") - try: - repos = json.loads(repos_json) if repos_json else [] - except: - repos = [] - - # Remove repo by name - repos = [r for r in repos if r.get("name") != repo_name] - - os.environ["REPOS_JSON"] = json.dumps(repos) - - # Reset adapter state - _adapter_initialized = False - adapter._first_run = True - - logger.info(f"Repo removed, adapter will reinitialize on next run") - - return {"message": "Repository removed"} - - -@app.get("/repos/status") -async def get_repos_status(): - """ - Get current status of all repositories in the workspace. - - Returns for each repo: - - url: Repository URL - - name: Directory name - - branches: All local branches - - currentActiveBranch: Currently checked out branch - - defaultBranch: Default branch of remote - """ - if not adapter: - raise HTTPException(status_code=503, detail="Adapter not initialized") - - import re - from pathlib import Path - - workspace_path = os.getenv("WORKSPACE_PATH", "/workspace") - repos_dir = Path(workspace_path) / "repos" - - if not repos_dir.exists(): - return {"repos": []} - - repos_status = [] - - # Iterate through all directories in repos/ - for repo_path in repos_dir.iterdir(): - if not repo_path.is_dir() or not (repo_path / ".git").exists(): - continue - - try: - repo_name = repo_path.name - - # Get remote URL - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(repo_path), - "config", - "--get", - "remote.origin.url", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() - repo_url = stdout.decode().strip() if process.returncode == 0 else "" - - # Strip any embedded tokens from URL before returning (security) - # Remove patterns like: https://x-access-token:TOKEN@github.com -> https://github.com - repo_url = re.sub(r"https://[^:]+:[^@]+@", "https://", repo_url) - - # Get current active branch - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(repo_path), - "rev-parse", - "--abbrev-ref", - "HEAD", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() - current_branch = ( - stdout.decode().strip() if process.returncode == 0 else "unknown" - ) - - # Get all local branches - process = await asyncio.create_subprocess_exec( - "git", - "-C", - str(repo_path), - "branch", - "--format=%(refname:short)", - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await process.communicate() - branches = ( - [b.strip() for b in stdout.decode().split("\n") if b.strip()] - if process.returncode == 0 - else [] - ) - - # Get default branch using robust detection - default_branch = await get_default_branch(str(repo_path)) - - repos_status.append( - { - "url": repo_url, - "name": repo_name, - "branches": branches, - "currentActiveBranch": current_branch, - "defaultBranch": default_branch, - } - ) - - except Exception as e: - logger.error(f"Error getting status for repo {repo_path}: {e}") - continue - - return {"repos": repos_status} + logger.error(f"Interrupt failed: {e}") + raise HTTPException(status_code=500, detail=str(e)) @app.get("/health") @@ -1630,23 +337,21 @@ async def health(): """Health check endpoint.""" return { "status": "healthy", - "session_id": context.session_id if context else None, + "session_id": state.context.session_id if state.context else None, } +# ------------------------------------------------------------------ +# Entry point +# ------------------------------------------------------------------ + + def main(): """Start the AG-UI server.""" port = int(os.getenv("AGUI_PORT", "8000")) host = os.getenv("AGUI_HOST", "0.0.0.0") - logger.info(f"Starting Claude Code AG-UI server on {host}:{port}") - - uvicorn.run( - app, - host=host, - port=port, - log_level="info", - ) + uvicorn.run(app, host=host, port=port, log_level="info") if __name__ == "__main__": diff --git a/components/runners/claude-code-runner/mcp.py b/components/runners/claude-code-runner/mcp.py new file mode 100644 index 000000000..41870e16b --- /dev/null +++ b/components/runners/claude-code-runner/mcp.py @@ -0,0 +1,89 @@ +""" +MCP server building and authentication checks for the Claude Code runner. + +Assembles the full MCP server dict (external servers from .mcp.json + +platform tools like restart_session and rubric evaluation) and provides +a pre-flight auth check that logs status without emitting events. +""" + +import logging + +import config as runner_config +from context import RunnerContext +from tools import create_restart_session_tool, create_rubric_mcp_tool, load_rubric_content + +logger = logging.getLogger(__name__) + + +DEFAULT_ALLOWED_TOOLS = [ + "Read", "Write", "Bash", "Glob", "Grep", "Edit", + "MultiEdit", "WebSearch", +] + + +def build_mcp_servers(context: RunnerContext, cwd_path: str, obs=None) -> dict: + """Build the full MCP server config dict including platform tools. + + Args: + context: Runner context. + cwd_path: Working directory (used to find rubric files). + obs: Optional ObservabilityManager (passed to rubric tool). + + Returns: + Dict of MCP server name → server config. + """ + from claude_agent_sdk import create_sdk_mcp_server + from claude_agent_sdk import tool as sdk_tool + + mcp_servers = runner_config.load_mcp_config(context, cwd_path) or {} + + # Session control tool + restart_tool = create_restart_session_tool(None, sdk_tool) + session_server = create_sdk_mcp_server( + name="session", version="1.0.0", tools=[restart_tool] + ) + mcp_servers["session"] = session_server + logger.info("Added session control MCP tools (restart_session)") + + # Rubric evaluation tool + rubric_content, rubric_config = load_rubric_content(cwd_path) + if rubric_content or rubric_config: + rubric_tool = create_rubric_mcp_tool( + rubric_content=rubric_content or "", + rubric_config=rubric_config, + obs=obs, + session_id=context.session_id, + sdk_tool_decorator=sdk_tool, + ) + if rubric_tool: + rubric_server = create_sdk_mcp_server( + name="rubric", version="1.0.0", tools=[rubric_tool] + ) + mcp_servers["rubric"] = rubric_server + logger.info( + f"Added rubric evaluation MCP tool " + f"(categories: {list(rubric_config.get('schema', {}).keys())})" + ) + + return mcp_servers + + +def build_allowed_tools(mcp_servers: dict) -> list[str]: + """Build the list of allowed tool names from default tools + MCP servers.""" + allowed = list(DEFAULT_ALLOWED_TOOLS) + for server_name in mcp_servers.keys(): + allowed.append(f"mcp__{server_name}") + logger.info(f"MCP tool permissions granted for servers: {list(mcp_servers.keys())}") + return allowed + + +def log_auth_status(mcp_servers: dict) -> None: + """Log MCP server authentication status (server-side only, no events).""" + from endpoints.mcp_status import check_mcp_authentication as _check_mcp_authentication + + for server_name in mcp_servers.keys(): + is_auth, msg = _check_mcp_authentication(server_name) + if is_auth is False: + logger.warning(f"MCP auth: {server_name}: {msg}") + elif is_auth is None and msg: + logger.info(f"MCP auth: {server_name}: {msg}") diff --git a/components/runners/claude-code-runner/middleware/__init__.py b/components/runners/claude-code-runner/middleware/__init__.py new file mode 100644 index 000000000..be18b1369 --- /dev/null +++ b/components/runners/claude-code-runner/middleware/__init__.py @@ -0,0 +1,11 @@ +""" +AG-UI middleware for the Ambient Code Platform runner. + +Middleware wraps the adapter's event stream to add platform concerns +(tracing, developer events) without modifying the adapter itself. +""" + +from middleware.developer_events import emit_developer_message +from middleware.tracing import tracing_middleware + +__all__ = ["tracing_middleware", "emit_developer_message"] diff --git a/components/runners/claude-code-runner/middleware/developer_events.py b/components/runners/claude-code-runner/middleware/developer_events.py new file mode 100644 index 000000000..6bfa59cb7 --- /dev/null +++ b/components/runners/claude-code-runner/middleware/developer_events.py @@ -0,0 +1,58 @@ +""" +AG-UI Developer Events Middleware — platform setup lifecycle events. + +Emits ``TextMessage`` events with ``role="developer"`` to report platform +setup progress (auth, workspace, MCP servers) through the AG-UI stream. + +The frontend can show or hide developer messages via a debug toggle. + +Usage:: + + from middleware.developer_events import emit_developer_message + + async for event in emit_developer_message("Auth connected via API key"): + yield encoder.encode(event) +""" + +import logging +import uuid +from typing import AsyncIterator + +from ag_ui.core import ( + BaseEvent, + EventType, + TextMessageContentEvent, + TextMessageEndEvent, + TextMessageStartEvent, +) + +logger = logging.getLogger(__name__) + + +async def emit_developer_message(text: str) -> AsyncIterator[BaseEvent]: + """Emit a single developer message as three AG-UI events. + + Args: + text: The developer message content. + + Yields: + TextMessageStartEvent, TextMessageContentEvent, TextMessageEndEvent + """ + msg_id = str(uuid.uuid4()) + + yield TextMessageStartEvent( + type=EventType.TEXT_MESSAGE_START, + message_id=msg_id, + role="developer", + ) + yield TextMessageContentEvent( + type=EventType.TEXT_MESSAGE_CONTENT, + message_id=msg_id, + delta=text, + ) + yield TextMessageEndEvent( + type=EventType.TEXT_MESSAGE_END, + message_id=msg_id, + ) + + logger.debug(f"Developer event emitted: {text}") diff --git a/components/runners/claude-code-runner/middleware/tracing.py b/components/runners/claude-code-runner/middleware/tracing.py new file mode 100644 index 000000000..3c9cde4db --- /dev/null +++ b/components/runners/claude-code-runner/middleware/tracing.py @@ -0,0 +1,82 @@ +""" +AG-UI Tracing Middleware — Langfuse observability for the event stream. + +Wraps an adapter's ``async for event in adapter.run(...)`` stream to: + +1. Track turns, tool calls, and usage in Langfuse (via ObservabilityManager) +2. Emit a ``CustomEvent`` with the Langfuse trace ID once available +3. Finalise the turn trace on ``RUN_FINISHED`` + +Usage:: + + from middleware.tracing import tracing_middleware + + async for event in tracing_middleware(adapter.run(input), obs=obs, model=model, prompt=prompt): + yield encoder.encode(event) + +The middleware is transparent — if ``obs`` is ``None`` it simply yields events +unchanged with zero overhead. +""" + +import logging +from typing import Any, AsyncIterator, Optional + +from ag_ui.core import BaseEvent, CustomEvent, EventType + +logger = logging.getLogger(__name__) + + +async def tracing_middleware( + event_stream: AsyncIterator[BaseEvent], + *, + obs: Optional[Any] = None, + model: str = "", + prompt: str = "", +) -> AsyncIterator[BaseEvent]: + """Wrap an AG-UI event stream with Langfuse tracing. + + Args: + event_stream: The upstream adapter's event stream (``adapter.run(input)``). + obs: An ``ObservabilityManager`` instance, or ``None`` to skip tracing. + model: Model name for the Langfuse generation. + prompt: User prompt (used as input for the first turn trace). + + Yields: + The original events plus an ``ambient:langfuse_trace`` ``CustomEvent`` + once the Langfuse trace ID becomes available. + """ + # Fast path: no observability — just pass through + if obs is None: + async for event in event_stream: + yield event + return + + # Initialise event-level tracking state + obs.init_event_tracking(model, prompt) + trace_id_emitted = False + + try: + async for event in event_stream: + # Side-channel: track for Langfuse (no event emission) + obs.track_agui_event(event) + + # Yield the original event unchanged + yield event + + # Emit trace ID as a CustomEvent once it becomes available. + # The trace ID appears after the first TEXT_MESSAGE_START with + # role=assistant triggers start_turn() inside the ObservabilityManager. + if not trace_id_emitted: + trace_id = obs.get_current_trace_id() + if trace_id: + yield CustomEvent( + type=EventType.CUSTOM, + name="ambient:langfuse_trace", + value={"traceId": trace_id}, + ) + trace_id_emitted = True + logger.info(f"Tracing middleware: emitted trace ID {trace_id}") + + finally: + # Safety-net: close any open turn that wasn't ended by RUN_FINISHED + obs.finalize_event_tracking() diff --git a/components/runners/claude-code-runner/observability.py b/components/runners/claude-code-runner/observability.py index a06a296c7..7a4e48a36 100644 --- a/components/runners/claude-code-runner/observability.py +++ b/components/runners/claude-code-runner/observability.py @@ -592,6 +592,205 @@ def track_tool_result(self, tool_use_id: str, content: Any, is_error: bool) -> N except Exception as e: logging.debug(f"Langfuse: Failed to track tool result: {e}") + # ------------------------------------------------------------------ + # AG-UI event-driven tracking + # ------------------------------------------------------------------ + + def init_event_tracking(self, model: str, prompt: str) -> None: + """Prepare the manager to track observability from AG-UI events. + + Call this once per run before feeding events via ``track_agui_event``. + + Args: + model: Model name for the Langfuse generation. + prompt: User prompt (used as input for the first turn trace). + """ + self._evt_model = model + self._evt_prompt = prompt + self._evt_turn_started = False + self._evt_accumulated_text = "" + self._evt_tool_args: dict[str, str] = {} + self._evt_tool_names: dict[str, str] = {} + + def track_agui_event(self, event: Any) -> None: + """Track a single AG-UI event for Langfuse observability. + + Derives turn boundaries, tool calls, and result data entirely from the + AG-UI event stream — no raw SDK messages needed. + + Args: + event: An AG-UI ``BaseEvent`` (or subclass). + """ + if not self.langfuse_client: + return + + from ag_ui.core import EventType + + etype = getattr(event, "type", None) + + # --- Turn start: first assistant text message ---- + if etype == EventType.TEXT_MESSAGE_START: + role = getattr(event, "role", "") + if role == "assistant" and not self._evt_turn_started: + self.start_turn(self._evt_model, user_input=self._evt_prompt) + self._evt_turn_started = True + + # --- Accumulate streamed text --- + elif etype == EventType.TEXT_MESSAGE_CONTENT: + delta = getattr(event, "delta", "") + if delta: + self._evt_accumulated_text += delta + + # --- Tool call start --- + elif etype == EventType.TOOL_CALL_START: + tool_id = getattr(event, "tool_call_id", "") + tool_name = getattr(event, "tool_call_name", "") + self._evt_tool_names[tool_id] = tool_name + self._evt_tool_args[tool_id] = "" + # Create Langfuse span immediately (input details arrive later) + self.track_tool_use(tool_name, tool_id, {}) + + # --- Streaming tool arguments --- + elif etype == EventType.TOOL_CALL_ARGS: + tool_id = getattr(event, "tool_call_id", "") + delta = getattr(event, "delta", "") + if tool_id in self._evt_tool_args: + self._evt_tool_args[tool_id] += delta + + # --- Tool call end --- + elif etype == EventType.TOOL_CALL_END: + tool_id = getattr(event, "tool_call_id", "") + result = getattr(event, "result", None) + error = getattr(event, "error", None) + self.track_tool_result(tool_id, result or error, bool(error)) + self._evt_tool_args.pop(tool_id, None) + self._evt_tool_names.pop(tool_id, None) + + # --- Run finished: close the turn with result data --- + elif etype == EventType.RUN_FINISHED: + self._close_turn_from_agui_result(event) + + def finalize_event_tracking(self) -> None: + """Safety-net: close any open turn that was not ended by a RUN_FINISHED.""" + if not self.langfuse_client: + return + if self._evt_turn_started: + self._close_turn_with_text( + turn_count=1, + text=self._evt_accumulated_text, + usage=None, + ) + self._evt_turn_started = False + + # --- private helpers for event tracking --- + + def _close_turn_from_agui_result(self, event: Any) -> None: + """Extract result data from a ``RUN_FINISHED`` event and close the turn.""" + if not self._evt_turn_started: + return + + result = getattr(event, "result", None) + usage = None + num_turns = 1 + + if isinstance(result, dict): + usage_raw = result.get("usage") + if usage_raw is not None and not isinstance(usage_raw, dict): + try: + if hasattr(usage_raw, "__dict__"): + usage_raw = usage_raw.__dict__ + elif hasattr(usage_raw, "model_dump"): + usage_raw = usage_raw.model_dump() + except Exception: + usage_raw = None + usage = usage_raw if isinstance(usage_raw, dict) else None + num_turns = result.get("num_turns", 1) or 1 + + self._close_turn_with_text( + turn_count=num_turns, + text=self._evt_accumulated_text, + usage=usage, + ) + self._evt_turn_started = False + + def _close_turn_with_text( + self, turn_count: int, text: str, usage: dict | None + ) -> None: + """Close the current Langfuse turn using pre-accumulated text. + + This is the event-driven equivalent of ``end_turn`` — it does the same + Langfuse bookkeeping but takes plain text instead of an SDK message. + """ + if not self._current_turn_generation: + return + + try: + output_text = text or "(no text output)" + + usage_details_dict = None + if usage and isinstance(usage, dict): + input_tokens = usage.get("input_tokens", 0) + output_tokens = usage.get("output_tokens", 0) + cache_creation = usage.get("cache_creation_input_tokens", 0) + cache_read = usage.get("cache_read_input_tokens", 0) + + usage_details_dict = { + "input": input_tokens, + "output": output_tokens, + } + if cache_read > 0: + usage_details_dict["cache_read_input_tokens"] = cache_read + if cache_creation > 0: + usage_details_dict["cache_creation_input_tokens"] = ( + cache_creation + ) + + update_params: dict[str, Any] = { + "output": output_text, + "metadata": {"turn": turn_count}, + } + if usage_details_dict: + update_params["usage_details"] = usage_details_dict + self._current_turn_generation.update(**update_params) + + if self._current_turn_ctx: + self._current_turn_ctx.__exit__(None, None, None) + + self._current_turn_generation = None + self._current_turn_ctx = None + + if self.langfuse_client: + try: + self.langfuse_client.flush() + logging.info(f"Langfuse: Flushed turn {turn_count} data") + except Exception as e: + logging.warning( + f"Langfuse: Flush failed after turn {turn_count}: {e}" + ) + + if usage_details_dict: + total = sum(usage_details_dict.values()) + logging.info( + f"Langfuse: Completed turn {turn_count} " + f"({usage_details_dict.get('input', 0)} input, " + f"{usage_details_dict.get('output', 0)} output, " + f"total: {total})" + ) + else: + logging.info( + f"Langfuse: Completed turn {turn_count} (no usage data)" + ) + + except Exception as e: + logging.error(f"Langfuse: Failed to close turn: {e}", exc_info=True) + if self._current_turn_ctx: + try: + self._current_turn_ctx.__exit__(None, None, None) + except Exception: + pass + self._current_turn_generation = None + self._current_turn_ctx = None + async def finalize(self) -> None: """Finalize and flush observability data.""" if not self.langfuse_client: diff --git a/components/runners/claude-code-runner/prompts.py b/components/runners/claude-code-runner/prompts.py index 4008057dd..e443f54be 100644 --- a/components/runners/claude-code-runner/prompts.py +++ b/components/runners/claude-code-runner/prompts.py @@ -58,6 +58,42 @@ "Provide honest, calibrated scores with clear reasoning.\n\n" ) +def build_sdk_system_prompt(workspace_path: str, cwd_path: str) -> dict: + """Build the full system prompt config dict for the Claude SDK. + + Loads ambient config, resolves the workflow name, builds the workspace + context prompt, and wraps it in the Claude Code preset format. + + Returns: + Dict with ``type``, ``preset``, and ``append`` keys. + """ + import config as runner_config + + repos_cfg = runner_config.get_repos_config() + active_workflow_url = (os.getenv("ACTIVE_WORKFLOW_GIT_URL") or "").strip() + ambient_config = ( + runner_config.load_ambient_config(cwd_path) if active_workflow_url else {} + ) + + derived_name = None + if active_workflow_url: + derived_name = active_workflow_url.split("/")[-1].removesuffix(".git") + + workspace_prompt = build_workspace_context_prompt( + repos_cfg=repos_cfg, + workflow_name=derived_name if active_workflow_url else None, + artifacts_path="artifacts", + ambient_config=ambient_config, + workspace_path=workspace_path, + ) + + return { + "type": "preset", + "preset": "claude_code", + "append": workspace_prompt, + } + + RESTART_TOOL_DESCRIPTION = ( "Restart the Claude session to recover from issues, clear state, " "or get a fresh connection. Use this if you detect you're in a " diff --git a/components/runners/claude-code-runner/pyproject.toml b/components/runners/claude-code-runner/pyproject.toml index 093186cf3..920ba7bbb 100644 --- a/components/runners/claude-code-runner/pyproject.toml +++ b/components/runners/claude-code-runner/pyproject.toml @@ -43,8 +43,8 @@ dev-dependencies = [ ] [tool.setuptools] -py-modules = ["main", "adapter", "auth", "config", "context", "observability", "prompts", "security_utils", "utils", "workspace"] -packages = ["tools"] +py-modules = ["main", "adapter", "auth", "config", "context", "mcp", "observability", "prompts", "security_utils", "utils", "workspace"] +packages = ["tools", "ag_ui_claude_sdk", "endpoints", "middleware", "ambient_runner", "ambient_runner.endpoints", "ambient_runner.middleware", "ambient_runner.platform", "ambient_runner.bridges"] [build-system] requires = ["setuptools>=61.0"] diff --git a/components/runners/claude-code-runner/tests/conftest.py b/components/runners/claude-code-runner/tests/conftest.py new file mode 100644 index 000000000..da4a761c7 --- /dev/null +++ b/components/runners/claude-code-runner/tests/conftest.py @@ -0,0 +1,129 @@ +""" +Shared fixtures for runner unit tests. +""" + +import asyncio +from dataclasses import dataclass, field +from typing import Any, AsyncIterator, Optional +from unittest.mock import AsyncMock, MagicMock, Mock + +import pytest + +from ag_ui.core import ( + BaseEvent, + CustomEvent, + EventType, + RunFinishedEvent, + RunStartedEvent, + TextMessageContentEvent, + TextMessageEndEvent, + TextMessageStartEvent, + ToolCallArgsEvent, + ToolCallEndEvent, + ToolCallStartEvent, +) + + +# ------------------------------------------------------------------ +# AG-UI event factories +# ------------------------------------------------------------------ + + +def make_run_started(thread_id: str = "t-1", run_id: str = "r-1") -> RunStartedEvent: + return RunStartedEvent( + type=EventType.RUN_STARTED, + thread_id=thread_id, + run_id=run_id, + ) + + +def make_text_start(msg_id: str = "m-1", role: str = "assistant") -> TextMessageStartEvent: + return TextMessageStartEvent( + type=EventType.TEXT_MESSAGE_START, + message_id=msg_id, + role=role, + ) + + +def make_text_content(msg_id: str = "m-1", delta: str = "Hello") -> TextMessageContentEvent: + return TextMessageContentEvent( + type=EventType.TEXT_MESSAGE_CONTENT, + message_id=msg_id, + delta=delta, + ) + + +def make_text_end(msg_id: str = "m-1") -> TextMessageEndEvent: + return TextMessageEndEvent( + type=EventType.TEXT_MESSAGE_END, + message_id=msg_id, + ) + + +def make_tool_start(tool_id: str = "tc-1", name: str = "Read") -> ToolCallStartEvent: + return ToolCallStartEvent( + type=EventType.TOOL_CALL_START, + tool_call_id=tool_id, + tool_call_name=name, + ) + + +def make_tool_args(tool_id: str = "tc-1", delta: str = '{"file":"x"}') -> ToolCallArgsEvent: + return ToolCallArgsEvent( + type=EventType.TOOL_CALL_ARGS, + tool_call_id=tool_id, + delta=delta, + ) + + +def make_tool_end(tool_id: str = "tc-1") -> ToolCallEndEvent: + return ToolCallEndEvent( + type=EventType.TOOL_CALL_END, + tool_call_id=tool_id, + ) + + +def make_run_finished(thread_id: str = "t-1", run_id: str = "r-1") -> RunFinishedEvent: + return RunFinishedEvent( + type=EventType.RUN_FINISHED, + thread_id=thread_id, + run_id=run_id, + ) + + +async def async_event_stream(events: list[BaseEvent]) -> AsyncIterator[BaseEvent]: + """Turn a list of events into an async iterator (simulates adapter.run()).""" + for event in events: + yield event + + +# ------------------------------------------------------------------ +# Mock ObservabilityManager +# ------------------------------------------------------------------ + + +class MockObservabilityManager: + """Lightweight mock of ObservabilityManager for middleware tests.""" + + def __init__(self, trace_id: str | None = "trace-abc-123"): + self._trace_id = trace_id + self.init_event_tracking_calls: list[tuple[str, str]] = [] + self.tracked_events: list[BaseEvent] = [] + self.finalize_called = False + # Simulate: trace ID only available after a TEXT_MESSAGE_START (assistant) + self._turn_started = False + + def init_event_tracking(self, model: str, prompt: str) -> None: + self.init_event_tracking_calls.append((model, prompt)) + + def track_agui_event(self, event: BaseEvent) -> None: + self.tracked_events.append(event) + if getattr(event, "type", None) == EventType.TEXT_MESSAGE_START: + if getattr(event, "role", "") == "assistant": + self._turn_started = True + + def get_current_trace_id(self) -> str | None: + return self._trace_id if self._turn_started else None + + def finalize_event_tracking(self) -> None: + self.finalize_called = True diff --git a/components/runners/claude-code-runner/tests/test_adapter.py b/components/runners/claude-code-runner/tests/test_adapter.py new file mode 100644 index 000000000..74287f6bc --- /dev/null +++ b/components/runners/claude-code-runner/tests/test_adapter.py @@ -0,0 +1,243 @@ +"""Unit tests for adapter.py (setup_platform, build_adapter, _build_options).""" + +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from context import RunnerContext + + +# ------------------------------------------------------------------ +# _build_options tests (pure function, no mocks needed) +# ------------------------------------------------------------------ + + +class TestBuildOptions: + """Test the _build_options function that assembles SDK options.""" + + def _make_context(self, **env_overrides) -> RunnerContext: + """Create a minimal RunnerContext for testing (avoids chdir side-effect).""" + ctx = object.__new__(RunnerContext) + ctx.session_id = "test" + ctx.workspace_path = "/tmp/test" + ctx.environment = {"IS_RESUME": "false", **env_overrides} + ctx.metadata = {} + return ctx + + def test_basic_options_structure(self): + from adapter import _build_options + + ctx = self._make_context() + opts = _build_options( + context=ctx, + cwd_path="/work", + add_dirs=[], + configured_model="claude-4", + allowed_tools=["Read", "Write"], + mcp_servers={"jira": {}}, + system_prompt_config={"type": "text"}, + sdk_stderr_handler=lambda x: None, + first_run=True, + ) + + assert opts["cwd"] == "/work" + assert opts["model"] == "claude-4" + assert opts["allowed_tools"] == ["Read", "Write"] + assert opts["mcp_servers"] == {"jira": {}} + assert opts["permission_mode"] == "acceptEdits" + assert opts["include_partial_messages"] is True + assert "continue_conversation" not in opts + + def test_add_dirs_included(self): + from adapter import _build_options + + ctx = self._make_context() + opts = _build_options( + context=ctx, cwd_path="/w", add_dirs=["/extra1", "/extra2"], + configured_model="", allowed_tools=[], mcp_servers={}, + system_prompt_config={}, sdk_stderr_handler=lambda x: None, first_run=True, + ) + assert opts["add_dirs"] == ["/extra1", "/extra2"] + + def test_add_dirs_excluded_when_empty(self): + from adapter import _build_options + + ctx = self._make_context() + opts = _build_options( + context=ctx, cwd_path="/w", add_dirs=[], + configured_model="", allowed_tools=[], mcp_servers={}, + system_prompt_config={}, sdk_stderr_handler=lambda x: None, first_run=True, + ) + assert "add_dirs" not in opts + + def test_max_tokens_from_LLM_MAX_TOKENS(self): + from adapter import _build_options + + ctx = self._make_context(LLM_MAX_TOKENS="8192") + opts = _build_options( + context=ctx, cwd_path="/w", add_dirs=[], configured_model="", + allowed_tools=[], mcp_servers={}, system_prompt_config={}, + sdk_stderr_handler=lambda x: None, first_run=True, + ) + assert opts["max_tokens"] == 8192 + + def test_max_tokens_fallback_MAX_TOKENS(self): + from adapter import _build_options + + ctx = self._make_context(MAX_TOKENS="4096") + opts = _build_options( + context=ctx, cwd_path="/w", add_dirs=[], configured_model="", + allowed_tools=[], mcp_servers={}, system_prompt_config={}, + sdk_stderr_handler=lambda x: None, first_run=True, + ) + assert opts["max_tokens"] == 4096 + + def test_invalid_max_tokens_ignored(self): + from adapter import _build_options + + ctx = self._make_context(LLM_MAX_TOKENS="nope") + opts = _build_options( + context=ctx, cwd_path="/w", add_dirs=[], configured_model="", + allowed_tools=[], mcp_servers={}, system_prompt_config={}, + sdk_stderr_handler=lambda x: None, first_run=True, + ) + assert "max_tokens" not in opts + + def test_temperature_from_env(self): + from adapter import _build_options + + ctx = self._make_context(LLM_TEMPERATURE="0.5") + opts = _build_options( + context=ctx, cwd_path="/w", add_dirs=[], configured_model="", + allowed_tools=[], mcp_servers={}, system_prompt_config={}, + sdk_stderr_handler=lambda x: None, first_run=True, + ) + assert opts["temperature"] == pytest.approx(0.5) + + def test_continue_conversation_on_second_run(self): + from adapter import _build_options + + ctx = self._make_context() + opts = _build_options( + context=ctx, cwd_path="/w", add_dirs=[], configured_model="", + allowed_tools=[], mcp_servers={}, system_prompt_config={}, + sdk_stderr_handler=lambda x: None, first_run=False, + ) + assert opts["continue_conversation"] is True + + def test_continue_conversation_on_resume(self): + from adapter import _build_options + + ctx = self._make_context(IS_RESUME="true") + opts = _build_options( + context=ctx, cwd_path="/w", add_dirs=[], configured_model="", + allowed_tools=[], mcp_servers={}, system_prompt_config={}, + sdk_stderr_handler=lambda x: None, first_run=True, + ) + assert opts["continue_conversation"] is True + + def test_no_continue_on_first_run_no_resume(self): + from adapter import _build_options + + ctx = self._make_context() + opts = _build_options( + context=ctx, cwd_path="/w", add_dirs=[], configured_model="", + allowed_tools=[], mcp_servers={}, system_prompt_config={}, + sdk_stderr_handler=lambda x: None, first_run=True, + ) + assert "continue_conversation" not in opts + + def test_model_not_set_when_empty(self): + from adapter import _build_options + + ctx = self._make_context() + opts = _build_options( + context=ctx, cwd_path="/w", add_dirs=[], configured_model="", + allowed_tools=[], mcp_servers={}, system_prompt_config={}, + sdk_stderr_handler=lambda x: None, first_run=True, + ) + assert "model" not in opts + + +# ------------------------------------------------------------------ +# build_adapter tests (mocked dependencies) +# ------------------------------------------------------------------ + + +class TestBuildAdapter: + """Test build_adapter with mocked platform modules.""" + + @patch("adapter.ClaudeAgentAdapter") + @patch("adapter.mcp_mod") + @patch("adapter.prompts") + def test_creates_adapter_with_correct_args(self, mock_prompts, mock_mcp, MockAdapter): + from adapter import build_adapter + + mock_mcp.build_mcp_servers.return_value = {"jira": {"url": "http://jira"}} + mock_mcp.build_allowed_tools.return_value = ["Read", "Write", "Bash"] + mock_mcp.log_auth_status = MagicMock() + mock_prompts.build_sdk_system_prompt.return_value = {"type": "text", "content": "Hello"} + MockAdapter.return_value = MagicMock() + + ctx = object.__new__(RunnerContext) + ctx.session_id = "test" + ctx.workspace_path = "/workspace" + ctx.environment = {"IS_RESUME": "false"} + ctx.metadata = {} + + adapter = build_adapter( + ctx, + configured_model="claude-sonnet", + cwd_path="/workspace/src", + add_dirs=["/extra"], + first_run=True, + obs=None, + ) + + # Verify ClaudeAgentAdapter was created + MockAdapter.assert_called_once() + call_kwargs = MockAdapter.call_args[1] + assert call_kwargs["name"] == "claude_code_runner" + + # Verify MCP was called + mock_mcp.build_mcp_servers.assert_called_once_with(ctx, "/workspace/src", None) + mock_mcp.log_auth_status.assert_called_once() + mock_mcp.build_allowed_tools.assert_called_once() + + # Verify prompts were built + mock_prompts.build_sdk_system_prompt.assert_called_once_with("/workspace", "/workspace/src") + + +# ------------------------------------------------------------------ +# setup_platform tests (mocked auth/workspace) +# ------------------------------------------------------------------ + + +@pytest.mark.asyncio +class TestSetupPlatform: + """Test setup_platform orchestration.""" + + @patch("adapter.workspace") + @patch("adapter.auth") + async def test_returns_model_and_platform_info(self, mock_auth, mock_workspace): + from adapter import setup_platform + + mock_auth.setup_sdk_authentication = AsyncMock(return_value=("key123", False, "claude-4")) + mock_auth.populate_runtime_credentials = AsyncMock() + mock_workspace.resolve_sdk_paths.return_value = ("/w/src", ["/w/extra"]) + + ctx = object.__new__(RunnerContext) + ctx.session_id = "test" + ctx.workspace_path = "/w" + ctx.environment = {} + ctx.metadata = {} + + model, info = await setup_platform(ctx) + + assert model == "claude-4" + assert info["cwd_path"] == "/w/src" + assert info["add_dirs"] == ["/w/extra"] + + mock_auth.setup_sdk_authentication.assert_awaited_once_with(ctx) + mock_auth.populate_runtime_credentials.assert_awaited_once_with(ctx) + mock_workspace.resolve_sdk_paths.assert_called_once_with(ctx) diff --git a/components/runners/claude-code-runner/tests/test_bridge_claude.py b/components/runners/claude-code-runner/tests/test_bridge_claude.py new file mode 100644 index 000000000..ade97308a --- /dev/null +++ b/components/runners/claude-code-runner/tests/test_bridge_claude.py @@ -0,0 +1,270 @@ +"""Unit tests for PlatformBridge ABC and ClaudeBridge.""" + +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from ag_ui.core import EventType, RunAgentInput + +from ambient_runner.bridge import FrameworkCapabilities, PlatformBridge, PlatformContext +from ambient_runner.bridges.claude import ClaudeBridge + + +# ------------------------------------------------------------------ +# PlatformBridge ABC tests +# ------------------------------------------------------------------ + + +class TestPlatformBridgeABC: + """Verify the abstract contract.""" + + def test_cannot_instantiate_directly(self): + with pytest.raises(TypeError): + PlatformBridge() + + def test_needs_rebuild_default_returns_false(self): + """The default implementation of needs_rebuild returns False.""" + + class MinimalBridge(PlatformBridge): + def capabilities(self): + return FrameworkCapabilities(framework="test") + + def create_adapter(self, ctx): + return None + + async def run(self, input_data): + yield # pragma: no cover + + async def interrupt(self): + pass + + bridge = MinimalBridge() + ctx = PlatformContext(session_id="s1", workspace_path="/tmp") + assert bridge.needs_rebuild(ctx) is False + + +class TestPlatformContext: + """Tests for the PlatformContext dataclass.""" + + def test_defaults(self): + ctx = PlatformContext(session_id="s1", workspace_path="/w") + assert ctx.session_id == "s1" + assert ctx.workspace_path == "/w" + assert ctx.cwd_path == "" + assert ctx.add_dirs == [] + assert ctx.model == "" + assert ctx.mcp_servers == {} + assert ctx.allowed_tools == [] + assert ctx.system_prompt == {} + assert ctx.first_run is True + assert ctx.is_resume is False + assert ctx.environment == {} + assert ctx.extra == {} + + def test_custom_values(self): + ctx = PlatformContext( + session_id="s2", + workspace_path="/work", + cwd_path="/work/src", + model="claude-4", + mcp_servers={"jira": {"url": "http://jira"}}, + allowed_tools=["Read", "Write"], + first_run=False, + is_resume=True, + environment={"LLM_MAX_TOKENS": "4096"}, + ) + assert ctx.model == "claude-4" + assert ctx.mcp_servers == {"jira": {"url": "http://jira"}} + assert ctx.allowed_tools == ["Read", "Write"] + assert ctx.first_run is False + assert ctx.is_resume is True + + +class TestFrameworkCapabilities: + """Tests for the FrameworkCapabilities dataclass.""" + + def test_defaults(self): + caps = FrameworkCapabilities(framework="test") + assert caps.framework == "test" + assert caps.agent_features == [] + assert caps.file_system is False + assert caps.mcp is False + assert caps.tracing is None + assert caps.session_persistence is False + + +# ------------------------------------------------------------------ +# ClaudeBridge tests +# ------------------------------------------------------------------ + + +class TestClaudeBridgeCapabilities: + """Test ClaudeBridge.capabilities() returns correct values.""" + + def test_framework_name(self): + assert ClaudeBridge().capabilities().framework == "claude-agent-sdk" + + def test_agent_features(self): + caps = ClaudeBridge().capabilities() + assert "agentic_chat" in caps.agent_features + assert "backend_tool_rendering" in caps.agent_features + assert "thinking" in caps.agent_features + + def test_file_system_support(self): + assert ClaudeBridge().capabilities().file_system is True + + def test_mcp_support(self): + assert ClaudeBridge().capabilities().mcp is True + + def test_tracing(self): + assert ClaudeBridge().capabilities().tracing == "langfuse" + + def test_session_persistence(self): + assert ClaudeBridge().capabilities().session_persistence is True + + +class TestClaudeBridgeBuildOptions: + """Test the private _build_options method with various contexts.""" + + def test_basic_options(self): + ctx = PlatformContext( + session_id="s1", + workspace_path="/w", + cwd_path="/w/src", + model="claude-sonnet", + allowed_tools=["Read"], + mcp_servers={"jira": {}}, + system_prompt={"type": "text", "content": "Hello"}, + ) + opts = ClaudeBridge._build_options(ctx) + + assert opts["cwd"] == "/w/src" + assert opts["model"] == "claude-sonnet" + assert opts["allowed_tools"] == ["Read"] + assert opts["mcp_servers"] == {"jira": {}} + assert opts["system_prompt"] == {"type": "text", "content": "Hello"} + assert opts["permission_mode"] == "acceptEdits" + assert opts["include_partial_messages"] is True + + def test_add_dirs_included_when_present(self): + ctx = PlatformContext(session_id="s1", workspace_path="/w", cwd_path="/w", add_dirs=["/extra"]) + opts = ClaudeBridge._build_options(ctx) + assert opts["add_dirs"] == ["/extra"] + + def test_add_dirs_excluded_when_empty(self): + ctx = PlatformContext(session_id="s1", workspace_path="/w", cwd_path="/w") + opts = ClaudeBridge._build_options(ctx) + assert "add_dirs" not in opts + + def test_max_tokens_from_environment(self): + ctx = PlatformContext( + session_id="s1", workspace_path="/w", cwd_path="/w", + environment={"LLM_MAX_TOKENS": "8192"}, + ) + opts = ClaudeBridge._build_options(ctx) + assert opts["max_tokens"] == 8192 + + def test_max_tokens_fallback_to_MAX_TOKENS(self): + ctx = PlatformContext( + session_id="s1", workspace_path="/w", cwd_path="/w", + environment={"MAX_TOKENS": "4096"}, + ) + opts = ClaudeBridge._build_options(ctx) + assert opts["max_tokens"] == 4096 + + def test_invalid_max_tokens_ignored(self): + ctx = PlatformContext( + session_id="s1", workspace_path="/w", cwd_path="/w", + environment={"LLM_MAX_TOKENS": "not_a_number"}, + ) + opts = ClaudeBridge._build_options(ctx) + assert "max_tokens" not in opts + + def test_temperature_from_environment(self): + ctx = PlatformContext( + session_id="s1", workspace_path="/w", cwd_path="/w", + environment={"LLM_TEMPERATURE": "0.7"}, + ) + opts = ClaudeBridge._build_options(ctx) + assert opts["temperature"] == pytest.approx(0.7) + + def test_continue_conversation_on_non_first_run(self): + ctx = PlatformContext(session_id="s1", workspace_path="/w", cwd_path="/w", first_run=False) + opts = ClaudeBridge._build_options(ctx) + assert opts["continue_conversation"] is True + + def test_continue_conversation_on_resume(self): + ctx = PlatformContext(session_id="s1", workspace_path="/w", cwd_path="/w", is_resume=True) + opts = ClaudeBridge._build_options(ctx) + assert opts["continue_conversation"] is True + + def test_no_continue_on_first_run(self): + ctx = PlatformContext(session_id="s1", workspace_path="/w", cwd_path="/w", first_run=True, is_resume=False) + opts = ClaudeBridge._build_options(ctx) + assert "continue_conversation" not in opts + + def test_model_not_set_when_empty(self): + ctx = PlatformContext(session_id="s1", workspace_path="/w", cwd_path="/w", model="") + opts = ClaudeBridge._build_options(ctx) + assert "model" not in opts + + +class TestClaudeBridgeNeedsRebuild: + """Test rebuild detection logic.""" + + def test_needs_rebuild_when_no_prior_context(self): + bridge = ClaudeBridge() + ctx = PlatformContext(session_id="s1", workspace_path="/w") + assert bridge.needs_rebuild(ctx) is True + + def test_no_rebuild_when_same_config(self): + bridge = ClaudeBridge() + ctx = PlatformContext(session_id="s1", workspace_path="/w", cwd_path="/w/src", model="claude-4") + bridge._last_ctx = ctx + assert bridge.needs_rebuild(ctx) is False + + def test_rebuild_when_cwd_changes(self): + bridge = ClaudeBridge() + bridge._last_ctx = PlatformContext(session_id="s1", workspace_path="/w", cwd_path="/w/old") + new_ctx = PlatformContext(session_id="s1", workspace_path="/w", cwd_path="/w/new") + assert bridge.needs_rebuild(new_ctx) is True + + def test_rebuild_when_model_changes(self): + bridge = ClaudeBridge() + bridge._last_ctx = PlatformContext(session_id="s1", workspace_path="/w", model="old-model") + new_ctx = PlatformContext(session_id="s1", workspace_path="/w", model="new-model") + assert bridge.needs_rebuild(new_ctx) is True + + def test_rebuild_when_mcp_servers_change(self): + bridge = ClaudeBridge() + bridge._last_ctx = PlatformContext(session_id="s1", workspace_path="/w", mcp_servers={"a": {}}) + new_ctx = PlatformContext(session_id="s1", workspace_path="/w", mcp_servers={"b": {}}) + assert bridge.needs_rebuild(new_ctx) is True + + +@pytest.mark.asyncio +class TestClaudeBridgeRunAndInterrupt: + """Test run/interrupt lifecycle with a mocked adapter.""" + + async def test_run_raises_if_no_adapter(self): + bridge = ClaudeBridge() + input_data = RunAgentInput( + thread_id="t1", run_id="r1", messages=[], state={}, + tools=[], context=[], forwarded_props={}, + ) + with pytest.raises(RuntimeError, match="adapter not created"): + _ = [e async for e in bridge.run(input_data)] + + async def test_interrupt_raises_if_no_adapter(self): + bridge = ClaudeBridge() + with pytest.raises(RuntimeError, match="no adapter to interrupt"): + await bridge.interrupt() + + @patch("ambient_runner.bridges.claude.ClaudeAgentAdapter") + async def test_create_adapter_stores_instance(self, MockAdapter): + MockAdapter.return_value = MagicMock() + bridge = ClaudeBridge() + ctx = PlatformContext(session_id="s1", workspace_path="/w", cwd_path="/w") + adapter = bridge.create_adapter(ctx) + assert bridge._adapter is adapter + assert bridge._last_ctx is ctx diff --git a/components/runners/claude-code-runner/tests/test_bridge_langgraph.py b/components/runners/claude-code-runner/tests/test_bridge_langgraph.py new file mode 100644 index 000000000..f275b5b0a --- /dev/null +++ b/components/runners/claude-code-runner/tests/test_bridge_langgraph.py @@ -0,0 +1,144 @@ +"""Unit tests for LangGraphBridge.""" + +from unittest.mock import MagicMock, patch + +import pytest + +from ag_ui.core import RunAgentInput + +from ambient_runner.bridge import PlatformBridge, PlatformContext +from ambient_runner.bridges.langgraph import LangGraphBridge + + +class TestLangGraphBridgeCapabilities: + """Test LangGraphBridge capabilities are correctly different from Claude.""" + + def test_framework_name(self): + assert LangGraphBridge().capabilities().framework == "langgraph" + + def test_no_filesystem(self): + assert LangGraphBridge().capabilities().file_system is False + + def test_no_mcp(self): + assert LangGraphBridge().capabilities().mcp is False + + def test_langsmith_tracing(self): + assert LangGraphBridge().capabilities().tracing == "langsmith" + + def test_no_session_persistence(self): + assert LangGraphBridge().capabilities().session_persistence is False + + def test_agent_features(self): + caps = LangGraphBridge().capabilities() + assert "agentic_chat" in caps.agent_features + assert "shared_state" in caps.agent_features + assert "human_in_the_loop" in caps.agent_features + # Claude-specific features should NOT be present + assert "backend_tool_rendering" not in caps.agent_features + assert "thinking" not in caps.agent_features + + def test_is_platform_bridge_subclass(self): + assert issubclass(LangGraphBridge, PlatformBridge) + + +class TestLangGraphBridgeNeedsRebuild: + """Test rebuild detection for LangGraph.""" + + def test_needs_rebuild_when_no_prior_context(self): + bridge = LangGraphBridge() + ctx = PlatformContext(session_id="s1", workspace_path="/w") + assert bridge.needs_rebuild(ctx) is True + + def test_no_rebuild_when_same_config(self): + bridge = LangGraphBridge() + ctx = PlatformContext( + session_id="s1", workspace_path="/w", + environment={"LANGGRAPH_URL": "http://lg", "LANGGRAPH_GRAPH_ID": "agent"}, + ) + bridge._last_ctx = ctx + assert bridge.needs_rebuild(ctx) is False + + def test_rebuild_when_url_changes(self): + bridge = LangGraphBridge() + bridge._last_ctx = PlatformContext( + session_id="s1", workspace_path="/w", + environment={"LANGGRAPH_URL": "http://old"}, + ) + new_ctx = PlatformContext( + session_id="s1", workspace_path="/w", + environment={"LANGGRAPH_URL": "http://new"}, + ) + assert bridge.needs_rebuild(new_ctx) is True + + def test_rebuild_when_graph_id_changes(self): + bridge = LangGraphBridge() + bridge._last_ctx = PlatformContext( + session_id="s1", workspace_path="/w", + environment={"LANGGRAPH_URL": "http://lg", "LANGGRAPH_GRAPH_ID": "old"}, + ) + new_ctx = PlatformContext( + session_id="s1", workspace_path="/w", + environment={"LANGGRAPH_URL": "http://lg", "LANGGRAPH_GRAPH_ID": "new"}, + ) + assert bridge.needs_rebuild(new_ctx) is True + + +class TestLangGraphBridgeCreateAdapter: + """Test adapter creation with mocked LangGraphAgent.""" + + def test_raises_without_langgraph_url(self): + """Should raise RuntimeError — either because ag_ui_langgraph is missing or URL is empty.""" + bridge = LangGraphBridge() + ctx = PlatformContext(session_id="s1", workspace_path="/w", environment={}) + with pytest.raises(RuntimeError): + bridge.create_adapter(ctx) + + @patch("ambient_runner.bridges.langgraph.LangGraphBridge.create_adapter") + def test_stores_adapter_and_context(self, mock_create): + mock_adapter = MagicMock() + mock_create.return_value = mock_adapter + + bridge = LangGraphBridge() + ctx = PlatformContext( + session_id="s1", workspace_path="/w", + environment={"LANGGRAPH_URL": "http://lg", "LANGGRAPH_GRAPH_ID": "agent"}, + ) + adapter = bridge.create_adapter(ctx) + assert adapter is mock_adapter + + +@pytest.mark.asyncio +class TestLangGraphBridgeRunAndInterrupt: + """Test run/interrupt lifecycle.""" + + async def test_run_raises_if_no_adapter(self): + bridge = LangGraphBridge() + input_data = RunAgentInput( + thread_id="t1", run_id="r1", messages=[], state={}, + tools=[], context=[], forwarded_props={}, + ) + with pytest.raises(RuntimeError, match="adapter not created"): + _ = [e async for e in bridge.run(input_data)] + + async def test_interrupt_raises_if_no_adapter(self): + bridge = LangGraphBridge() + with pytest.raises(RuntimeError, match="no adapter to interrupt"): + await bridge.interrupt() + + async def test_interrupt_with_adapter_that_supports_it(self): + from unittest.mock import AsyncMock + + bridge = LangGraphBridge() + mock_adapter = MagicMock() + mock_adapter.interrupt = AsyncMock() # async mock so await works + bridge._adapter = mock_adapter + # LangGraphBridge checks hasattr before calling + await bridge.interrupt() + mock_adapter.interrupt.assert_awaited_once() + + async def test_interrupt_with_adapter_without_support(self): + bridge = LangGraphBridge() + mock_adapter = MagicMock(spec=[]) # spec=[] means no attributes + bridge._adapter = mock_adapter + # Should not raise, just log a warning + await bridge.interrupt() diff --git a/components/runners/claude-code-runner/tests/test_capabilities_endpoint.py b/components/runners/claude-code-runner/tests/test_capabilities_endpoint.py new file mode 100644 index 000000000..65d290848 --- /dev/null +++ b/components/runners/claude-code-runner/tests/test_capabilities_endpoint.py @@ -0,0 +1,132 @@ +"""Unit tests for the capabilities endpoint.""" + +from unittest.mock import MagicMock, patch + +import pytest +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from endpoints.capabilities import router + + +@pytest.fixture +def client(): + """Create a test client with the capabilities router.""" + app = FastAPI() + app.include_router(router) + return TestClient(app) + + +class TestCapabilitiesEndpoint: + """Test GET /capabilities response shape and values.""" + + @patch("endpoints.capabilities.state") + def test_returns_expected_fields(self, mock_state, client): + mock_state._obs = None + mock_state._configured_model = "claude-sonnet-4-5" + mock_state.context = MagicMock() + mock_state.context.session_id = "test-session" + + resp = client.get("/capabilities") + assert resp.status_code == 200 + data = resp.json() + + # Required fields + assert data["framework"] == "claude-agent-sdk" + assert isinstance(data["agent_features"], list) + assert isinstance(data["platform_features"], list) + assert isinstance(data["file_system"], bool) + assert isinstance(data["mcp"], bool) + assert isinstance(data["session_persistence"], bool) + + @patch("endpoints.capabilities.state") + def test_agent_features_list(self, mock_state, client): + mock_state._obs = None + mock_state._configured_model = "" + mock_state.context = None + + resp = client.get("/capabilities") + data = resp.json() + assert "agentic_chat" in data["agent_features"] + assert "thinking" in data["agent_features"] + + @patch("endpoints.capabilities.state") + def test_platform_features_list(self, mock_state, client): + mock_state._obs = None + mock_state._configured_model = "" + mock_state.context = None + + resp = client.get("/capabilities") + data = resp.json() + assert "repos" in data["platform_features"] + assert "workflows" in data["platform_features"] + assert "feedback" in data["platform_features"] + assert "mcp_diagnostics" in data["platform_features"] + + @patch("endpoints.capabilities.state") + def test_tracing_langfuse_when_obs_present(self, mock_state, client): + mock_obs = MagicMock() + mock_obs.langfuse_client = MagicMock() # truthy + mock_state._obs = mock_obs + mock_state._configured_model = "" + mock_state.context = None + + resp = client.get("/capabilities") + assert resp.json()["tracing"] == "langfuse" + + @patch("endpoints.capabilities.state") + def test_tracing_none_when_no_obs(self, mock_state, client): + mock_state._obs = None + mock_state._configured_model = "" + mock_state.context = None + + resp = client.get("/capabilities") + assert resp.json()["tracing"] is None + + @patch("endpoints.capabilities.state") + def test_tracing_none_when_obs_has_no_client(self, mock_state, client): + mock_obs = MagicMock() + mock_obs.langfuse_client = None + mock_state._obs = mock_obs + mock_state._configured_model = "" + mock_state.context = None + + resp = client.get("/capabilities") + assert resp.json()["tracing"] is None + + @patch("endpoints.capabilities.state") + def test_model_returned(self, mock_state, client): + mock_state._obs = None + mock_state._configured_model = "claude-4-opus" + mock_state.context = None + + resp = client.get("/capabilities") + assert resp.json()["model"] == "claude-4-opus" + + @patch("endpoints.capabilities.state") + def test_model_none_when_empty(self, mock_state, client): + mock_state._obs = None + mock_state._configured_model = "" + mock_state.context = None + + resp = client.get("/capabilities") + assert resp.json()["model"] is None + + @patch("endpoints.capabilities.state") + def test_session_id_returned(self, mock_state, client): + mock_state._obs = None + mock_state._configured_model = "" + mock_state.context = MagicMock() + mock_state.context.session_id = "sess-xyz" + + resp = client.get("/capabilities") + assert resp.json()["session_id"] == "sess-xyz" + + @patch("endpoints.capabilities.state") + def test_session_id_none_when_no_context(self, mock_state, client): + mock_state._obs = None + mock_state._configured_model = "" + mock_state.context = None + + resp = client.get("/capabilities") + assert resp.json()["session_id"] is None diff --git a/components/runners/claude-code-runner/tests/test_developer_events.py b/components/runners/claude-code-runner/tests/test_developer_events.py new file mode 100644 index 000000000..2ff94a914 --- /dev/null +++ b/components/runners/claude-code-runner/tests/test_developer_events.py @@ -0,0 +1,51 @@ +"""Unit tests for the developer events middleware.""" + +import pytest + +from ag_ui.core import EventType + +from middleware.developer_events import emit_developer_message + + +@pytest.mark.asyncio +class TestEmitDeveloperMessage: + """Tests for emit_developer_message async generator.""" + + async def test_yields_three_events(self): + """Should emit START, CONTENT, END for every message.""" + events = [e async for e in emit_developer_message("Auth connected")] + assert len(events) == 3 + + async def test_event_types_in_order(self): + events = [e async for e in emit_developer_message("Hello")] + assert events[0].type == EventType.TEXT_MESSAGE_START + assert events[1].type == EventType.TEXT_MESSAGE_CONTENT + assert events[2].type == EventType.TEXT_MESSAGE_END + + async def test_role_is_developer(self): + events = [e async for e in emit_developer_message("test")] + assert events[0].role == "developer" + + async def test_content_matches_input(self): + text = "MCP servers initialised (3 connected)" + events = [e async for e in emit_developer_message(text)] + assert events[1].delta == text + + async def test_message_ids_consistent(self): + """All three events should share the same message_id.""" + events = [e async for e in emit_developer_message("test")] + msg_id = events[0].message_id + assert msg_id # not empty + assert events[1].message_id == msg_id + assert events[2].message_id == msg_id + + async def test_different_calls_get_different_ids(self): + events_a = [e async for e in emit_developer_message("first")] + events_b = [e async for e in emit_developer_message("second")] + assert events_a[0].message_id != events_b[0].message_id + + async def test_single_char_text(self): + """Minimal text should produce valid events (AG-UI requires min_length=1 for delta).""" + events = [e async for e in emit_developer_message("x")] + assert len(events) == 3 + assert events[1].delta == "x" diff --git a/components/runners/claude-code-runner/tests/test_tracing_middleware.py b/components/runners/claude-code-runner/tests/test_tracing_middleware.py new file mode 100644 index 000000000..dfc7a7ac5 --- /dev/null +++ b/components/runners/claude-code-runner/tests/test_tracing_middleware.py @@ -0,0 +1,157 @@ +"""Unit tests for the tracing middleware.""" + +import pytest + +from ag_ui.core import CustomEvent, EventType + +from middleware.tracing import tracing_middleware +from tests.conftest import ( + MockObservabilityManager, + async_event_stream, + make_run_finished, + make_run_started, + make_text_content, + make_text_end, + make_text_start, + make_tool_args, + make_tool_end, + make_tool_start, +) + + +@pytest.mark.asyncio +class TestTracingMiddlewarePassthrough: + """When obs=None the middleware is a transparent pass-through.""" + + async def test_yields_all_events_unchanged(self): + events = [make_run_started(), make_text_start(), make_text_content(), make_text_end(), make_run_finished()] + result = [e async for e in tracing_middleware(async_event_stream(events), obs=None)] + assert len(result) == len(events) + for orig, got in zip(events, result): + assert orig is got # same object reference + + async def test_empty_stream(self): + result = [e async for e in tracing_middleware(async_event_stream([]), obs=None)] + assert result == [] + + +@pytest.mark.asyncio +class TestTracingMiddlewareObservability: + """When obs is provided it should track events and emit a trace ID.""" + + async def test_initialises_event_tracking(self): + obs = MockObservabilityManager() + events = [make_run_started()] + _ = [e async for e in tracing_middleware(async_event_stream(events), obs=obs, model="claude-4", prompt="hi")] + assert obs.init_event_tracking_calls == [("claude-4", "hi")] + + async def test_tracks_all_events(self): + obs = MockObservabilityManager() + events = [make_run_started(), make_text_start(), make_text_content(delta="yo"), make_text_end(), make_run_finished()] + _ = [e async for e in tracing_middleware(async_event_stream(events), obs=obs)] + assert len(obs.tracked_events) == len(events) + + async def test_emits_trace_id_custom_event_after_assistant_start(self): + """Trace ID should appear as a CustomEvent right after the first assistant TEXT_MESSAGE_START.""" + obs = MockObservabilityManager(trace_id="trace-xyz") + events = [ + make_run_started(), + make_text_start(role="assistant"), + make_text_content(delta="Hello"), + make_text_end(), + make_run_finished(), + ] + result = [e async for e in tracing_middleware(async_event_stream(events), obs=obs)] + + # Original 5 events + 1 trace CustomEvent + assert len(result) == 6 + + custom_events = [e for e in result if isinstance(e, CustomEvent)] + assert len(custom_events) == 1 + assert custom_events[0].name == "ambient:langfuse_trace" + assert custom_events[0].value == {"traceId": "trace-xyz"} + + async def test_trace_id_emitted_only_once(self): + """Even with multiple assistant messages, the trace ID event should appear only once.""" + obs = MockObservabilityManager(trace_id="trace-once") + events = [ + make_run_started(), + make_text_start(msg_id="m1", role="assistant"), + make_text_content(msg_id="m1", delta="First"), + make_text_end(msg_id="m1"), + make_text_start(msg_id="m2", role="assistant"), + make_text_content(msg_id="m2", delta="Second"), + make_text_end(msg_id="m2"), + make_run_finished(), + ] + result = [e async for e in tracing_middleware(async_event_stream(events), obs=obs)] + custom_events = [e for e in result if isinstance(e, CustomEvent)] + assert len(custom_events) == 1 + + async def test_no_trace_id_when_none(self): + """If the ObservabilityManager never provides a trace ID, no custom event is emitted.""" + obs = MockObservabilityManager(trace_id=None) + events = [ + make_run_started(), + make_text_start(role="assistant"), + make_text_content(delta="Hello"), + make_text_end(), + make_run_finished(), + ] + result = [e async for e in tracing_middleware(async_event_stream(events), obs=obs)] + custom_events = [e for e in result if isinstance(e, CustomEvent)] + assert len(custom_events) == 0 + + async def test_no_trace_id_before_assistant_message(self): + """The trace ID should not be emitted before the first assistant message.""" + obs = MockObservabilityManager(trace_id="trace-early") + events = [ + make_run_started(), + make_text_start(role="user"), # user message, not assistant + make_text_content(delta="Hello"), + make_text_end(), + make_run_finished(), + ] + result = [e async for e in tracing_middleware(async_event_stream(events), obs=obs)] + custom_events = [e for e in result if isinstance(e, CustomEvent)] + # trace_id is not emitted because the mock only returns it after assistant message + assert len(custom_events) == 0 + + async def test_finalizes_on_normal_completion(self): + obs = MockObservabilityManager() + events = [make_run_started(), make_run_finished()] + _ = [e async for e in tracing_middleware(async_event_stream(events), obs=obs)] + assert obs.finalize_called is True + + async def test_finalizes_on_error(self): + """finalize_event_tracking is called even if the stream raises.""" + obs = MockObservabilityManager() + + async def failing_stream(): + yield make_run_started() + raise RuntimeError("boom") + + with pytest.raises(RuntimeError, match="boom"): + _ = [e async for e in tracing_middleware(failing_stream(), obs=obs)] + + assert obs.finalize_called is True + + async def test_preserves_event_order(self): + obs = MockObservabilityManager(trace_id="trace-order") + events = [ + make_run_started(), + make_text_start(role="assistant"), + make_tool_start(tool_id="tc-1", name="Read"), + make_tool_args(tool_id="tc-1"), + make_tool_end(tool_id="tc-1"), + make_text_content(delta="Done"), + make_text_end(), + make_run_finished(), + ] + result = [e async for e in tracing_middleware(async_event_stream(events), obs=obs)] + + # Filter out the injected CustomEvent to verify original order + original_events = [e for e in result if not isinstance(e, CustomEvent)] + assert len(original_events) == len(events) + for orig, got in zip(events, original_events): + assert orig is got diff --git a/components/runners/claude-code-runner/workspace.py b/components/runners/claude-code-runner/workspace.py index 0b01e84aa..c1ad37e86 100644 --- a/components/runners/claude-code-runner/workspace.py +++ b/components/runners/claude-code-runner/workspace.py @@ -139,6 +139,48 @@ def setup_multi_repo_paths( return cwd_path, add_dirs +def resolve_sdk_paths(context: RunnerContext) -> tuple[str, list[str]]: + """Resolve CWD and additional directories for the Claude SDK. + + Determines the working directory based on active workflow, repos config, + or falls back to the artifacts directory. + + Returns: + (cwd_path, additional_dirs) + """ + import config as runner_config + + repos_cfg = runner_config.get_repos_config() + cwd_path = context.workspace_path + add_dirs: list[str] = [] + + active_workflow_url = (os.getenv("ACTIVE_WORKFLOW_GIT_URL") or "").strip() + + if active_workflow_url: + cwd_path, add_dirs, _ = setup_workflow_paths( + context, active_workflow_url, repos_cfg + ) + elif repos_cfg: + cwd_path, add_dirs = setup_multi_repo_paths(context, repos_cfg) + else: + cwd_path = str(Path(context.workspace_path) / "artifacts") + + cwd_path_obj = Path(cwd_path) + if not cwd_path_obj.exists(): + logger.warning(f"Working directory missing, creating: {cwd_path}") + try: + cwd_path_obj.mkdir(parents=True, exist_ok=True) + except Exception as e: + logger.error(f"Failed to create working directory: {e}") + cwd_path = context.workspace_path + + logger.info(f"Claude SDK CWD: {cwd_path}") + if add_dirs: + logger.info(f"Claude SDK additional directories: {add_dirs}") + + return cwd_path, add_dirs + + async def prepare_workspace(context: RunnerContext) -> None: """Validate workspace prepared by init container. diff --git a/docs/adr/0006-ambient-runner-sdk-architecture.md b/docs/adr/0006-ambient-runner-sdk-architecture.md new file mode 100644 index 000000000..2f103b54f --- /dev/null +++ b/docs/adr/0006-ambient-runner-sdk-architecture.md @@ -0,0 +1,423 @@ +# ADR-0006: Ambient Runner SDK Architecture + +**Status:** Proposed +**Date:** 2026-02-10 +**Authors:** Gavin Krumbacher +**Deciders:** Platform Team + +## Context + +The Ambient Code Platform currently has a single runner implementation tightly coupled to the Claude Agent SDK. As we look to support additional frameworks (LangGraph, Cursor SDK, etc.) and adopt the [AG-UI protocol](https://docs.ag-ui.com/) properly, we need a clean architecture that separates: + +1. **Framework-specific logic** (how each SDK works) +2. **Protocol translation** (framework → AG-UI events) +3. **Platform concerns** (auth, workspace, observability, repos, workflows) +4. **Event delivery** (FastAPI, SSE, middleware) + +This ADR defines the layered architecture for the Ambient Runner SDK — a reusable package that lets any AG-UI-compatible framework adapter plug into the Ambient platform. + +## Decision + +### Layer Architecture + +``` +┌──────────────────────────────────────────────────────────┐ +│ Frontend (CopilotKit / Ambient UI) │ +│ Consumes AG-UI events, shows features per capabilities │ +├──────────────────────────────────────────────────────────┤ +│ Backend API (Go) │ +│ Proxies SSE stream, persists MESSAGES_SNAPSHOT to DB, │ +│ manages session lifecycle (K8s pods) │ +├──────────────────────────────────────────────────────────┤ +│ Ambient Runner (Python — this ADR) │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ FastAPI App │ │ +│ │ add_ambient_endpoints(app, bridge) │ │ +│ │ │ │ +│ │ ┌──────────────────────────────────────────────┐ │ │ +│ │ │ Ambient Middleware (AG-UI middleware) │ │ │ +│ │ │ - Tracing (Langfuse) │ │ │ +│ │ │ - Capability declaration │ │ │ +│ │ └──────────────────────────────────────────────┘ │ │ +│ │ │ │ +│ │ ┌──────────────────────────────────────────────┐ │ │ +│ │ │ Platform Bridge (per framework) │ │ │ +│ │ │ Translates platform concepts → framework │ │ │ +│ │ │ config. One bridge per supported framework. │ │ │ +│ │ └──────────────────────────────────────────────┘ │ │ +│ │ │ │ +│ │ ┌──────────────────────────────────────────────┐ │ │ +│ │ │ AG-UI Adapter (per framework) │ │ │ +│ │ │ ag_ui_claude_sdk, ag_ui_langgraph, etc. │ │ │ +│ │ │ Framework SDK → AG-UI events │ │ │ +│ │ └──────────────────────────────────────────────┘ │ │ +│ └────────────────────────────────────────────────────┘ │ +├──────────────────────────────────────────────────────────┤ +│ Framework SDK (Claude Agent SDK, LangGraph, etc.) │ +└──────────────────────────────────────────────────────────┘ +``` + +### Layer Responsibilities + +#### Layer 1: Framework SDK +The raw framework. Each has its own option shapes, tool systems, session management, and model config. **Not our code** — third-party. + +#### Layer 2: AG-UI Adapter +Normalises the framework into [AG-UI events](https://docs.ag-ui.com/concepts/events). Open source, community-maintained. Each adapter implements: + +```python +async def run(input_data: RunAgentInput) -> AsyncIterator[BaseEvent] +async def interrupt() -> None # optional +``` + +Examples: `ag_ui_claude_sdk.ClaudeAgentAdapter`, `ag_ui_langgraph.LangGraphAgent`. + +#### Layer 3: Platform Bridge +Translates platform concepts into framework-specific configuration. **One bridge per framework.** This is where framework differences are handled. + +#### Layer 4: Ambient Middleware +[AG-UI middleware](https://docs.ag-ui.com/concepts/middleware) that adds platform concerns to the event stream as a side-channel. Does NOT emit protocol events — only observes and annotates. + +#### Layer 5: FastAPI App + Platform Endpoints +`add_ambient_endpoints(app, bridge)` registers all platform routes. Conditional on framework capabilities. + +--- + +## Platform Bridge + +### Contract + +```python +@dataclass +class PlatformContext: + """Platform concepts provided to every framework bridge.""" + workspace_path: str + cwd_path: str + add_dirs: list[str] + model: str + max_tokens: int | None + temperature: float | None + system_prompt_append: str + mcp_servers: dict + custom_tools: list + session_id: str + is_continuation: bool + +@dataclass +class FrameworkCapabilities: + """What this framework can do — declared by the bridge.""" + file_system: bool = False # Read/Write/Bash tools + mcp: bool = False # Native MCP support + session_persistence: bool = False + streaming: bool = True + thinking: bool = False + tool_use: bool = True + +class PlatformBridge(ABC): + @abstractmethod + def create_adapter(self, ctx: PlatformContext) -> Any: + """Build the AG-UI adapter with platform config applied.""" + ... + + @abstractmethod + def capabilities(self) -> FrameworkCapabilities: + """Declare what this framework supports.""" + ... +``` + +### Bridge Implementations + +**Claude Agent SDK Bridge:** +```python +class ClaudeBridge(PlatformBridge): + def capabilities(self): + return FrameworkCapabilities( + file_system=True, mcp=True, + session_persistence=True, thinking=True, + ) + + def create_adapter(self, ctx): + return ClaudeAgentAdapter(name="session", options={ + "cwd": ctx.cwd_path, + "model": ctx.model, + "mcp_servers": ctx.mcp_servers, # native MCP + "system_prompt": {"type": "preset", "preset": "claude_code", "append": ctx.system_prompt_append}, + "permission_mode": "acceptEdits", + "continue_conversation": ctx.is_continuation, + }) +``` + +**LangGraph Bridge (example):** +```python +class LangGraphBridge(PlatformBridge): + def __init__(self, graph): + self.graph = graph + + def capabilities(self): + return FrameworkCapabilities( + file_system=False, mcp=False, + session_persistence=True, + ) + + def create_adapter(self, ctx): + return LangGraphAgent(name="session", graph=self.graph) +``` + +The bridge is the extension point for new frameworks. Users implement `PlatformBridge` (2 methods), and the platform handles everything else. + +--- + +## Adapter Lifecycle + +The AG-UI adapter is created **once** per session (not per run) and reused across runs. This preserves session state (e.g. `thread_id → session_id` mapping for conversation resumption). + +The adapter is rebuilt only when configuration changes (workflow switch, repo add/remove). + +For pod restarts (K8s), the `.claude/` directory is restored from S3 by the init container. The bridge sets `is_continuation=True` so the adapter resumes from disk state. + +--- + +## AG-UI Middleware + +Following the [AG-UI middleware pattern](https://docs.ag-ui.com/concepts/middleware), platform concerns are implemented as middleware that wraps the adapter's event stream. + +### Tracing Middleware (Langfuse) + +Observes AG-UI events and drives Langfuse spans/traces. Pure side-channel — no event emission. + +- `TEXT_MESSAGE_START` (role=assistant) → starts a Langfuse turn trace +- `TEXT_MESSAGE_CONTENT` → accumulates text for the trace output +- `TOOL_CALL_START` → creates a Langfuse tool span +- `TOOL_CALL_END` → closes the tool span with result +- `RUN_FINISHED` → closes the turn with usage data from `result` +- `RUN_STARTED` → annotates with Langfuse trace ID in event metadata (not a separate RawEvent) + +The trace ID is added to the `RUN_STARTED` event's metadata by the middleware, not injected as a separate event. The frontend reads it from there for feedback association. + +### Capability Middleware + +Optionally emits a `CustomEvent("ambient:capabilities", {...})` at the start of the first run so the frontend knows what features are available without a separate REST call. + +--- + +## Platform Endpoints + +Registered via `add_ambient_endpoints(app, bridge)`. Conditional on `bridge.capabilities()`. + +### Always registered + +| Endpoint | Method | Purpose | +|---|---|---| +| `/` | POST | AG-UI run endpoint — `adapter.run(input_data)` → SSE stream | +| `/interrupt` | POST | Interrupt active execution — `adapter.interrupt()` | +| `/health` | GET | Health check | +| `/capabilities` | GET | Returns framework + platform capabilities | +| `/feedback` | POST | Thumbs up/down → Langfuse scoring | + +### Conditional on `file_system=True` + +| Endpoint | Method | Purpose | +|---|---|---| +| `/repos/add` | POST | Clone repo into workspace | +| `/repos/remove` | POST | Remove repo from workspace | +| `/repos/status` | GET | Branch/status info for all repos | +| `/workflow` | POST | Change active workflow | + +### Conditional on `mcp=True` + +| Endpoint | Method | Purpose | +|---|---|---| +| `/mcp/status` | GET | MCP server connection diagnostics | + +--- + +## Event Stream Patterns + +### AG-UI Protocol Events (emitted by adapter) + +The adapter handles all standard AG-UI events. The runner does NOT emit these: +- `RUN_STARTED`, `RUN_FINISHED`, `RUN_ERROR` +- `TEXT_MESSAGE_START/CONTENT/END` +- `TOOL_CALL_START/ARGS/END` +- `STATE_SNAPSHOT`, `STATE_DELTA` +- `MESSAGES_SNAPSHOT` +- `THINKING_START/END`, `THINKING_TEXT_MESSAGE_*` + +### Developer Events (role="developer") + +Platform setup lifecycle uses the standard `TextMessage` events with [`role="developer"`](https://docs.ag-ui.com/concepts/events#textmessagestart) — a first-class AG-UI role: +- "Auth connected" +- "Workspace validated" +- "MCP servers initialised" + +Frontends can show/hide developer messages based on user preference (debug mode). + +### Custom Events (platform-specific) + +[`CustomEvent`](https://docs.ag-ui.com/concepts/events#custom) is used for platform-specific extensions that standard AG-UI clients would ignore, but the Ambient frontend understands: + +- `ambient:repo_added` — repo cloned into workspace +- `ambient:repo_removed` — repo removed +- `ambient:workflow_changed` — active workflow switched +- `ambient:capabilities` — framework + platform capabilities declaration +- `ambient:setup_error` — platform setup failure details + +### Meta Events (user annotations) + +[`MetaEvent`](https://docs.ag-ui.com/concepts/events#metaevent) (draft spec) for user feedback that needs to be in the event stream for UI state tracking: + +- `thumbs_up` — positive feedback on a message +- `thumbs_down` — negative feedback on a message + +The REST `POST /feedback` endpoint triggers the Langfuse scoring AND emits a MetaEvent so the frontend can update the thumbs icon state. + +--- + +## Message Persistence + +### Principle: No double compaction + +The AG-UI adapter produces `MESSAGES_SNAPSHOT` at the end of each run with the complete conversation history. This snapshot IS the compacted truth. + +### Flow + +``` +Runner: adapter.run() → emits MESSAGES_SNAPSHOT + ↓ +Backend API: intercepts MESSAGES_SNAPSHOT in SSE proxy → persists to DB + ↓ +Session resume: Backend loads messages from DB → sends as RunAgentInput.messages + ↓ +Runner: feeds messages to adapter → SDK continues conversation +``` + +The runner never touches the DB. The backend does not reconstruct or compact messages — it stores what `MESSAGES_SNAPSHOT` provides. + +### Framework-specific persistence + +The Claude Agent SDK persists its own session state in `.claude/` on disk. This is synced to/from S3 for pod restarts. This is complementary to (not competing with) the backend's DB persistence: + +| What | Where | Purpose | +|---|---|---| +| `.claude/` directory | S3 ↔ PVC | SDK session resume (framework concern) | +| `MESSAGES_SNAPSHOT` | Backend DB | Platform history, audit trail, multi-client sync | + +--- + +## Public API + +### `add_ambient_endpoints(app, bridge)` + +The primary integration point. Follows the `add_langgraph_fastapi_endpoint` pattern — adds routes to YOUR app, doesn't create one. + +```python +from ambient_runner import add_ambient_endpoints +from ambient_runner.bridges.claude import ClaudeBridge + +app = FastAPI() +add_ambient_endpoints(app, bridge=ClaudeBridge()) +``` + +### Adding a new framework + +1. Implement `PlatformBridge` (2 methods: `create_adapter` + `capabilities`) +2. Optionally implement framework-specific middleware +3. Call `add_ambient_endpoints(app, bridge=YourBridge())` + +```python +from ambient_runner import add_ambient_endpoints, PlatformBridge, PlatformContext, FrameworkCapabilities + +class MyBridge(PlatformBridge): + def capabilities(self): + return FrameworkCapabilities(file_system=False, mcp=False) + + def create_adapter(self, ctx): + return MyAdapter(model=ctx.model, prompt=ctx.system_prompt_append) + +app = FastAPI() +add_ambient_endpoints(app, bridge=MyBridge()) +``` + +--- + +## Implementation Phases + +### Phase 1: Foundation (current PR) +- [x] Vendored `ag_ui_claude_sdk` package +- [x] Clean adapter as functions module (not class) +- [x] Split modules: `auth.py`, `workspace.py`, `prompts.py`, `mcp.py` +- [x] Split endpoints: `endpoints/repos.py`, `endpoints/workflow.py`, `endpoints/feedback.py`, `endpoints/mcp_status.py` +- [x] Observability tracks AG-UI events (not SDK messages) +- [x] Langfuse trace ID emitted once per run + +### Phase 2: Tracing middleware +- [ ] Move `obs.track_agui_event()` into a proper AG-UI middleware class +- [ ] Trace ID injected into `RUN_STARTED` metadata (not separate RawEvent) +- [ ] Developer events for setup lifecycle (role="developer") + +### Phase 3: Capabilities endpoint +- [ ] `GET /capabilities` returns framework + platform features +- [ ] Frontend reads capabilities and shows/hides UI features +- [ ] Conditional endpoint registration based on capabilities + +### Phase 4: `ambient_runner` package extraction +- [ ] Extract into standalone package: `ambient_runner` +- [ ] `add_ambient_endpoints(app, bridge)` as public API +- [ ] `PlatformBridge` ABC and `PlatformContext`/`FrameworkCapabilities` types +- [ ] `ClaudeBridge` as first implementation +- [ ] Current `main.py` becomes a thin consumer + +### Phase 5: Adapter persistence +- [ ] Adapter created once, reused across runs +- [ ] Rebuild only on config change (workflow, repo) +- [ ] Session resume via bridge + `.claude/` S3 sync + +### Phase 6: Backend consumes MESSAGES_SNAPSHOT +- [ ] Backend proxy intercepts `MESSAGES_SNAPSHOT` events +- [ ] Persists to DB directly (no separate compaction) +- [ ] On session resume, loads from DB into `RunAgentInput.messages` +- [ ] Remove duplicated compaction logic from backend + +### Phase 7: CopilotKit frontend adoption +- [ ] Replace custom `useAGUIStream` hook with CopilotKit's `useCopilotChat` +- [ ] Use `useCopilotAction` for human-in-the-loop tools +- [ ] MetaEvents for feedback UI state + +### Phase 8: Second framework (LangGraph) +- [ ] `LangGraphBridge` implementation +- [ ] Validates the abstraction works across frameworks +- [ ] Different capabilities (no file_system, no MCP) +- [ ] Frontend adapts UI based on capabilities + +--- + +## Consequences + +### Benefits +- **Framework-agnostic**: Any AG-UI adapter plugs in via the bridge pattern +- **Clean separation**: Platform concerns (auth, repos, workflows) are isolated from protocol translation +- **Standards-based**: Uses AG-UI events, middleware, and roles as designed +- **No double work**: MESSAGES_SNAPSHOT eliminates duplicate compaction +- **Extensible**: New frameworks = implement 2 methods, get all platform features + +### Trade-offs +- **Bridge per framework**: Each new framework needs a bridge implementation (but it's just 2 methods) +- **MetaEvent is draft spec**: Feedback via MetaEvent depends on AG-UI finalising the draft +- **Adapter reuse**: Need to handle config changes carefully (rebuild vs reuse) + +### Risks +- AG-UI protocol is still evolving (MetaEvents, Interrupts are drafts) +- CopilotKit adoption is a frontend-wide change +- LangGraph bridge may surface abstraction gaps not visible with Claude SDK alone + +--- + +## References + +- [AG-UI Protocol — Events](https://docs.ag-ui.com/concepts/events) +- [AG-UI Protocol — Middleware](https://docs.ag-ui.com/concepts/middleware) +- [AG-UI Dojo — Feature Registry (menu.ts)](https://github.com/ag-ui-protocol/ag-ui/blob/main/apps/dojo/src/menu.ts) +- [AG-UI LangGraph Integration](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/langgraph) +- [AG-UI Claude Agent SDK Integration](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/claude-agent-sdk) +- [ADR-0004: Go Backend, Python Runner](../adr/0004-go-backend-python-runner.md) +- [ADR-0001: Kubernetes-Native Architecture](../adr/0001-kubernetes-native-architecture.md) From 4e046ec7c9e7e8703f2cd40d71ffe9e26948fed8 Mon Sep 17 00:00:00 2001 From: Gage Krumbach Date: Wed, 11 Feb 2026 08:02:33 -0600 Subject: [PATCH 2/2] fix: correct event type constant and enhance message handling - Fixed a typo in the event type constant from `EventTypStateDelta` to `EventTypeStateDelta`. - Added a new event type constant `EventTypeCustom` for platform extensions. - Refactored message extraction logic from snapshots to improve handling of messages from persisted snapshots. - Removed the deprecated `loadCompactedMessages` function and updated the event streaming logic to utilize persisted message snapshots for better performance and reliability. These changes enhance the overall stability and functionality of the AG-UI event handling system. --- components/backend/types/agui.go | 5 +- components/backend/websocket/agui.go | 217 +++-- components/backend/websocket/compaction.go | 420 --------- .../[name]/sessions/[sessionName]/page.tsx | 622 +------------ .../src/components/session/MessagesTab.tsx | 743 --------------- .../frontend/src/hooks/use-agui-stream.ts | 867 ------------------ .../runners/claude-code-runner/README.md | 41 +- .../ambient_runner/endpoints/capabilities.py | 24 +- .../ambient_runner/endpoints/feedback.py | 12 +- .../ambient_runner/endpoints/mcp_status.py | 10 +- .../ambient_runner/endpoints/repos.py | 11 +- .../ambient_runner/endpoints/workflow.py | 10 +- .../ambient_runner/middleware/__init__.py | 15 +- components/runners/claude-code-runner/main.py | 52 +- .../0006-ambient-runner-sdk-architecture.md | 52 +- 15 files changed, 277 insertions(+), 2824 deletions(-) delete mode 100644 components/backend/websocket/compaction.go delete mode 100644 components/frontend/src/components/session/MessagesTab.tsx delete mode 100644 components/frontend/src/hooks/use-agui-stream.ts diff --git a/components/backend/types/agui.go b/components/backend/types/agui.go index 457c3d595..bd55bdcb4 100644 --- a/components/backend/types/agui.go +++ b/components/backend/types/agui.go @@ -44,7 +44,7 @@ const ( // State management events EventTypeStateSnapshot = "STATE_SNAPSHOT" - EventTypStateDelta = "STATE_DELTA" + EventTypeStateDelta = "STATE_DELTA" // Message snapshot for restore/reconnect EventTypeMessagesSnapshot = "MESSAGES_SNAPSHOT" @@ -56,6 +56,9 @@ const ( // Raw event for pass-through EventTypeRaw = "RAW" + // Custom event for platform extensions + EventTypeCustom = "CUSTOM" + // META event for user feedback (thumbs up/down) // See: https://docs.ag-ui.com/drafts/meta-events EventTypeMeta = "META" diff --git a/components/backend/websocket/agui.go b/components/backend/websocket/agui.go index d44cf50d4..cccee3534 100644 --- a/components/backend/websocket/agui.go +++ b/components/backend/websocket/agui.go @@ -250,10 +250,76 @@ func loadPersistedSnapshot(sessionID string) map[string]interface{} { return event } -// loadCompactedMessages loads pre-compacted messages from completed runs -// NOTE: Removed loadCompactedMessages and compactAndPersistRun functions. -// We now use "compact-on-read" strategy in streamThreadEvents. -// This eliminates race conditions, dual-file complexity, and async compaction issues. +// messagesFromSnapshot extracts typed Message objects from a raw +// MESSAGES_SNAPSHOT event (as persisted on disk or received from the runner). +// Returns nil if the event is nil or has no messages. +func messagesFromSnapshot(snapshot map[string]interface{}) []types.Message { + if snapshot == nil { + return nil + } + msgs, ok := snapshot["messages"].([]interface{}) + if !ok || len(msgs) == 0 { + return nil + } + + result := make([]types.Message, 0, len(msgs)) + for _, m := range msgs { + msgMap, ok := m.(map[string]interface{}) + if !ok { + continue + } + msg := types.Message{} + if id, ok := msgMap["id"].(string); ok { + msg.ID = id + } + if role, ok := msgMap["role"].(string); ok { + msg.Role = role + } + if content, ok := msgMap["content"].(string); ok { + msg.Content = content + } + if ts, ok := msgMap["timestamp"].(string); ok { + msg.Timestamp = ts + } + if toolCalls, ok := msgMap["toolCalls"].([]interface{}); ok { + msg.ToolCalls = make([]types.ToolCall, 0, len(toolCalls)) + for _, tc := range toolCalls { + tcMap, ok := tc.(map[string]interface{}) + if !ok { + continue + } + toolCall := types.ToolCall{} + if id, ok := tcMap["id"].(string); ok { + toolCall.ID = id + } + if name, ok := tcMap["name"].(string); ok { + toolCall.Name = name + } + if args, ok := tcMap["args"].(string); ok { + toolCall.Args = args + } + if t, ok := tcMap["type"].(string); ok { + toolCall.Type = t + } + if pid, ok := tcMap["parentToolUseId"].(string); ok { + toolCall.ParentToolUseID = pid + } + if r, ok := tcMap["result"].(string); ok { + toolCall.Result = r + } + if s, ok := tcMap["status"].(string); ok { + toolCall.Status = s + } + if e, ok := tcMap["error"].(string); ok { + toolCall.Error = e + } + msg.ToolCalls = append(msg.ToolCalls, toolCall) + } + } + result = append(result, msg) + } + return result +} // persistAGUIEventMap persists a map[string]interface{} event to disk func persistAGUIEventMap(sessionID, runID string, event map[string]interface{}) { @@ -373,114 +439,60 @@ func streamThreadEvents(c *gin.Context, projectName, sessionName string) { close(eventCh) }() - // OPTION 1: Compact-on-Read Strategy (COMPLETED RUNS ONLY) - // Load events from agui-events.jsonl and compact only COMPLETED runs - // Active/in-progress runs will be streamed raw - - // Declare outside so it's accessible later for replaying active runs + // Use persisted MESSAGES_SNAPSHOT for fast reconnect. + // The runner emits MESSAGES_SNAPSHOT at the end of each run with + // the complete conversation history — no compaction needed. + // + // Declare activeRunIDs outside so it's accessible for replaying active runs. activeRunIDs := make(map[string]bool) + // Determine which runs are active from in-memory state + event log events, err := loadEventsForRun(sessionName, "") if err == nil && len(events) > 0 { - - // CRITICAL FIX: Determine which runs are TRULY active by checking event log - // A run is only active if NO terminal event exists in the log runHasTerminalEvent := make(map[string]bool) for _, event := range events { - eventRunID, ok := event["runId"].(string) - if !ok { - continue - } - eventType, ok := event["type"].(string) - if !ok { - continue - } - + eventRunID, _ := event["runId"].(string) + eventType, _ := event["type"].(string) if eventRunID != "" && isTerminalEventType(eventType) { runHasTerminalEvent[eventRunID] = true } } - // Check in-memory state and override with event log truth - // Also fix stale in-memory state aguiRunsMu.Lock() for _, state := range aguiRuns { if state.SessionID == sessionName { runID := state.RunID - // Only consider active if NO terminal event in log if !runHasTerminalEvent[runID] { activeRunIDs[runID] = true - } else { - // Fix stale memory state - if state.Status == "running" { - state.Status = "completed" - } + } else if state.Status == "running" { + state.Status = "completed" } } } aguiRunsMu.Unlock() + } else if err != nil { + log.Printf("AGUI: Failed to load events: %v", err) + } - // Filter to only events from COMPLETED runs (have terminal event) - // Also collect session-level META events (feedback, etc.) which may not have runId - completedEvents := make([]map[string]interface{}, 0) - sessionMetaEvents := make([]map[string]interface{}, 0) - skippedCount := 0 - for _, event := range events { - eventType, _ := event["type"].(string) - eventRunID, ok := event["runId"].(string) - - // META events may not have runId (session-level feedback) - collect them separately - if eventType == types.EventTypeMeta { - sessionMetaEvents = append(sessionMetaEvents, event) - continue - } - - if !ok { - continue - } - - // Skip events without runId - if eventRunID == "" { - skippedCount++ - continue - } - - // Skip events from active runs (no terminal event yet) - if activeRunIDs[eventRunID] { - skippedCount++ - continue - } - - // Include events from completed runs - completedEvents = append(completedEvents, event) - } - - if len(completedEvents) > 0 { - // Compact only completed run events - messages := CompactEvents(completedEvents) - - // Send single MESSAGES_SNAPSHOT with compacted messages from COMPLETED runs - if len(messages) > 0 { - snapshot := &types.MessagesSnapshotEvent{ - BaseEvent: types.NewBaseEvent(types.EventTypeMessagesSnapshot, threadID, "thread-snapshot"), - Messages: messages, - } - writeSSEEvent(c.Writer, snapshot) - c.Writer.(http.Flusher).Flush() - } + // Send persisted MESSAGES_SNAPSHOT for completed conversation history + persistedSnapshot := loadPersistedSnapshot(sessionName) + if messages := messagesFromSnapshot(persistedSnapshot); len(messages) > 0 { + snapshot := &types.MessagesSnapshotEvent{ + BaseEvent: types.NewBaseEvent(types.EventTypeMessagesSnapshot, threadID, "thread-snapshot"), + Messages: messages, } + writeSSEEvent(c.Writer, snapshot) + c.Writer.(http.Flusher).Flush() + } - // Replay ALL session META events (feedback, tags, annotations) - // META events are session-level and not part of MESSAGES_SNAPSHOT - // They must be replayed regardless of runId to survive reconnects - if len(sessionMetaEvents) > 0 { - for _, event := range sessionMetaEvents { + // Replay session META events (feedback, annotations) — not in MESSAGES_SNAPSHOT + if events != nil { + for _, event := range events { + if eventType, _ := event["type"].(string); eventType == types.EventTypeMeta { writeSSEEvent(c.Writer, event) } - c.Writer.(http.Flusher).Flush() } - } else if err != nil { - log.Printf("AGUI: Failed to load events: %v", err) + c.Writer.(http.Flusher).Flush() } // Replay ALL active runs (not just most recent) @@ -691,23 +703,15 @@ func sendInitialSyncEvents(c *gin.Context, runState *AGUIRunState, projectName, // 2. Send basic state snapshot (always succeeds) sendBasicStateSnapshot(c, runState, projectName, sessionName) - // 3. Compact stored events and send MESSAGES_SNAPSHOT - // Per AG-UI spec: compact at read-time, not write-time - events, err := loadEventsForRun(sessionName, runID) - if err != nil { - log.Printf("AGUI: Failed to load events for %s: %v", sessionName, err) - } - - if len(events) > 0 { - messages := CompactEvents(events) - - if len(messages) > 0 { - snapshot := &types.MessagesSnapshotEvent{ - BaseEvent: types.NewBaseEvent(types.EventTypeMessagesSnapshot, threadID, runID), - Messages: messages, - } - writeSSEEvent(c.Writer, snapshot) + // 3. Send persisted MESSAGES_SNAPSHOT for conversation history + // The runner emits MESSAGES_SNAPSHOT at the end of each run — use it directly. + persistedSnapshot := loadPersistedSnapshot(sessionName) + if messages := messagesFromSnapshot(persistedSnapshot); len(messages) > 0 { + snapshot := &types.MessagesSnapshotEvent{ + BaseEvent: types.NewBaseEvent(types.EventTypeMessagesSnapshot, threadID, runID), + Messages: messages, } + writeSSEEvent(c.Writer, snapshot) } } @@ -1007,14 +1011,9 @@ func HandleAGUIHistory(c *gin.Context) { } runID := c.Query("runId") - // Compact events to messages - var messages []types.Message - if runID != "" { - events, err := loadEventsForRun(sessionName, runID) - if err == nil { - messages = CompactEvents(events) - } - } + // Load messages from persisted MESSAGES_SNAPSHOT (no compaction needed) + persistedSnapshot := loadPersistedSnapshot(sessionName) + messages := messagesFromSnapshot(persistedSnapshot) // Get runs for this session runs := getRunsForSession(sessionName) diff --git a/components/backend/websocket/compaction.go b/components/backend/websocket/compaction.go deleted file mode 100644 index de94dc1bc..000000000 --- a/components/backend/websocket/compaction.go +++ /dev/null @@ -1,420 +0,0 @@ -package websocket - -import ( - "ambient-code-backend/types" - "log" - - "github.com/google/uuid" -) - -// MessageCompactor compacts AG-UI events into message snapshots -// Per AG-UI spec: https://docs.ag-ui.com/concepts/serialization -type MessageCompactor struct { - messages []types.Message - currentMessage *types.Message - activeToolCalls map[string]*ActiveToolCall // toolId -> tool state - hiddenMessages map[string]bool // messageId -> hidden flag -} - -// ActiveToolCall tracks an in-progress tool call -type ActiveToolCall struct { - ID string - Name string - Args string // Accumulated from TOOL_CALL_ARGS deltas - ParentToolUseID string - Status string -} - -// NewMessageCompactor creates a new message compactor -func NewMessageCompactor() *MessageCompactor { - return &MessageCompactor{ - messages: make([]types.Message, 0), - activeToolCalls: make(map[string]*ActiveToolCall), - hiddenMessages: make(map[string]bool), - } -} - -// HandleEvent processes a single AG-UI event and updates compacted state -func (c *MessageCompactor) HandleEvent(event map[string]interface{}) { - eventType, _ := event["type"].(string) - - switch eventType { - case types.EventTypeTextMessageStart: - c.handleTextMessageStart(event) - case types.EventTypeTextMessageContent: - c.handleTextMessageContent(event) - case types.EventTypeTextMessageEnd: - c.handleTextMessageEnd(event) - case types.EventTypeToolCallStart: - c.handleToolCallStart(event) - case types.EventTypeToolCallArgs: - c.handleToolCallArgs(event) - case types.EventTypeToolCallEnd: - c.handleToolCallEnd(event) - case types.EventTypeRaw: - c.handleRawEvent(event) - case types.EventTypeMessagesSnapshot: - c.handleMessagesSnapshot(event) - case types.EventTypeRunStarted, types.EventTypeRunFinished, types.EventTypeRunError: - // Lifecycle events - skip, don't affect message compaction - case types.EventTypeStepStarted, types.EventTypeStepFinished: - // Step events - skip, don't affect message compaction - case types.EventTypeStateSnapshot, types.EventTypStateDelta: - // State events - skip, don't affect message compaction - case types.EventTypeActivitySnapshot, types.EventTypeActivityDelta: - // Activity events - skip, don't affect message compaction - default: - log.Printf("Compaction: WARNING - Unhandled event type: %s", eventType) - } -} - -// GetMessages returns the compacted messages (excluding hidden ones) -func (c *MessageCompactor) GetMessages() []types.Message { - // Flush any active message - if c.currentMessage != nil { - c.messages = append(c.messages, *c.currentMessage) - c.currentMessage = nil - } - - // DO NOT include in-progress tools in snapshots! - // Snapshots should only contain COMPLETED runs with finished tool calls. - // In-progress tools will be streamed as raw events from the active run. - // - // If we included "running" status tools here, they would duplicate when - // the active run's TOOL_CALL_END events are replayed. - if len(c.activeToolCalls) > 0 { - // Clear activeToolCalls - don't include them in snapshot - c.activeToolCalls = make(map[string]*ActiveToolCall) - } - - // Filter out hidden messages (auto-sent initial/workflow prompts) - visibleMessages := make([]types.Message, 0, len(c.messages)) - hiddenCount := 0 - for _, msg := range c.messages { - if c.hiddenMessages[msg.ID] { - hiddenCount++ - continue - } - visibleMessages = append(visibleMessages, msg) - } - - return visibleMessages -} - -// Event Handlers - -func (c *MessageCompactor) handleTextMessageStart(event map[string]interface{}) { - // Flush previous message if any - if c.currentMessage != nil { - c.messages = append(c.messages, *c.currentMessage) - } - - // Handle both camelCase and snake_case - messageID, _ := event["messageId"].(string) - if messageID == "" { - messageID, _ = event["message_id"].(string) - } - role, _ := event["role"].(string) - if role == "" { - role = types.RoleAssistant - } - // Preserve timestamp from the event - timestamp, _ := event["timestamp"].(string) - - c.currentMessage = &types.Message{ - ID: messageID, - Role: role, - Content: "", - Timestamp: timestamp, - } -} - -func (c *MessageCompactor) handleTextMessageContent(event map[string]interface{}) { - if c.currentMessage == nil { - return - } - - delta, _ := event["delta"].(string) - c.currentMessage.Content += delta -} - -func (c *MessageCompactor) handleTextMessageEnd(event map[string]interface{}) { - if c.currentMessage != nil { - // User messages never have tool calls - flush immediately - // Assistant messages might have tool calls - keep open - // We'll flush when a new TEXT_MESSAGE_START arrives or at the end of compaction - if c.currentMessage.Role == types.RoleUser { - c.messages = append(c.messages, *c.currentMessage) - c.currentMessage = nil - } - } -} - -func (c *MessageCompactor) handleToolCallStart(event map[string]interface{}) { - // Handle both camelCase (TypeScript) and snake_case (Python ag_ui.core) - toolID, _ := event["toolCallId"].(string) - if toolID == "" { - toolID, _ = event["tool_call_id"].(string) - } - toolName, _ := event["toolCallName"].(string) - if toolName == "" { - toolName, _ = event["tool_call_name"].(string) - } - - // Try multiple field names for parent tool ID - parentToolUseID, _ := event["parentToolUseId"].(string) - if parentToolUseID == "" { - parentToolUseID, _ = event["parentToolUseID"].(string) - } - if parentToolUseID == "" { - parentToolUseID, _ = event["parent_tool_call_id"].(string) - } - - if toolID != "" { - c.activeToolCalls[toolID] = &ActiveToolCall{ - ID: toolID, - Name: toolName, - Args: "", - ParentToolUseID: parentToolUseID, - Status: "running", - } - } -} - -func (c *MessageCompactor) handleToolCallArgs(event map[string]interface{}) { - // Handle both camelCase and snake_case - toolID, _ := event["toolCallId"].(string) - if toolID == "" { - toolID, _ = event["tool_call_id"].(string) - } - delta, _ := event["delta"].(string) - - if toolID == "" { - return - } - - if active, ok := c.activeToolCalls[toolID]; ok { - active.Args += delta - } -} - -func (c *MessageCompactor) handleToolCallEnd(event map[string]interface{}) { - // Handle both camelCase and snake_case - toolID, _ := event["toolCallId"].(string) - if toolID == "" { - toolID, _ = event["tool_call_id"].(string) - } - result, _ := event["result"].(string) - errorStr, _ := event["error"].(string) - - if toolID == "" { - return - } - - active, ok := c.activeToolCalls[toolID] - if !ok { - return - } - - // Create completed tool call - tc := types.ToolCall{ - ID: active.ID, - Name: active.Name, - Args: active.Args, - Type: "function", - ParentToolUseID: active.ParentToolUseID, - Result: result, - Status: "completed", - } - if errorStr != "" { - tc.Error = errorStr - tc.Status = "error" - } - - // Preserve timestamp from the event - timestamp, _ := event["timestamp"].(string) - - // Add to message - // Check if we need to create a new message or add to current - if c.currentMessage != nil && c.currentMessage.Role == types.RoleAssistant { - // Add to current message - c.currentMessage.ToolCalls = append(c.currentMessage.ToolCalls, tc) - // Update timestamp if not already set - if c.currentMessage.Timestamp == "" && timestamp != "" { - c.currentMessage.Timestamp = timestamp - } - } else { - // Create new message for this tool call - c.messages = append(c.messages, types.Message{ - ID: uuid.New().String(), - Role: types.RoleAssistant, - ToolCalls: []types.ToolCall{tc}, - Timestamp: timestamp, - }) - } - - // Remove from active - delete(c.activeToolCalls, toolID) -} - -func (c *MessageCompactor) handleRawEvent(event map[string]interface{}) { - // Check for both "data" and "event" fields (AG-UI uses "event") - var data map[string]interface{} - if d, ok := event["event"].(map[string]interface{}); ok { - data = d - } else if d, ok := event["data"].(map[string]interface{}); ok { - data = d - } else { - return - } - - // Handle message_metadata events (for hiding auto-sent prompts) - if msgType, _ := data["type"].(string); msgType == "message_metadata" { - if hidden, _ := data["hidden"].(bool); hidden { - if messageID, ok := data["messageId"].(string); ok { - c.hiddenMessages[messageID] = true - } - } - return - } - - role, _ := data["role"].(string) - if role == "" { - return - } - - // Flush current message - if c.currentMessage != nil { - c.messages = append(c.messages, *c.currentMessage) - c.currentMessage = nil - } - - // Add raw message - msg := types.Message{Role: role} - if id, ok := data["id"].(string); ok { - msg.ID = id - } - if content, ok := data["content"].(string); ok { - msg.Content = content - } - if timestamp, ok := data["timestamp"].(string); ok { - msg.Timestamp = timestamp - } - - c.messages = append(c.messages, msg) -} - -func (c *MessageCompactor) handleMessagesSnapshot(event map[string]interface{}) { - // If runner sends MESSAGES_SNAPSHOT, use it directly (overrides compaction) - msgs, ok := event["messages"].([]interface{}) - if !ok { - return - } - - // Replace all messages with snapshot - c.messages = make([]types.Message, 0, len(msgs)) - c.currentMessage = nil - - for _, m := range msgs { - msgMap, ok := m.(map[string]interface{}) - if !ok { - continue - } - - msg := types.Message{} - if id, ok := msgMap["id"].(string); ok { - msg.ID = id - } - if role, ok := msgMap["role"].(string); ok { - msg.Role = role - } - if content, ok := msgMap["content"].(string); ok { - msg.Content = content - } - if timestamp, ok := msgMap["timestamp"].(string); ok { - msg.Timestamp = timestamp - } - - // Extract toolCalls array - if toolCalls, ok := msgMap["toolCalls"].([]interface{}); ok { - msg.ToolCalls = make([]types.ToolCall, 0, len(toolCalls)) - for _, tc := range toolCalls { - tcMap, ok := tc.(map[string]interface{}) - if !ok { - continue - } - - toolCall := types.ToolCall{} - if id, ok := tcMap["id"].(string); ok { - toolCall.ID = id - } - if name, ok := tcMap["name"].(string); ok { - toolCall.Name = name - } - if args, ok := tcMap["args"].(string); ok { - toolCall.Args = args - } - if tcType, ok := tcMap["type"].(string); ok { - toolCall.Type = tcType - } - if parentID, ok := tcMap["parentToolUseId"].(string); ok { - toolCall.ParentToolUseID = parentID - } - if result, ok := tcMap["result"].(string); ok { - toolCall.Result = result - } - if status, ok := tcMap["status"].(string); ok { - toolCall.Status = status - } - if errorStr, ok := tcMap["error"].(string); ok { - toolCall.Error = errorStr - } - - msg.ToolCalls = append(msg.ToolCalls, toolCall) - } - } - - c.messages = append(c.messages, msg) - } - -} - -// CompactEvents is the main entry point for event compaction. -// -// Optimisation: if the event stream contains MESSAGES_SNAPSHOT events -// (sent by the AG-UI adapter), we skip to the LAST snapshot and only -// compact events that come after it. This avoids replaying the entire -// event log on reconnect. -func CompactEvents(events []map[string]interface{}) []types.Message { - - // Find the index of the last MESSAGES_SNAPSHOT event. - // Everything before it is already accounted for in that snapshot. - lastSnapshotIdx := -1 - for i := len(events) - 1; i >= 0; i-- { - if et, _ := events[i]["type"].(string); et == types.EventTypeMessagesSnapshot { - lastSnapshotIdx = i - break - } - } - - compactor := NewMessageCompactor() - - if lastSnapshotIdx >= 0 { - // Fast path: start from the snapshot, then replay only subsequent events - compactor.HandleEvent(events[lastSnapshotIdx]) - for _, event := range events[lastSnapshotIdx+1:] { - compactor.HandleEvent(event) - } - log.Printf("Compaction: fast-path via MESSAGES_SNAPSHOT at index %d (%d events skipped)", - lastSnapshotIdx, lastSnapshotIdx) - } else { - // Fallback: replay all events - for _, event := range events { - compactor.HandleEvent(event) - } - } - - messages := compactor.GetMessages() - - return messages -} diff --git a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx index a64aadc8c..44db2f3a8 100644 --- a/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx +++ b/components/frontend/src/app/projects/[name]/sessions/[sessionName]/page.tsx @@ -28,7 +28,6 @@ import { useRouter } from "next/navigation"; import { cn } from "@/lib/utils"; // Custom components -import MessagesTab from "@/components/session/MessagesTab"; import { CopilotChatPanel } from "@/components/session/CopilotChatPanel"; import { FileTree, type FileTreeNode } from "@/components/file-tree"; @@ -77,11 +76,7 @@ import { useFileOperations } from "./hooks/use-file-operations"; import { useSessionQueue } from "@/hooks/use-session-queue"; import type { DirectoryOption, DirectoryRemote } from "./lib/types"; -import type { MessageObject, ToolUseMessages, HierarchicalToolMessage, ReconciledRepo, SessionRepo } from "@/types/agentic-session"; -import type { AGUIToolCall } from "@/types/agui"; - -// AG-UI streaming -import { useAGUIStream } from "@/hooks/use-agui-stream"; +import type { ReconciledRepo, SessionRepo } from "@/types/agentic-session"; // React Query hooks import { @@ -110,33 +105,15 @@ import { FeedbackProvider } from "@/contexts/FeedbackContext"; // Wait 1 second after last tool completion to batch rapid writes together // Prevents excessive API calls during burst writes (e.g., when Claude creates multiple files in quick succession) // Testing: 500ms was too aggressive (hit API rate limits), 2000ms felt sluggish to users -const ARTIFACTS_DEBOUNCE_MS = 1000; + // Wait 2 seconds after session completes before final artifact refresh // Backend can take 1-2 seconds to flush final artifacts to storage // Ensures users see all artifacts even if final writes occur after status transition const COMPLETION_DELAY_MS = 2000; -/** - * Type guard to check if a message is a completed ToolUseMessages with result. - * Extracted for testability and proper validation. - * Uses proper type assertion and validation. - */ -function isCompletedToolUseMessage(msg: MessageObject | ToolUseMessages): msg is ToolUseMessages { - if (msg.type !== "tool_use_messages") { - return false; - } - - // Cast to ToolUseMessages for proper type checking - const toolMsg = msg as ToolUseMessages; - - return ( - toolMsg.resultBlock !== undefined && - toolMsg.resultBlock !== null && - typeof toolMsg.resultBlock === "object" && - toolMsg.resultBlock.content !== null - ); -} +// NOTE: isCompletedToolUseMessage type guard removed — was only used by the +// old streamMessages useMemo. CopilotKit handles tool rendering now. export default function ProjectSessionDetailPage({ params, @@ -146,7 +123,6 @@ export default function ProjectSessionDetailPage({ const router = useRouter(); const [projectName, setProjectName] = useState(""); const [sessionName, setSessionName] = useState(""); - const [chatInput, setChatInput] = useState(""); const [backHref, setBackHref] = useState(null); const [openAccordionItems, setOpenAccordionItems] = useState([]); const [contextModalOpen, setContextModalOpen] = useState(false); @@ -225,78 +201,8 @@ export default function ProjectSessionDetailPage({ ); // Track the current Langfuse trace ID for feedback association + // CopilotKit receives this via CustomEvent from the tracing middleware const [langfuseTraceId, setLangfuseTraceId] = useState(null); - - // AG-UI streaming hook - replaces useSessionMessages and useSendChatMessage - // Note: autoConnect is intentionally false to avoid SSR hydration mismatch - // Connection is triggered manually in useEffect after client hydration - const aguiStream = useAGUIStream({ - projectName: projectName || "", - sessionName: sessionName || "", - autoConnect: false, // Manual connection after hydration - onError: (err) => console.error("AG-UI stream error:", err), - onTraceId: (traceId) => setLangfuseTraceId(traceId), // Capture Langfuse trace ID for feedback - }); - const aguiState = aguiStream.state; - const aguiSendMessage = aguiStream.sendMessage; - const aguiInterrupt = aguiStream.interrupt; - const isRunActive = aguiStream.isRunActive; - const aguiConnectRef = useRef(aguiStream.connect); - - // Keep connect ref up to date - useEffect(() => { - aguiConnectRef.current = aguiStream.connect; - }, [aguiStream.connect]); - - // Connect to AG-UI event stream for history and live updates - // AG-UI pattern: GET /agui/events streams ALL thread events (past + future) - // POST /agui/run creates runs, events broadcast to GET subscribers - const hasConnectedRef = useRef(false); - const disconnectRef = useRef(aguiStream.disconnect); - - // Keep disconnect ref up to date without triggering re-renders - useEffect(() => { - disconnectRef.current = aguiStream.disconnect; - }, [aguiStream.disconnect]); - - useEffect(() => { - if (!projectName || !sessionName) return; - - // Connect once on mount and keep connection open - if (!hasConnectedRef.current) { - hasConnectedRef.current = true; - aguiConnectRef.current(); - } - - // CRITICAL: Disconnect when navigating away to prevent hung connections - return () => { - console.log('[Session Detail] Unmounting, disconnecting AG-UI stream'); - disconnectRef.current(); - hasConnectedRef.current = false; - }; - // NOTE: Only depend on projectName and sessionName - NOT aguiStream - // aguiStream is an object that changes every render, which would cause infinite reconnects - }, [projectName, sessionName]); - - // Auto-send initial prompt (handles session start, workflow activation, restarts) - // AG-UI pattern: Client (or backend) initiates runs via POST /agui/run - const lastProcessedPromptRef = useRef(""); - - useEffect(() => { - if (!session || !aguiSendMessage) return; - - const initialPrompt = session?.spec?.initialPrompt; - - // NOTE: Initial prompt execution handled by backend auto-trigger (StartSession handler) - // Backend waits for subscriber before executing, ensuring events are received - // This works for both UI and headless/API usage - - // Track that we've seen this prompt (for workflow changes) - if (initialPrompt && lastProcessedPromptRef.current !== initialPrompt) { - lastProcessedPromptRef.current = initialPrompt; - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [session?.spec?.initialPrompt, session?.status?.phase, aguiState.messages.length, aguiState.status]); // Workflow management hook const workflowManagement = useWorkflowManagement({ @@ -360,30 +266,8 @@ export default function ProjectSessionDetailPage({ return () => clearInterval(pollInterval); }, [sessionQueue.messages, session?.status?.phase, refetchSession]); - // Process queued messages when session becomes Running - useEffect(() => { - const phase = session?.status?.phase; - const unsentMessages = sessionQueue.messages.filter(m => !m.sentAt); - - if (phase === "Running" && unsentMessages.length > 0) { - // Session is now running, send all queued messages - const processMessages = async () => { - for (const messageItem of unsentMessages) { - try { - await aguiSendMessage(messageItem.content); - sessionQueue.markMessageSent(messageItem.id); - // Small delay between messages to avoid overwhelming the system - await new Promise(resolve => setTimeout(resolve, 100)); - } catch (err) { - errorToast(err instanceof Error ? err.message : "Failed to send queued message"); - } - } - }; - - processMessages(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [session?.status?.phase, sessionQueue.messages.length]); + // Note: Message sending is handled by CopilotKit's chat component. + // Queued messages are cleared when session becomes Running and CopilotKit connects. // Repo management mutations const addRepoMutation = useMutation({ @@ -644,7 +528,7 @@ export default function ProjectSessionDetailPage({ // Note: userHasInteracted is only set when: // 1. User explicitly selects a workflow (handleWelcomeWorkflowSelect -> onUserInteraction) - // 2. User sends a message (sendChat sets it to true) + // 2. User sends a message via CopilotKit chat // It should NOT be set automatically when backend messages arrive // Load remotes from session annotations (one-time initialization) @@ -733,399 +617,22 @@ export default function ProjectSessionDetailPage({ handleWorkflowChange(workflowId); }; - // Convert AG-UI messages to display format with hierarchical tool call rendering - const streamMessages: Array = useMemo(() => { - - // Helper function to parse tool arguments - const parseToolArgs = (args: string | undefined): Record => { - if (!args) return {}; - try { - const parsed = JSON.parse(args); - if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { - return parsed as Record; - } - return { value: parsed }; - } catch { - return { _raw: String(args || '') }; - } - }; - - // Helper function to create a tool message from a tool call - const createToolMessage = ( - tc: AGUIToolCall, - timestamp: string - ): ToolUseMessages => { - const toolInput = parseToolArgs(tc.args); - return { - type: "tool_use_messages", - timestamp, - toolUseBlock: { - type: "tool_use_block", - id: tc.id, - name: tc.name, - input: toolInput, - }, - resultBlock: { - type: "tool_result_block", - tool_use_id: tc.id, - content: tc.result || null, - is_error: tc.status === "error", - }, - }; - }; - - const result: Array = []; - - // Phase A: Collect all tool calls from all messages for hierarchy building - const allToolCalls = new Map(); - - for (const msg of aguiState.messages) { - // Use msg.timestamp from backend, fallback to current time for legacy messages - // Note: After backend fix, new messages will have proper timestamps - const timestamp = msg.timestamp || new Date().toISOString(); - - if (msg.toolCalls && Array.isArray(msg.toolCalls)) { - for (const tc of msg.toolCalls) { - if (tc && tc.id && tc.name) { - allToolCalls.set(tc.id, { tc, timestamp }); - } - } - } - } - - // Add currently streaming tool call to the map if present - // This ensures streaming tools (both parents and children) are included in hierarchy - // CRITICAL: Don't require name - add even if name is null to prevent orphaned children - if (aguiState.currentToolCall?.id) { - const streamingToolId = aguiState.currentToolCall.id; - const streamingParentId = aguiState.currentToolCall.parentToolUseId; - const toolName = aguiState.currentToolCall.name || "unknown_tool"; // Default if null - - // Create a pseudo-tool-call for the streaming tool - const streamingTC: AGUIToolCall = { - id: streamingToolId, - name: toolName, - args: aguiState.currentToolCall.args || "", - type: "function", - parentToolUseId: streamingParentId, - status: "running", - }; - - if (!allToolCalls.has(streamingToolId)) { - allToolCalls.set(streamingToolId, { - tc: streamingTC, - // Use timestamp from currentToolCall if available, fallback to current time for legacy - timestamp: aguiState.pendingToolCalls?.get(streamingToolId)?.timestamp || new Date().toISOString() - }); - } - } - - // Add pending children to render map so they show during streaming! - // These are children that finished before their parent tool finished - if (aguiState.pendingChildren && aguiState.pendingChildren.size > 0) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - for (const [parentId, children] of aguiState.pendingChildren.entries()) { - for (const childMsg of children) { - if (childMsg.toolCalls) { - for (const tc of childMsg.toolCalls) { - if (!allToolCalls.has(tc.id)) { - allToolCalls.set(tc.id, { - tc: tc, - // Use timestamp from child message if available, fallback to current time - timestamp: childMsg.timestamp || new Date().toISOString(), - }); - } - } - } - } - } - } - - // Phase B: Build parent-child relationships - const topLevelTools = new Set(); - const childrenByParent = new Map(); - - for (const [toolId, { tc }] of allToolCalls) { - if (tc.parentToolUseId) { - // This is a child tool call - if (!childrenByParent.has(tc.parentToolUseId)) { - childrenByParent.set(tc.parentToolUseId, []); - } - childrenByParent.get(tc.parentToolUseId)!.push(toolId); - } else { - // This is a top-level tool call - topLevelTools.add(toolId); - } - } - - // Handle orphaned children - but DON'T promote to top-level if parent is streaming - for (const [toolId, { tc }] of allToolCalls) { - if (tc.parentToolUseId && !allToolCalls.has(tc.parentToolUseId)) { - // Check if parent is the currently streaming tool - if (aguiState.currentToolCall?.id === tc.parentToolUseId) { - // Don't promote to top-level - parent is streaming and will appear - } else { - // Parent truly not found, render as top-level (fallback) - console.warn(` ⚠️ Orphaned child: ${tc.name} (${toolId.substring(0, 8)}) - parent ${tc.parentToolUseId.substring(0, 8)} not found`); - topLevelTools.add(toolId); - } - } - } - - // Track which tool calls we've already rendered - const renderedToolCalls = new Set(); - - // Phase C: Process messages and build hierarchical structure - for (const msg of aguiState.messages) { - // Use msg.timestamp from backend, fallback to current time for legacy messages - const timestamp = msg.timestamp || new Date().toISOString(); - - // Handle text content by role - if (msg.role === "user") { - result.push({ - type: "user_message", - id: msg.id, // Preserve message ID for feedback association - content: { type: "text_block", text: msg.content || "" }, - timestamp, - }); - } else if (msg.role === "assistant") { - // Check if this is a thinking block (from RAW event) - const metadata = msg.metadata as Record | undefined; - if (metadata?.type === "thinking_block") { - result.push({ - type: "agent_message", - id: msg.id, // Preserve message ID for feedback association - content: { - type: "thinking_block", - thinking: metadata.thinking as string || "", - signature: metadata.signature as string || "", - }, - model: "claude", - timestamp, - }); - } else if (msg.content) { - // Only push text message if there's actual content - result.push({ - type: "agent_message", - id: msg.id, // Preserve message ID for feedback association - content: { type: "text_block", text: msg.content }, - model: "claude", - timestamp, - }); - } - } else if (msg.role === "tool") { - // Standalone tool results (not from toolCalls array) - if (msg.toolCallId && !allToolCalls.has(msg.toolCallId)) { - result.push({ - type: "tool_use_messages", - timestamp, - toolUseBlock: { - type: "tool_use_block", - id: msg.toolCallId, - name: msg.name || "tool", - input: {}, - }, - resultBlock: { - type: "tool_result_block", - tool_use_id: msg.toolCallId, - content: msg.content || null, - is_error: false, - }, - }); - } - } else if (msg.role === "system") { - result.push({ - type: "system_message", - subtype: "system.message", - data: { message: msg.content || "" }, - timestamp, - }); - } - - // Handle tool calls embedded in this message - if (msg.toolCalls && Array.isArray(msg.toolCalls)) { - for (const tc of msg.toolCalls) { - if (!tc || !tc.id || !tc.name) continue; - - // Skip if already rendered or if it's a child (will be rendered inside parent) - if (renderedToolCalls.has(tc.id)) { - continue; - } - if (!topLevelTools.has(tc.id)) { - continue; - } - - // Build children array for this tool call - const childIds = childrenByParent.get(tc.id) || []; - - const children: ToolUseMessages[] = childIds - .map(childId => { - const childData = allToolCalls.get(childId); - if (!childData) return null; - renderedToolCalls.add(childId); - return createToolMessage(childData.tc, childData.timestamp); - }) - .filter((c): c is ToolUseMessages => c !== null); - - // Create the hierarchical tool message - const toolInput = parseToolArgs(tc.args); - - const toolMessage: HierarchicalToolMessage = { - type: "tool_use_messages", - timestamp, - toolUseBlock: { - type: "tool_use_block", - id: tc.id, - name: tc.name, - input: toolInput, - }, - resultBlock: { - type: "tool_result_block", - tool_use_id: tc.id, - content: tc.result || null, - is_error: tc.status === "error", - }, - children: children.length > 0 ? children : undefined, - }; - - result.push(toolMessage); - renderedToolCalls.add(tc.id); - } - } - } - - // Add streaming message if currently streaming - if (aguiState.currentMessage?.content) { - result.push({ - type: "agent_message", - content: { type: "text_block", text: aguiState.currentMessage.content }, - model: "claude", - // Use timestamp from currentMessage (captured from TEXT_MESSAGE_START), fallback to current time - timestamp: aguiState.currentMessage.timestamp || new Date().toISOString(), - streaming: true, - } as MessageObject & { streaming?: boolean }); - } - - // Render ALL currently streaming tool calls (supports parallel tool execution) - // CRITICAL: This renders tools immediately when TOOL_CALL_START arrives, - // not waiting until TOOL_CALL_END like the allToolCalls map approach does - const pendingToolCalls = aguiState.pendingToolCalls || new Map(); - - for (const [toolId, pendingTool] of pendingToolCalls) { - if (renderedToolCalls.has(toolId)) continue; - - const toolName = pendingTool.name || "unknown_tool"; - const toolArgs = pendingTool.args || ""; - const streamingParentId = pendingTool.parentToolUseId; - - // Only render if this is a top-level tool (not a child waiting for parent) - // Children will be rendered nested inside their parent - const isTopLevel = !streamingParentId || !pendingToolCalls.has(streamingParentId); - - if (isTopLevel) { - const toolInput = parseToolArgs(toolArgs); - - // Get any pending children for this tool (children that finished before parent) - const pendingForThis = aguiState.pendingChildren?.get(toolId) || []; - const children: ToolUseMessages[] = pendingForThis - .map(childMsg => { - const childTC = childMsg.toolCalls?.[0]; - if (!childTC) return null; - // Use timestamp from child message if available - return createToolMessage(childTC, childMsg.timestamp || new Date().toISOString()); - }) - .filter((c): c is ToolUseMessages => c !== null); - - // Also include any streaming children from pendingToolCalls - for (const [childId, childTool] of pendingToolCalls) { - if (childTool.parentToolUseId === toolId && !renderedToolCalls.has(childId)) { - const childInput = parseToolArgs(childTool.args || ""); - children.push({ - type: "tool_use_messages", - // Use timestamp from pending tool call (captured from TOOL_CALL_START) - timestamp: childTool.timestamp || new Date().toISOString(), - toolUseBlock: { - type: "tool_use_block", - id: childId, - name: childTool.name, - input: childInput, - }, - resultBlock: { - type: "tool_result_block", - tool_use_id: childId, - content: null, // Still streaming - is_error: false, - }, - }); - renderedToolCalls.add(childId); - } - } - - // Also include any children from the childrenByParent map - const childIds = childrenByParent.get(toolId) || []; - for (const childId of childIds) { - if (renderedToolCalls.has(childId)) continue; - const childData = allToolCalls.get(childId); - if (childData) { - children.push(createToolMessage(childData.tc, childData.timestamp)); - renderedToolCalls.add(childId); - } - } - - const streamingToolMessage: HierarchicalToolMessage = { - type: "tool_use_messages", - // Use timestamp from pending tool call (captured from TOOL_CALL_START) - timestamp: pendingTool.timestamp || new Date().toISOString(), - toolUseBlock: { - type: "tool_use_block", - id: toolId, - name: toolName, - input: toolInput, - }, - resultBlock: { - type: "tool_result_block", - tool_use_id: toolId, - content: null, // No result yet - still running! - is_error: false, - }, - children: children.length > 0 ? children : undefined, - }; - - result.push(streamingToolMessage); - renderedToolCalls.add(toolId); - } - } - - return result; - }, [ - aguiState.messages, - aguiState.currentMessage, - aguiState.currentToolCall, - aguiState.pendingToolCalls, // CRITICAL: Include so UI updates when new tools start - aguiState.pendingChildren, // CRITICAL: Include so UI updates when children finish - ]); - - // Check if there are any real messages (user or assistant messages, not just system) + // Session has real messages when phase indicates activity has occurred. + // CopilotKit handles all message display — this is for sidebar logic only. const hasRealMessages = useMemo(() => { - return streamMessages.some( - (msg) => msg.type === "user_message" || msg.type === "agent_message" - ); - }, [streamMessages]); + const phase = session?.status?.phase; + return phase === "Running" || phase === "Completed"; + }, [session?.status?.phase]); - // Clear queued messages when first agent response arrives - useEffect(() => { - const sentMessages = sessionQueue.messages.filter(m => m.sentAt); - if (sentMessages.length > 0 && streamMessages.length > 0) { - // Check if there's at least one agent message (response to our queued messages) - const hasAgentResponse = streamMessages.some( - msg => msg.type === "agent_message" || msg.type === "tool_use_messages" - ); - - if (hasAgentResponse) { - sessionQueue.clearMessages(); - } - } - }, [sessionQueue, streamMessages]); + // REMOVED: streamMessages useMemo (370+ lines of custom AG-UI message transformation) + // CopilotKit's component now handles all message display, + // streaming, tool rendering, and conversation history. See CopilotChatPanel.tsx. + + // NOTE: The old streamMessages useMemo (370+ lines) was removed. + // CopilotKit's handles all message display and tool rendering. + // See CopilotChatPanel.tsx for the replacement. + // NOTE: streamMessages useMemo (370+ lines) was removed — CopilotKit handles it. + // See CopilotChatPanel.tsx for the replacement chat layer. // Load workflow from session when session data and workflows are available // Syncs the workflow panel with the workflow reported by the API @@ -1158,49 +665,22 @@ export default function ProjectSessionDetailPage({ } }, [session, ootbWorkflows, workflowManagement, hasRealMessages]); - // Auto-refresh artifacts when messages complete - // UX improvement: Automatically refresh the artifacts panel when Claude writes new files, - // so users can see their changes immediately without manually clicking the refresh button - const previousToolResultCount = useRef(0); - const artifactsRefreshTimeoutRef = useRef(null); + // Auto-refresh artifacts periodically while session is running + // CopilotKit handles chat — we just poll artifacts during active sessions const completionTimeoutRef = useRef(null); const hasRefreshedOnCompletionRef = useRef(false); - // Memoize the completed tool count to avoid redundant filtering - // Uses extracted type guard for testability and proper validation - const completedToolCount = useMemo(() => { - return streamMessages.filter(isCompletedToolUseMessage).length; - }, [streamMessages]); - useEffect(() => { - // Initialize on first mount to avoid triggering refresh for existing tools - if (previousToolResultCount.current === 0 && completedToolCount > 0) { - previousToolResultCount.current = completedToolCount; - return; - } - - // If we have new completed tools, refresh artifacts after a short delay - if (completedToolCount > previousToolResultCount.current && completedToolCount > 0) { - // Clear any pending refresh timeout - if (artifactsRefreshTimeoutRef.current) { - clearTimeout(artifactsRefreshTimeoutRef.current); - } - - // Debounce refresh to avoid excessive calls during rapid tool completions - artifactsRefreshTimeoutRef.current = setTimeout(() => { - refetchArtifactsFiles(); - }, ARTIFACTS_DEBOUNCE_MS); + const phase = session?.status?.phase; + if (phase !== "Running") return; - previousToolResultCount.current = completedToolCount; - } + // Poll artifacts every 10 seconds while session is running + const interval = setInterval(() => { + refetchArtifactsFiles(); + }, 10_000); - // Cleanup timeout on unmount or effect re-run - return () => { - if (artifactsRefreshTimeoutRef.current) { - clearTimeout(artifactsRefreshTimeoutRef.current); - } - }; - }, [completedToolCount, refetchArtifactsFiles]); + return () => clearInterval(interval); + }, [session?.status?.phase, refetchArtifactsFiles]); // Also refresh artifacts when session completes (catch any final artifacts) useEffect(() => { @@ -1283,42 +763,8 @@ export default function ProjectSessionDetailPage({ ); }; - const sendChat = async () => { - if (!chatInput.trim()) return; - - const finalMessage = chatInput.trim(); - setChatInput(""); - - // Mark user interaction when they send first message - setUserHasInteracted(true); - - const phase = session?.status?.phase; - - // If session is not yet running, queue the message for later - // This includes: undefined (loading), "Pending", "Creating", or any other non-Running state - if (!phase || phase !== "Running") { - sessionQueue.addMessage(finalMessage); - return; - } - - try { - await aguiSendMessage(finalMessage); - } catch (err) { - errorToast(err instanceof Error ? err.message : "Failed to send message"); - } - }; - - const handleCommandClick = async (slashCommand: string) => { - try { - await aguiSendMessage(slashCommand); - successToast(`Command ${slashCommand} sent`); - } catch (err) { - errorToast(err instanceof Error ? err.message : "Failed to send command"); - } - }; - - // LEGACY: Old handleInterrupt removed - now using aguiInterrupt from useAGUIStream - // which calls the proper AG-UI interrupt endpoint that signals Claude SDK + // NOTE: sendChat, handleCommandClick, handleInterrupt removed. + // CopilotKit's component handles all message sending and interruption. // Loading state if (isLoading || !projectName || !sessionName) { diff --git a/components/frontend/src/components/session/MessagesTab.tsx b/components/frontend/src/components/session/MessagesTab.tsx deleted file mode 100644 index 3cd691322..000000000 --- a/components/frontend/src/components/session/MessagesTab.tsx +++ /dev/null @@ -1,743 +0,0 @@ -"use client"; - -import React, { useState, useRef, useEffect } from "react"; -import { Button } from "@/components/ui/button"; -import { Badge } from "@/components/ui/badge"; -import { MessageSquare, Loader2, Settings, Terminal, Users } from "lucide-react"; -import { StreamMessage } from "@/components/ui/stream-message"; -import { LoadingDots } from "@/components/ui/message"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuCheckboxItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import type { AgenticSession, MessageObject, ToolUseMessages } from "@/types/agentic-session"; -import type { WorkflowMetadata } from "@/app/projects/[name]/sessions/[sessionName]/lib/types"; -import type { QueuedMessageItem } from "@/hooks/use-session-queue"; - -export type MessagesTabProps = { - session: AgenticSession; - streamMessages: Array; - chatInput: string; - setChatInput: (v: string) => void; - onSendChat: () => Promise; - onInterrupt: () => Promise; - onGoToResults?: () => void; - onContinue: () => void; - workflowMetadata?: WorkflowMetadata; - onCommandClick?: (slashCommand: string) => void; - isRunActive?: boolean; // Track if agent is actively processing - showWelcomeExperience?: boolean; - welcomeExperienceComponent?: React.ReactNode; - activeWorkflow?: string | null; // Track if workflow has been selected - userHasInteracted?: boolean; // Track if user has sent any messages - queuedMessages?: QueuedMessageItem[]; // Messages queued while session wasn't running - hasRealMessages?: boolean; // Track if there are real user/agent messages -}; - - -const MessagesTab: React.FC = ({ session, streamMessages, chatInput, setChatInput, onSendChat, onInterrupt, onGoToResults, onContinue, workflowMetadata, onCommandClick, isRunActive = false, showWelcomeExperience, welcomeExperienceComponent, activeWorkflow, userHasInteracted = false, queuedMessages = [], hasRealMessages = false }) => { - const [interrupting, setInterrupting] = useState(false); - const [sendingChat, setSendingChat] = useState(false); - const [showSystemMessages, setShowSystemMessages] = useState(false); - const [agentsPopoverOpen, setAgentsPopoverOpen] = useState(false); - const [commandsPopoverOpen, setCommandsPopoverOpen] = useState(false); - const [waitingDotCount, setWaitingDotCount] = useState(0); - - // Autocomplete state - const [autocompleteOpen, setAutocompleteOpen] = useState(false); - const [autocompleteType, setAutocompleteType] = useState<'agent' | 'command' | null>(null); - const [autocompleteFilter, setAutocompleteFilter] = useState(''); - const [autocompleteTriggerPos, setAutocompleteTriggerPos] = useState(0); - const [autocompleteSelectedIndex, setAutocompleteSelectedIndex] = useState(0); - - const messagesContainerRef = useRef(null); - const textareaRef = useRef(null); - const autocompleteRef = useRef(null); - const [isAtBottom, setIsAtBottom] = useState(true); - - const phase = session?.status?.phase || ""; - const isInteractive = session?.spec?.interactive; - - // Show chat interface only when session is interactive AND Running - // Welcome experience can be shown during Pending/Creating, but chat input only when Running - const showChatInterface = isInteractive && phase === "Running"; - - // Determine if session is in a terminal state - const isTerminalState = ["Completed", "Failed", "Stopped"].includes(phase); - const isCreating = ["Creating", "Pending"].includes(phase); - - // Filter out system messages unless showSystemMessages is true - const filteredMessages = streamMessages.filter((msg) => { - if (showSystemMessages) return true; - - // Hide system_message type by default - // Check if msg has a type property and if it's a system_message - if ('type' in msg && msg.type === "system_message") { - return false; - } - - return true; - }); - - // Check if user is scrolled to the bottom - const checkIfAtBottom = () => { - const container = messagesContainerRef.current; - if (!container) return true; - - // For normal scroll (not reversed), we check if scrollTop + clientHeight >= scrollHeight - const threshold = 50; // pixels from bottom to still consider "at bottom" - const isBottom = container.scrollHeight - container.scrollTop - container.clientHeight < threshold; - return isBottom; - }; - - // Handle scroll event to track if user is at bottom - const handleScroll = () => { - setIsAtBottom(checkIfAtBottom()); - }; - - // Scroll to bottom function - only scrolls the messages container, not the whole page - const scrollToBottom = () => { - const container = messagesContainerRef.current; - if (container) { - container.scrollTop = container.scrollHeight; - } - }; - - // Auto-scroll to bottom when new messages arrive, but only if user was already at bottom - useEffect(() => { - if (isAtBottom) { - scrollToBottom(); - } - }, [filteredMessages, isAtBottom]); - - // Initial scroll to bottom on mount - useEffect(() => { - scrollToBottom(); - }, []); - - // Animate dots for "Please wait one moment" message - useEffect(() => { - const unsentCount = queuedMessages.filter(m => !m.sentAt).length; - if (unsentCount === 0) return; - - const interval = setInterval(() => { - setWaitingDotCount((prev) => (prev + 1) % 4); // Cycles 0, 1, 2, 3 - }, 500); // Change dot every 500ms - - return () => clearInterval(interval); - }, [queuedMessages]); - - // Click outside to close autocomplete - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (autocompleteOpen && - autocompleteRef.current && - !autocompleteRef.current.contains(event.target as Node) && - textareaRef.current && - !textareaRef.current.contains(event.target as Node)) { - setAutocompleteOpen(false); - setAutocompleteType(null); - setAutocompleteFilter(''); - } - }; - - document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [autocompleteOpen]); - - const handleSendChat = async () => { - setSendingChat(true); - try { - await onSendChat(); - } finally { - setSendingChat(false); - } - }; - - const handleInterrupt = async () => { - setInterrupting(true); - try { - await onInterrupt(); - } finally { - setInterrupting(false); - } - }; - - // Get filtered autocomplete items - const getFilteredItems = () => { - if (!autocompleteType) return []; - - if (autocompleteType === 'agent' && workflowMetadata?.agents) { - const filter = autocompleteFilter.toLowerCase(); - return workflowMetadata.agents.filter(agent => - agent.name.toLowerCase().includes(filter) - ); - } - - if (autocompleteType === 'command' && workflowMetadata?.commands) { - const filter = autocompleteFilter.toLowerCase(); - return workflowMetadata.commands.filter(cmd => - cmd.name.toLowerCase().includes(filter) || - cmd.slashCommand.toLowerCase().includes(filter) - ); - } - - return []; - }; - - const filteredAutocompleteItems = getFilteredItems(); - - // Handle autocomplete selection - const handleAutocompleteSelect = (item: { id: string; name: string; slashCommand?: string; description?: string }) => { - if (!textareaRef.current) return; - - const cursorPos = textareaRef.current.selectionStart; - const textBefore = chatInput.substring(0, autocompleteTriggerPos); - const textAfter = chatInput.substring(cursorPos); - - let insertText = ''; - if (autocompleteType === 'agent') { - const agentNameShort = item.name.split(' - ')[0]; - insertText = `@${agentNameShort} `; - } else if (autocompleteType === 'command') { - insertText = `${item.slashCommand} `; - } - - const newText = textBefore + insertText + textAfter; - setChatInput(newText); - - // Reset autocomplete - setAutocompleteOpen(false); - setAutocompleteType(null); - setAutocompleteFilter(''); - setAutocompleteSelectedIndex(0); - - // Set cursor position after insert - setTimeout(() => { - if (textareaRef.current) { - const newCursorPos = textBefore.length + insertText.length; - textareaRef.current.selectionStart = newCursorPos; - textareaRef.current.selectionEnd = newCursorPos; - textareaRef.current.focus(); - } - }, 0); - }; - - // Handle input change to detect @ or / - const handleChatInputChange = (e: React.ChangeEvent) => { - const newValue = e.target.value; - const cursorPos = e.target.selectionStart; - - setChatInput(newValue); - - // Check if we should show autocomplete - if (cursorPos > 0) { - const charBeforeCursor = newValue[cursorPos - 1]; - const textBeforeCursor = newValue.substring(0, cursorPos); - - // Check for @ or / trigger - if (charBeforeCursor === '@' || charBeforeCursor === '/') { - // Make sure it's at the start or after whitespace - if (cursorPos === 1 || /\s/.test(newValue[cursorPos - 2])) { - setAutocompleteTriggerPos(cursorPos - 1); - setAutocompleteType(charBeforeCursor === '@' ? 'agent' : 'command'); - setAutocompleteFilter(''); - setAutocompleteSelectedIndex(0); - setAutocompleteOpen(true); - return; - } - } - - // Update filter if autocomplete is open - if (autocompleteOpen) { - const filterText = textBeforeCursor.substring(autocompleteTriggerPos + 1); - - // Close if we've moved past the trigger or hit whitespace - if (cursorPos <= autocompleteTriggerPos || /\s/.test(filterText)) { - setAutocompleteOpen(false); - setAutocompleteType(null); - setAutocompleteFilter(''); - } else { - setAutocompleteFilter(filterText); - setAutocompleteSelectedIndex(0); - } - } - } else { - // Cursor at start, close autocomplete - if (autocompleteOpen) { - setAutocompleteOpen(false); - setAutocompleteType(null); - setAutocompleteFilter(''); - } - } - }; - - // Determine if we should show messages - // Messages should be hidden until workflow is selected OR user sends a message when welcome experience is active - // BUT always show messages if there are real messages (e.g., when loading an existing session with messages) - const shouldShowMessages = !showWelcomeExperience || activeWorkflow || userHasInteracted || hasRealMessages; - - return ( -
-
- {/* Show welcome experience if active - let the component handle its own visibility logic */} - {showWelcomeExperience && welcomeExperienceComponent} - - {/* Show filtered messages only if workflow is selected or welcome experience is not shown */} - {shouldShowMessages && filteredMessages.map((m, idx) => ( - - ))} - - {/* Show queued messages as regular user messages (only if not yet sent) */} - {queuedMessages.length > 0 && queuedMessages.filter(m => !m.sentAt).map((item) => { - const queuedUserMessage: MessageObject = { - type: "user_message", - content: { type: "text_block", text: item.content }, - timestamp: new Date(item.timestamp).toISOString(), - }; - return ( - - ); - })} - - {/* Show "Please wait" message while queued messages are waiting */} - {queuedMessages.filter(m => !m.sentAt).length > 0 && ( -
-
- {/* Avatar */} -
-
- AI -
-
- - {/* Message Content */} -
- {/* Timestamp */} -
just now
-
- {/* Content */} -

- Please wait one moment{".".repeat(waitingDotCount)} -

-
-
-
-
- )} - - {/* Show loading indicator when agent is actively processing */} - {shouldShowMessages && isRunActive && filteredMessages.length > 0 && ( -
- -
- )} - - {/* Show empty state only if no welcome experience and no messages */} - {!showWelcomeExperience && filteredMessages.length === 0 && ( -
- -

No messages yet

-

- {isInteractive - ? isTerminalState - ? `Session has ${phase.toLowerCase()}.` - : "Start by sending a message below." - : "This session is not interactive."} -

-
- )} -
- - {/* Settings for non-interactive sessions with messages */} - {!isInteractive && filteredMessages.length > 0 && ( -
-
-
- - - - - - - Show system messages - - - -

Non-interactive session

-
-
-
- )} - - {showChatInterface && ( -
-
-
-