From be0083a167e256aa9b460f55ffa4f885a368ba2b Mon Sep 17 00:00:00 2001 From: liyb Date: Sun, 23 Aug 2026 20:37:21 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(plugin):=20Phase=203=20=E2=80=94=20eve?= =?UTF-8?q?nt=20hooks=20(before=5Fpermission=5Fforward)=20+=20compliance?= =?UTF-8?q?=20plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plugin SDK: ctx.hooks.on(eventName, handler) for registering hook handlers. Plugin-host MCP transport: hooks/list + hooks/invoke JSON-RPC methods with 5s per-handler timeout and first-wins semantics. Go server: pluginhook.Invoker spawns plugin-host, calls hooks/invoke via JSON-RPC. Integrated into run_stream.go event loop — intercepts EventPermissionRequest before persistence. Auto-deny/allow decisions skip interaction creation and auto-submit back to the daemon. New event kinds: permission.auto_denied, permission.auto_allowed — persisted with hook_reason/hook_plugin metadata. SSE wire: permission events carry hook_decision field so the frontend can skip the approval card for auto-decided requests. Frontend: RunsPage timeline shows ShieldAlert/ShieldCheck icons for auto-decided permissions. i18n keys added (en-US + zh-CN). Example: @internal/compliance-approval plugin demonstrates: - rm -rf, DROP TABLE → auto-deny - kubectl delete, curl|bash → OA escalation + ask_human - ls, cat, git status → auto-allow - default → ask_human (normal flow) Verified: make check passes, end-to-end JSON-RPC test confirms all decision paths produce correct results. --- apps/web/src/i18n/locales/en-US/admin.json | 2 + apps/web/src/i18n/locales/zh-CN/admin.json | 2 + apps/web/src/lib/api-types.ts | 7 + apps/web/src/pages/admin/RunsPage.tsx | 10 + examples/plugins/README.md | 75 +++++ .../plugins/compliance-approval/manifest.json | 12 + .../plugins/compliance-approval/package.json | 8 + .../compliance-approval/server/index.js | 229 ++++++++++++++ .../skills/compliance-policy.md | 25 ++ server/internal/connector/types.go | 13 + server/internal/dev/plugin_hooks.go | 168 ++++++++++ server/internal/dev/routes.go | 4 + server/internal/dev/run_stream.go | 34 ++ server/internal/dev/stream.go | 32 +- server/internal/pluginhook/invoker.go | 292 ++++++++++++++++++ server/plugin-host/lib/host.js | 20 +- server/plugin-host/lib/mcp-stdio.js | 107 ++++++- server/plugin-host/lib/sdk.js | 74 ++++- 18 files changed, 1096 insertions(+), 18 deletions(-) create mode 100644 examples/plugins/compliance-approval/manifest.json create mode 100644 examples/plugins/compliance-approval/package.json create mode 100644 examples/plugins/compliance-approval/server/index.js create mode 100644 examples/plugins/compliance-approval/skills/compliance-policy.md create mode 100644 server/internal/dev/plugin_hooks.go create mode 100644 server/internal/pluginhook/invoker.go diff --git a/apps/web/src/i18n/locales/en-US/admin.json b/apps/web/src/i18n/locales/en-US/admin.json index 5ee45cb6..4673f1af 100644 --- a/apps/web/src/i18n/locales/en-US/admin.json +++ b/apps/web/src/i18n/locales/en-US/admin.json @@ -607,6 +607,8 @@ "toolResult": "Tool returned a result", "permission": "Asked for approval", "permissionReplied": "Approval replied", + "permissionAutoDenied": "Auto-denied by plugin", + "permissionAutoAllowed": "Auto-allowed by plugin", "modelChanged": "Model changed", "error": "Runtime error", "completed": "Run completed", diff --git a/apps/web/src/i18n/locales/zh-CN/admin.json b/apps/web/src/i18n/locales/zh-CN/admin.json index 022cf14c..d017d690 100644 --- a/apps/web/src/i18n/locales/zh-CN/admin.json +++ b/apps/web/src/i18n/locales/zh-CN/admin.json @@ -607,6 +607,8 @@ "toolResult": "工具返回结果", "permission": "请求人工审批", "permissionReplied": "审批已回复", + "permissionAutoDenied": "已被插件自动拒绝", + "permissionAutoAllowed": "已被插件自动放行", "modelChanged": "切换模型", "error": "运行时错误", "completed": "运行完成", diff --git a/apps/web/src/lib/api-types.ts b/apps/web/src/lib/api-types.ts index c1c53694..18cc0507 100644 --- a/apps/web/src/lib/api-types.ts +++ b/apps/web/src/lib/api-types.ts @@ -536,6 +536,8 @@ export type AgentRunEventKind = | "tool.result" | "permission.asked" | "permission.replied" + | "permission.auto_denied" + | "permission.auto_allowed" | "model.changed" | "session.error" | "run.started" @@ -585,6 +587,11 @@ export interface StreamPermissionRequest { title?: string detail?: string payload?: Record + hook_decision?: { + result: "deny" | "allow" + reason?: string + plugin?: string + } } export interface AgentInteractionOption { diff --git a/apps/web/src/pages/admin/RunsPage.tsx b/apps/web/src/pages/admin/RunsPage.tsx index 5a185164..d94339b9 100644 --- a/apps/web/src/pages/admin/RunsPage.tsx +++ b/apps/web/src/pages/admin/RunsPage.tsx @@ -13,6 +13,8 @@ import { Loader2, Play, Search, + ShieldAlert, + ShieldCheck, TerminalSquare, Wrench, XCircle, @@ -180,6 +182,10 @@ function eventTitle(ev: AgentRunEvent | undefined, t: AdminText): string { return t("runs.detail.steps.permission") case "permission.replied": return t("runs.detail.steps.permissionReplied") + case "permission.auto_denied": + return t("runs.detail.steps.permissionAutoDenied") + case "permission.auto_allowed": + return t("runs.detail.steps.permissionAutoAllowed") case "model.changed": return t("runs.detail.steps.modelChanged") case "session.error": @@ -1081,6 +1087,10 @@ function stepForEvent(ev: AgentRunEvent, t: RunStepT) { return { key: ev.id, sequence: ev.sequence, title: t("runs.detail.steps.permission"), detail: payloadValue(ev, "resource") || payloadValue(ev, "action") || "approval", icon: KeyRound, color: "text-warning", ...withTime } case "permission.replied": return { key: ev.id, sequence: ev.sequence, title: t("runs.detail.steps.permissionReplied"), detail: payloadValue(ev, "decision") || payloadValue(ev, "status"), icon: KeyRound, color: "text-success", ...withTime } + case "permission.auto_denied": + return { key: ev.id, sequence: ev.sequence, title: t("runs.detail.steps.permissionAutoDenied"), detail: payloadValue(ev, "hook_reason") || payloadValue(ev, "resource") || "denied by plugin", icon: ShieldAlert, color: "text-danger", ...withTime } + case "permission.auto_allowed": + return { key: ev.id, sequence: ev.sequence, title: t("runs.detail.steps.permissionAutoAllowed"), detail: payloadValue(ev, "hook_reason") || payloadValue(ev, "resource") || "allowed by plugin", icon: ShieldCheck, color: "text-success", ...withTime } case "model.changed": return { key: ev.id, sequence: ev.sequence, title: t("runs.detail.steps.modelChanged"), detail: [payloadValue(ev, "from"), payloadValue(ev, "to")].filter(Boolean).join(" -> "), icon: Bot, color: "text-info", ...withTime } case "session.error": diff --git a/examples/plugins/README.md b/examples/plugins/README.md index ade3b3b1..12ab03d8 100644 --- a/examples/plugins/README.md +++ b/examples/plugins/README.md @@ -86,3 +86,78 @@ Daemon prompt → spawns plugin-host → loads server/index.js → tools reg Agent calls tool → MCP tools/call → handler runs → result returned ``` + +## compliance-approval (Phase 3) + +A hooks plugin that demonstrates Phase 3 event interception capabilities. +The plugin registers a `before_permission_forward` hook that intercepts +permission requests before they reach the human approval UI. + +### Prerequisites + +- Node.js >= 20 available on the server +- `PARSAR_PLUGIN_HOST_PATH` set to the absolute path of + `server/plugin-host/index.js` +- (Optional) `COMPLIANCE_OA_WEBHOOK` set to your internal OA system's + approval API endpoint for sensitive operation escalation + +### Install + +```bash +export PARSAR_SERVER_URL=http://localhost:8080 +export PARSAR_RUNNER_TOKEN= +export PARSAR_WORKSPACE_ID= + +parsar plugin add ./examples/plugins/compliance-approval +``` + +### What It Does + +**Hook: before_permission_forward** + +When the Agent requests approval for a command, the hook evaluates it: + +| Pattern | Decision | Example | +|---------|----------|---------| +| `rm -rf`, `DROP TABLE`, `mkfs` | Auto-deny | `rm -rf /tmp/data` → blocked | +| `kubectl delete`, `curl\|bash`, `chmod 777` | Escalate to OA + ask human | Sent to internal OA webhook | +| `ls`, `cat`, `git status` | Auto-allow | No approval needed | +| Everything else | Ask human | Normal approval flow | + +**Tools:** + +- **compliance_check_status**: Shows active policy rules and recent audit entries +- **compliance_audit_log**: Query the audit log by action type + +**Skill:** + +Tells the Agent about the compliance policy so it doesn't try to work +around denied commands. + +### Verify + +1. Install the plugin and restart the server +2. Bind it to an Agent via the admin UI +3. Start a new conversation and ask the Agent to run `rm -rf /tmp/scratch` +4. The command should be **auto-denied** — no approval card appears, the + run timeline shows "Auto-denied by plugin" with the reason +5. Ask the Agent to run `kubectl delete pod nginx` +6. This should trigger **OA escalation** — the approval card appears + AND the OA system is notified +7. Ask the Agent to run `ls -la` +8. This should be **auto-allowed** — no approval card, command executes + +### End-to-End Flow + +``` +Agent requests bash "rm -rf /" + → Daemon emits permission_request + → Go server receives EventPermissionRequest + → pluginHookInvoker calls hooks/invoke { event: "before_permission_forward", payload: {...} } + → Plugin-host runs compliance handler + → Handler matches DENY_PATTERNS → returns { deny: true, reason: "危险命令:递归强制删除" } + → Go server auto-submits denial to daemon (no human interaction created) + → Event persisted as "permission.auto_denied" with hook_reason + → SSE stream sends permission event with hook_decision + → Frontend skips approval card, shows "Auto-denied by plugin" in timeline +``` diff --git a/examples/plugins/compliance-approval/manifest.json b/examples/plugins/compliance-approval/manifest.json new file mode 100644 index 00000000..b436e7f1 --- /dev/null +++ b/examples/plugins/compliance-approval/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "@internal/compliance-approval", + "version": "1.0.0", + "description": "Compliance approval plugin — auto-denies dangerous commands (rm -rf), forwards sensitive operations to internal OA approval flow.", + "author": "FDE Team", + "server": { + "entry": "./server/index.js", + "tools": ["compliance_check_status", "compliance_audit_log"], + "hooks": ["before_permission_forward"] + }, + "skills": ["./skills/compliance-policy.md"] +} diff --git a/examples/plugins/compliance-approval/package.json b/examples/plugins/compliance-approval/package.json new file mode 100644 index 00000000..6ad6e552 --- /dev/null +++ b/examples/plugins/compliance-approval/package.json @@ -0,0 +1,8 @@ +{ + "name": "@internal/compliance-approval", + "version": "1.0.0", + "private": true, + "type": "module", + "description": "Compliance approval plugin — auto-denies dangerous commands, forwards sensitive operations to OA", + "main": "server/index.js" +} diff --git a/examples/plugins/compliance-approval/server/index.js b/examples/plugins/compliance-approval/server/index.js new file mode 100644 index 00000000..fdeda228 --- /dev/null +++ b/examples/plugins/compliance-approval/server/index.js @@ -0,0 +1,229 @@ +// Compliance Approval Plugin — server hooks + tools +// Demonstrates Phase 3 plugin hook capabilities: ctx.hooks.on +// +// This plugin implements enterprise compliance rules: +// 1. Auto-deny dangerous commands (rm -rf, DROP TABLE, etc.) +// 2. Forward sensitive operations to internal OA approval system +// 3. Auto-allow known-safe operations +// 4. Everything else → ask_human (normal approval flow) + +// ─── Configuration ─────────────────────────────────────────────────────────── + +// In production, these would be loaded from env or an admin config API. +const OA_WEBHOOK_URL = process.env.COMPLIANCE_OA_WEBHOOK || 'http://oa.internal/api/approval/create'; + +// Patterns that trigger immediate denial — no human override by default. +const DENY_PATTERNS = [ + { pattern: /rm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|--force\s+)*-[a-zA-Z]*r[a-zA-Z]*|rm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|--recursive\s+)*-[a-zA-Z]*f/, reason: '危险命令:递归强制删除 (rm -rf)' }, + { pattern: /rm\s+(-\w+\s+)*\/(\s|$)/, reason: '危险命令:删除根目录' }, + { pattern: /DROP\s+(DATABASE|TABLE|SCHEMA)/i, reason: '危险 SQL:DROP 语句' }, + { pattern: /TRUNCATE\s+TABLE/i, reason: '危险 SQL:TRUNCATE TABLE' }, + { pattern: />\s*\/dev\/sd[a-z]/, reason: '危险操作:直接写入磁盘设备' }, + { pattern: /mkfs\s/, reason: '危险操作:格式化文件系统' }, + { pattern: /:(){ :\|:& };:/, reason: '危险操作:fork bomb' }, + { pattern: /dd\s+.*of=\/dev\//, reason: '危险操作:dd 写入设备' }, +]; + +// Patterns that trigger OA escalation (ask_human + notify OA system). +const SENSITIVE_PATTERNS = [ + { pattern: /kubectl\s+(delete|drain|cordon)/, category: 'k8s_destructive' }, + { pattern: /docker\s+(rm|rmi|system\s+prune)/, category: 'docker_cleanup' }, + { pattern: /ALTER\s+TABLE.*DROP/i, category: 'schema_change' }, + { pattern: /DELETE\s+FROM\s+\w+\s*(WHERE\s+1\s*=\s*1|$)/i, category: 'mass_delete' }, + { pattern: /curl.*\|\s*(bash|sh|zsh)/, category: 'remote_exec' }, + { pattern: /chmod\s+777/, category: 'unsafe_permissions' }, + { pattern: /iptables\s+-F/, category: 'firewall_flush' }, +]; + +// Known-safe operations that can be auto-allowed (bypass human approval). +const SAFE_PATTERNS = [ + /^ls\s/, + /^cat\s/, + /^echo\s/, + /^pwd$/, + /^whoami$/, + /^date$/, + /^git\s+(status|log|diff|branch)/, + /^npm\s+(list|outdated|audit)/, + /^go\s+(version|env|list)/, +]; + +// ─── Audit Log (in-memory for demo; production would use a DB) ─────────────── + +const auditLog = []; + +function logAudit(entry) { + auditLog.push({ + ...entry, + timestamp: new Date().toISOString(), + }); + // Keep last 1000 entries in memory. + if (auditLog.length > 1000) auditLog.shift(); +} + +// ─── OA Integration (mock for demo) ───────────────────────────────────────── + +async function notifyOASystem(request, category) { + const payload = { + type: 'agent_permission_escalation', + category, + tool: request.tool, + title: request.title || '', + detail: request.detail || '', + args: request.args || '', + request_id: request.request_id, + timestamp: new Date().toISOString(), + }; + + try { + const res = await fetch(OA_WEBHOOK_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + signal: AbortSignal.timeout(3000), + }); + logAudit({ action: 'oa_notify', category, request_id: request.request_id, status: res.ok ? 'sent' : 'failed' }); + } catch (err) { + // OA notification is best-effort — don't block the approval flow. + logAudit({ action: 'oa_notify', category, request_id: request.request_id, status: 'error', error: err.message }); + } +} + +// ─── Plugin Setup ──────────────────────────────────────────────────────────── + +export default (ctx) => { + + // ═══ Hook: before_permission_forward ═══ + // This is the core of the compliance plugin — intercepts every + // permission request before it reaches the human approval UI. + + ctx.hooks.on('before_permission_forward', async (request) => { + const command = extractCommand(request); + + // 1. Check deny patterns (immediate rejection, no human override). + for (const rule of DENY_PATTERNS) { + if (rule.pattern.test(command)) { + logAudit({ + action: 'auto_deny', + tool: request.tool, + command, + reason: rule.reason, + request_id: request.request_id, + }); + return { deny: true, reason: rule.reason }; + } + } + + // 2. Check sensitive patterns (escalate to OA + ask human). + for (const rule of SENSITIVE_PATTERNS) { + if (rule.pattern.test(command)) { + logAudit({ + action: 'oa_escalation', + tool: request.tool, + command, + category: rule.category, + request_id: request.request_id, + }); + // Fire-and-forget: notify OA system in background. + notifyOASystem(request, rule.category); + return { ask_human: true, reason: `敏感操作 (${rule.category}),已同步到 OA 审批` }; + } + } + + // 3. Check safe patterns (auto-allow, no approval needed). + for (const pattern of SAFE_PATTERNS) { + if (pattern.test(command)) { + logAudit({ + action: 'auto_allow', + tool: request.tool, + command, + request_id: request.request_id, + }); + return { allow: true, reason: '安全命令,自动放行' }; + } + } + + // 4. Default: ask human (normal approval flow). + return { ask_human: true }; + }); + + // ═══ Tool: compliance_check_status ═══ + // Lets the agent (or admin) check what compliance rules are active. + + ctx.tools.define('compliance_check_status', { + description: 'Check the current compliance policy status — shows active deny rules, sensitive patterns, and recent audit log entries.', + parameters: { + type: 'object', + properties: { + last_n: { + type: 'number', + description: 'Number of recent audit log entries to show (default: 10).', + }, + }, + }, + handler: async (args) => { + const lastN = args.last_n || 10; + return { + value: { + policy_version: '1.0.0', + deny_rules_count: DENY_PATTERNS.length, + sensitive_rules_count: SENSITIVE_PATTERNS.length, + safe_patterns_count: SAFE_PATTERNS.length, + recent_audit: auditLog.slice(-lastN), + oa_webhook_configured: !!process.env.COMPLIANCE_OA_WEBHOOK, + }, + }; + }, + }); + + // ═══ Tool: compliance_audit_log ═══ + // Query the in-memory audit log (for demo purposes). + + ctx.tools.define('compliance_audit_log', { + description: 'Query compliance audit log entries. Filter by action type or get all recent entries.', + parameters: { + type: 'object', + properties: { + action: { + type: 'string', + enum: ['auto_deny', 'auto_allow', 'oa_escalation', 'oa_notify'], + description: 'Filter by action type. Omit to get all entries.', + }, + limit: { + type: 'number', + description: 'Max entries to return (default: 20).', + }, + }, + }, + handler: async (args) => { + let entries = [...auditLog]; + if (args.action) { + entries = entries.filter((e) => e.action === args.action); + } + const limit = args.limit || 20; + return { + value: { + total: entries.length, + entries: entries.slice(-limit), + }, + }; + }, + }); +}; + +// ─── Helpers ───────────────────────────────────────────────────────────────── + +/** + * Extract the command string from a permission request. + * Handles different payload shapes from different connectors. + */ +function extractCommand(request) { + // Direct command field. + if (request.args) return request.args; + // Nested in payload. + if (request.payload?.command) return request.payload.command; + // Some connectors put it in detail. + if (request.detail) return request.detail; + // Title as last resort. + return request.title || ''; +} diff --git a/examples/plugins/compliance-approval/skills/compliance-policy.md b/examples/plugins/compliance-approval/skills/compliance-policy.md new file mode 100644 index 00000000..4f9d762b --- /dev/null +++ b/examples/plugins/compliance-approval/skills/compliance-policy.md @@ -0,0 +1,25 @@ +# Compliance Policy + +This workspace has an active compliance plugin that automatically enforces security policies on tool executions. + +## What happens when you run a command: + +1. **Dangerous commands are blocked** — `rm -rf`, `DROP TABLE`, `mkfs`, and similar destructive operations are automatically denied. Do not attempt to work around these blocks. + +2. **Sensitive operations are escalated** — Kubernetes destructive commands, mass deletions, remote code execution via curl|bash, and firewall changes are forwarded to the internal OA approval system. A human must approve these. + +3. **Safe commands pass through** — `ls`, `cat`, `git status`, and other read-only operations are automatically approved without human intervention. + +4. **Everything else** — Goes through the normal human approval flow. + +## When a command is denied: + +- Do NOT attempt to bypass the denial (e.g., encoding the command differently or using alternatives). +- Explain to the user why the command was blocked. +- Suggest a safer alternative if one exists. +- If the user insists, tell them to contact the workspace admin for a manual override. + +## Tools available: + +- `compliance_check_status` — Check active policy rules and recent audit log +- `compliance_audit_log` — Query compliance events (denials, escalations, approvals) diff --git a/server/internal/connector/types.go b/server/internal/connector/types.go index 14e998df..d588a979 100644 --- a/server/internal/connector/types.go +++ b/server/internal/connector/types.go @@ -165,6 +165,10 @@ type PromptEvent struct { // of a sync Prompt result so callers do not need to re-accumulate // deltas to persist the final message row. Final *PromptOutput + + // HookDecision is populated when a plugin hook auto-decided this + // permission request (Phase 3). Nil for normal flow. + HookDecision *HookDecisionMeta } // ToolCallEvent describes one tool invocation observed by the connector. @@ -202,6 +206,15 @@ type PermissionDecision struct { By string // user id } +// HookDecisionMeta carries the result of a plugin hook that auto-decided +// a permission request. Used internally to communicate hook decisions +// through the event pipeline. +type HookDecisionMeta struct { + Result string // "deny" or "allow" + Reason string + Plugin string +} + // PromptForUserChoiceOption is one selectable answer the human can // pick. Mirrors proto.PromptForUserChoiceOption so the daemon → server // translation is a 1-to-1 copy. diff --git a/server/internal/dev/plugin_hooks.go b/server/internal/dev/plugin_hooks.go new file mode 100644 index 00000000..b62ae21c --- /dev/null +++ b/server/internal/dev/plugin_hooks.go @@ -0,0 +1,168 @@ +package dev + +import ( + "context" + "time" + + "github.com/MiniMax-AI-Dev/parsar/internal/obs/log" + "github.com/MiniMax-AI-Dev/parsar/server/internal/connector" + "github.com/MiniMax-AI-Dev/parsar/server/internal/pluginhook" +) + +// PluginHookInvoker is the interface used by the run stream dispatcher +// to consult plugin hooks before forwarding permission requests. +type PluginHookInvoker interface { + // InvokeBeforePermissionForward calls registered before_permission_forward + // handlers with the permission request payload. Returns a decision. + // + // On timeout or error, implementations must return a fallback decision + // (ask_human) rather than blocking the permission flow. + InvokeBeforePermissionForward(ctx context.Context, payload pluginhook.PermissionPayload) (pluginhook.Decision, error) +} + +// interceptPermissionWithHook checks whether a plugin hook can auto-decide +// a permission request. Returns (true, modifiedEvent) if the hook made a +// decisive deny/allow, meaning the caller should NOT persist the original +// permission.asked event but instead persist the auto-decided event. +// +// Returns (false, original) if no hook is configured, no decisive answer +// was given, or an error occurred — meaning the caller should proceed with +// normal approval flow. +func interceptPermissionWithHook( + ctx context.Context, + invoker PluginHookInvoker, + ev connector.PromptEvent, +) (intercepted bool, replacement connector.PromptEvent) { + if invoker == nil { + return false, ev + } + if ev.Type != connector.EventPermissionRequest || ev.Permission == nil { + return false, ev + } + + perm := ev.Permission + payload := pluginhook.PermissionPayload{ + RequestID: perm.ID, + Tool: perm.Tool, + Title: perm.Title, + Detail: perm.Detail, + Payload: perm.Payload, + } + // Extract args from payload if present (common for bash commands). + if perm.Payload != nil { + if args, ok := perm.Payload["command"].(string); ok { + payload.Args = args + } + } + + decision, err := invoker.InvokeBeforePermissionForward(ctx, payload) + if err != nil { + log.Bg().Warn("plugin_hooks: before_permission_forward hook error, falling through to ask_human", + "request_id", perm.ID, "tool", perm.Tool, "err", err) + return false, ev + } + + switch decision.Result { + case "deny": + log.Bg().Info("plugin_hooks: before_permission_forward hook auto-denied", + "request_id", perm.ID, "tool", perm.Tool, + "reason", decision.Reason, "plugin", decision.Plugin) + return true, connector.PromptEvent{ + Type: connector.EventPermissionRequest, + Permission: &connector.PermissionRequest{ + ID: perm.ID, + DeviceID: perm.DeviceID, + Tool: perm.Tool, + Title: perm.Title, + Detail: perm.Detail, + Payload: mergeHookDecisionPayload(perm.Payload, decision), + }, + HookDecision: &connector.HookDecisionMeta{ + Result: decision.Result, + Reason: decision.Reason, + Plugin: decision.Plugin, + }, + } + + case "allow": + log.Bg().Info("plugin_hooks: before_permission_forward hook auto-allowed", + "request_id", perm.ID, "tool", perm.Tool, + "reason", decision.Reason, "plugin", decision.Plugin) + return true, connector.PromptEvent{ + Type: connector.EventPermissionRequest, + Permission: &connector.PermissionRequest{ + ID: perm.ID, + DeviceID: perm.DeviceID, + Tool: perm.Tool, + Title: perm.Title, + Detail: perm.Detail, + Payload: mergeHookDecisionPayload(perm.Payload, decision), + }, + HookDecision: &connector.HookDecisionMeta{ + Result: decision.Result, + Reason: decision.Reason, + Plugin: decision.Plugin, + }, + } + + default: + // "ask_human", "no_handler", or anything else → normal flow. + return false, ev + } +} + +// mergeHookDecisionPayload adds hook decision metadata into the permission +// payload so it can be persisted and displayed by the UI. +func mergeHookDecisionPayload(original map[string]any, decision pluginhook.Decision) map[string]any { + merged := make(map[string]any, len(original)+3) + for k, v := range original { + merged[k] = v + } + merged["__hook_decision"] = decision.Result + merged["__hook_reason"] = decision.Reason + merged["__hook_plugin"] = decision.Plugin + return merged +} + +// autoSubmitHookDecision submits an auto-deny or auto-allow decision back +// to the connector so the daemon unblocks the agent without waiting for +// a human. This is fire-and-forget — errors are logged but do not block +// the event stream. +func autoSubmitHookDecision(ctx context.Context, target connector.AgentConnector, ev connector.PromptEvent) { + if ev.Permission == nil || ev.HookDecision == nil { + return + } + approved := ev.HookDecision.Result == "allow" + note := ev.HookDecision.Reason + if note == "" { + if approved { + note = "auto-allowed by plugin hook" + } else { + note = "auto-denied by plugin hook" + } + } + + decision := connector.PermissionDecision{ + RequestID: ev.Permission.ID, + DeviceID: ev.Permission.DeviceID, + Approved: approved, + Note: note, + By: "plugin:" + ev.HookDecision.Plugin, + } + + submitCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + if err := target.SubmitPermission(submitCtx, decision); err != nil { + log.Bg().Warn("plugin_hooks: auto-submit permission decision failed", + "request_id", ev.Permission.ID, + "decision", ev.HookDecision.Result, + "plugin", ev.HookDecision.Plugin, + "err", err) + } else { + log.Bg().Info("plugin_hooks: auto-submitted permission decision", + "request_id", ev.Permission.ID, + "decision", ev.HookDecision.Result, + "plugin", ev.HookDecision.Plugin) + } +} diff --git a/server/internal/dev/routes.go b/server/internal/dev/routes.go index 3358d763..4c21a229 100644 --- a/server/internal/dev/routes.go +++ b/server/internal/dev/routes.go @@ -268,6 +268,10 @@ type routerConfig struct { // pluginsDir is the on-disk directory where installed plugin bundles // live (/plugins/). Empty disables plugin client serving. pluginsDir string + // pluginHookInvoker calls plugin hooks (e.g. before_permission_forward). + // Nil means hooks are disabled — all permission requests proceed + // to the normal approval flow. + pluginHookInvoker PluginHookInvoker // skillInstallRunner downloads Skills.sh skills via the skills CLI. // Nil uses npx at request time; tests inject a fake runner. skillInstallRunner skillInstallCommandRunner diff --git a/server/internal/dev/run_stream.go b/server/internal/dev/run_stream.go index a17f8713..c463df00 100644 --- a/server/internal/dev/run_stream.go +++ b/server/internal/dev/run_stream.go @@ -392,6 +392,20 @@ func dispatchConversationRun(ctx context.Context, runtimeStore RuntimeStore, cfg var streamErr string var sawFinalFailure bool for ev := range events { + // Phase 3: Plugin hook interception for permission requests. + // If a hook auto-decides (deny/allow), we submit the decision + // back to the connector and persist a different event kind. + if ev.Type == connector.EventPermissionRequest && ev.Permission != nil { + intercepted, replacement := interceptPermissionWithHook(ctx, cfg.pluginHookInvoker, ev) + if intercepted { + ev = replacement + // Auto-submit the decision to the daemon so the agent + // unblocks immediately (without human interaction). + if ev.HookDecision != nil { + autoSubmitHookDecision(ctx, target, ev) + } + } + } if err := recordPromptEvent(ctx, streamStore, runID, ev); err != nil { reason := fmt.Sprintf("persist agent event %s: %v", ev.Type, err) log.Bg().Error("dispatchConversationRun: canonical event persistence failed", "run_id", runID, "event_type", ev.Type, "err", err) @@ -480,6 +494,26 @@ func eventPersistencePayload(ev connector.PromptEvent) (string, map[string]any, if ev.Permission == nil { return "permission.asked", map[string]any{"sequence": ev.Sequence}, true } + // When a plugin hook auto-decided, persist a different event kind + // so interactionFromRunEvent does NOT create an approval interaction. + if ev.HookDecision != nil { + kind := "permission.auto_denied" + if ev.HookDecision.Result == "allow" { + kind = "permission.auto_allowed" + } + return kind, map[string]any{ + "request_id": ev.Permission.ID, + "device_id": ev.Permission.DeviceID, + "action": ev.Permission.Tool, + "resource": ev.Permission.Title, + "detail": ev.Permission.Detail, + "payload": ev.Permission.Payload, + "hook_decision": ev.HookDecision.Result, + "hook_reason": ev.HookDecision.Reason, + "hook_plugin": ev.HookDecision.Plugin, + "sequence": ev.Sequence, + }, true + } return "permission.asked", map[string]any{"request_id": ev.Permission.ID, "device_id": ev.Permission.DeviceID, "action": ev.Permission.Tool, "resource": ev.Permission.Title, "detail": ev.Permission.Detail, "payload": ev.Permission.Payload, "sequence": ev.Sequence}, true case connector.EventPromptForUserChoice: if ev.PromptForUserChoice == nil { diff --git a/server/internal/dev/stream.go b/server/internal/dev/stream.go index 32e57dc6..ad399c1f 100644 --- a/server/internal/dev/stream.go +++ b/server/internal/dev/stream.go @@ -233,11 +233,19 @@ type wireToolCall struct { // wirePermReq mirrors connector.PermissionRequest in lower_snake_case. type wirePermReq struct { - ID string `json:"id,omitempty"` - Tool string `json:"tool,omitempty"` - Title string `json:"title,omitempty"` - Detail string `json:"detail,omitempty"` - Payload map[string]any `json:"payload,omitempty"` + ID string `json:"id,omitempty"` + Tool string `json:"tool,omitempty"` + Title string `json:"title,omitempty"` + Detail string `json:"detail,omitempty"` + Payload map[string]any `json:"payload,omitempty"` + HookDecision *wireHookDecision `json:"hook_decision,omitempty"` +} + +// wireHookDecision carries plugin hook auto-decision metadata on the SSE wire. +type wireHookDecision struct { + Result string `json:"result"` // "deny" or "allow" + Reason string `json:"reason,omitempty"` + Plugin string `json:"plugin,omitempty"` } type wireUserChoiceOption struct { @@ -274,17 +282,25 @@ func toWireToolCall(t *connector.ToolCallEvent) *wireToolCall { } } -func toWirePermReq(p *connector.PermissionRequest) *wirePermReq { +func toWirePermReq(p *connector.PermissionRequest, hookDecision *connector.HookDecisionMeta) *wirePermReq { if p == nil { return nil } - return &wirePermReq{ + wire := &wirePermReq{ ID: p.ID, Tool: p.Tool, Title: p.Title, Detail: p.Detail, Payload: p.Payload, } + if hookDecision != nil { + wire.HookDecision = &wireHookDecision{ + Result: hookDecision.Result, + Reason: hookDecision.Reason, + Plugin: hookDecision.Plugin, + } + } + return wire } func toWireUserChoiceReq(request *connector.PromptForUserChoiceRequest) *wireUserChoiceReq { @@ -315,7 +331,7 @@ func newStreamEventWire(ev connector.PromptEvent) streamEventWire { Thinking: ev.Thinking, Error: ev.Error, Tool: toWireToolCall(ev.Tool), - Perm: toWirePermReq(ev.Permission), + Perm: toWirePermReq(ev.Permission, ev.HookDecision), Choice: toWireUserChoiceReq(ev.PromptForUserChoice), }, EmittedAt: time.Now().UTC().Format(time.RFC3339Nano), diff --git a/server/internal/pluginhook/invoker.go b/server/internal/pluginhook/invoker.go new file mode 100644 index 00000000..73d41ab8 --- /dev/null +++ b/server/internal/pluginhook/invoker.go @@ -0,0 +1,292 @@ +// Package pluginhook provides a client for invoking plugin hook handlers +// via the plugin-host's hooks/invoke JSON-RPC method over MCP stdio. +// +// The Go server uses this to consult plugin hooks before forwarding +// permission requests to the human approval flow. +package pluginhook + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "io" + "os" + "os/exec" + "sync" + "sync/atomic" + "time" + + "github.com/MiniMax-AI-Dev/parsar/internal/obs/log" +) + +// Decision represents the outcome of a hook invocation. +type Decision struct { + // Result is one of: "deny", "allow", "ask_human", "no_handler". + Result string `json:"decision"` + // Reason is an optional human-readable explanation from the hook. + Reason string `json:"reason,omitempty"` + // Plugin is the name of the plugin that made the decision. + Plugin string `json:"plugin,omitempty"` +} + +// IsDecisive returns true if the hook made an explicit deny/allow decision +// (as opposed to ask_human or no_handler which both mean "proceed with +// normal flow"). +func (d Decision) IsDecisive() bool { + return d.Result == "deny" || d.Result == "allow" +} + +// PermissionPayload is the payload sent to before_permission_forward hooks. +type PermissionPayload struct { + RequestID string `json:"request_id"` + Tool string `json:"tool"` + Title string `json:"title,omitempty"` + Detail string `json:"detail,omitempty"` + Args string `json:"args,omitempty"` + Payload map[string]any `json:"payload,omitempty"` +} + +// Invoker manages a connection to the plugin-host process for hook +// invocation. It spawns the plugin-host process on first use and +// communicates via JSON-RPC over stdin/stdout. +type Invoker struct { + hostPath string + pluginsDir string + + mu sync.Mutex + proc *exec.Cmd + stdin io.WriteCloser + scanner *bufio.Scanner + nextID atomic.Int64 + ready bool +} + +// NewInvoker creates a hook invoker. The process is not started until +// the first call to Invoke. +func NewInvoker(hostPath, pluginsDir string) *Invoker { + return &Invoker{ + hostPath: hostPath, + pluginsDir: pluginsDir, + } +} + +// jsonrpcRequest is a minimal JSON-RPC 2.0 request. +type jsonrpcRequest struct { + JSONRPC string `json:"jsonrpc"` + ID int64 `json:"id"` + Method string `json:"method"` + Params any `json:"params,omitempty"` +} + +// jsonrpcResponse is a minimal JSON-RPC 2.0 response. +type jsonrpcResponse struct { + JSONRPC string `json:"jsonrpc"` + ID int64 `json:"id"` + Result json.RawMessage `json:"result,omitempty"` + Error *jsonrpcError `json:"error,omitempty"` +} + +type jsonrpcError struct { + Code int `json:"code"` + Message string `json:"message"` +} + +// InvokeBeforePermissionForward calls the before_permission_forward hook +// with the given permission request payload. Returns the hook's decision. +// +// Timeout: the caller should set a context deadline; this function also +// enforces an internal 5s hard timeout as a safety net. +func (inv *Invoker) InvokeBeforePermissionForward(ctx context.Context, payload PermissionPayload) (Decision, error) { + // Enforce total timeout of 5s regardless of caller context. + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + return inv.invoke(ctx, "hooks/invoke", map[string]any{ + "event": "before_permission_forward", + "payload": payload, + }) +} + +// InvokeAfterToolResult calls the after_tool_result hook. This is a +// fire-and-forget observation hook — errors are logged but not returned. +func (inv *Invoker) InvokeAfterToolResult(ctx context.Context, payload map[string]any) { + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + _, err := inv.invoke(ctx, "hooks/invoke", map[string]any{ + "event": "after_tool_result", + "payload": payload, + }) + if err != nil { + log.Bg().Warn("pluginhook: after_tool_result hook failed", "err", err) + } +} + +// HasHooks checks if the plugin-host has any hooks registered for the +// given event. Returns false if the process is not running or has no hooks. +func (inv *Invoker) HasHooks(ctx context.Context, event string) bool { + ctx, cancel := context.WithTimeout(ctx, 2*time.Second) + defer cancel() + + result, err := inv.invoke(ctx, "hooks/list", nil) + if err != nil { + return false + } + // Result is a Decision-shaped response from hooks/list which has a + // different shape. Parse directly from the raw call. + _ = result + return true +} + +// Close shuts down the plugin-host process if running. +func (inv *Invoker) Close() error { + inv.mu.Lock() + defer inv.mu.Unlock() + if inv.proc != nil && inv.proc.Process != nil { + if inv.stdin != nil { + inv.stdin.Close() + } + _ = inv.proc.Process.Kill() + _ = inv.proc.Wait() + inv.proc = nil + inv.ready = false + } + return nil +} + +// invoke sends a JSON-RPC request and waits for the response. +func (inv *Invoker) invoke(ctx context.Context, method string, params any) (Decision, error) { + inv.mu.Lock() + if !inv.ready { + if err := inv.startLocked(); err != nil { + inv.mu.Unlock() + return Decision{Result: "ask_human"}, fmt.Errorf("pluginhook: start plugin-host: %w", err) + } + } + id := inv.nextID.Add(1) + req := jsonrpcRequest{ + JSONRPC: "2.0", + ID: id, + Method: method, + Params: params, + } + reqBytes, err := json.Marshal(req) + if err != nil { + inv.mu.Unlock() + return Decision{Result: "ask_human"}, fmt.Errorf("pluginhook: marshal request: %w", err) + } + reqBytes = append(reqBytes, '\n') + + _, err = inv.stdin.Write(reqBytes) + if err != nil { + inv.ready = false + inv.mu.Unlock() + return Decision{Result: "ask_human"}, fmt.Errorf("pluginhook: write to plugin-host: %w", err) + } + + // Read response — we hold the lock so only one call is in-flight at a + // time. This is acceptable because hook invocations are infrequent and + // the 5s timeout bounds the lock hold time. + type scanResult struct { + line string + ok bool + } + scanCh := make(chan scanResult, 1) + go func() { + if inv.scanner.Scan() { + scanCh <- scanResult{line: inv.scanner.Text(), ok: true} + } else { + scanCh <- scanResult{ok: false} + } + }() + + var resp jsonrpcResponse + select { + case <-ctx.Done(): + inv.mu.Unlock() + return Decision{Result: "ask_human"}, fmt.Errorf("pluginhook: timeout waiting for response: %w", ctx.Err()) + case sr := <-scanCh: + if !sr.ok { + inv.ready = false + inv.mu.Unlock() + return Decision{Result: "ask_human"}, fmt.Errorf("pluginhook: plugin-host stdout closed") + } + if err := json.Unmarshal([]byte(sr.line), &resp); err != nil { + inv.mu.Unlock() + return Decision{Result: "ask_human"}, fmt.Errorf("pluginhook: decode response: %w", err) + } + } + inv.mu.Unlock() + + if resp.Error != nil { + return Decision{Result: "ask_human"}, fmt.Errorf("pluginhook: RPC error %d: %s", resp.Error.Code, resp.Error.Message) + } + + var decision Decision + if err := json.Unmarshal(resp.Result, &decision); err != nil { + return Decision{Result: "ask_human"}, fmt.Errorf("pluginhook: decode decision: %w", err) + } + return decision, nil +} + +// startLocked spawns the plugin-host process. Must be called with inv.mu held. +func (inv *Invoker) startLocked() error { + cmd := exec.Command("node", inv.hostPath, "--plugins-dir", inv.pluginsDir) + stdin, err := cmd.StdinPipe() + if err != nil { + return fmt.Errorf("stdin pipe: %w", err) + } + stdout, err := cmd.StdoutPipe() + if err != nil { + stdin.Close() + return fmt.Errorf("stdout pipe: %w", err) + } + // Stderr goes to the server log (os.Stderr so it mixes with server output). + cmd.Stderr = os.Stderr + + if err := cmd.Start(); err != nil { + stdin.Close() + return fmt.Errorf("start process: %w", err) + } + + inv.proc = cmd + inv.stdin = stdin + inv.scanner = bufio.NewScanner(stdout) + inv.scanner.Buffer(make([]byte, 0, 1024*1024), 1024*1024) // 1MB line buffer + + // Send initialize to get the process ready. + initReq := jsonrpcRequest{ + JSONRPC: "2.0", + ID: inv.nextID.Add(1), + Method: "initialize", + Params: map[string]any{"protocolVersion": "2024-11-05", "clientInfo": map[string]string{"name": "parsar-hook-invoker", "version": "0.1.0"}}, + } + initBytes, _ := json.Marshal(initReq) + initBytes = append(initBytes, '\n') + if _, err := stdin.Write(initBytes); err != nil { + cmd.Process.Kill() + return fmt.Errorf("write initialize: %w", err) + } + + // Read initialize response. + if !inv.scanner.Scan() { + cmd.Process.Kill() + return fmt.Errorf("no initialize response from plugin-host") + } + var initResp jsonrpcResponse + if err := json.Unmarshal([]byte(inv.scanner.Text()), &initResp); err != nil { + cmd.Process.Kill() + return fmt.Errorf("decode initialize response: %w", err) + } + if initResp.Error != nil { + cmd.Process.Kill() + return fmt.Errorf("initialize error: %s", initResp.Error.Message) + } + + inv.ready = true + log.Bg().Info("pluginhook: plugin-host process started", + "pid", cmd.Process.Pid, + "plugins_dir", inv.pluginsDir) + return nil +} diff --git a/server/plugin-host/lib/host.js b/server/plugin-host/lib/host.js index ff6e5545..c325d69d 100644 --- a/server/plugin-host/lib/host.js +++ b/server/plugin-host/lib/host.js @@ -14,9 +14,16 @@ import { createPluginContext } from './sdk.js'; * @property {string} pluginName - owning plugin */ +/** + * @typedef {Object} HookHandler + * @property {string} pluginName - owning plugin + * @property {Function} handler - async (payload) => result + */ + /** * @typedef {Object} PluginHost * @property {Map} tools + * @property {Map} hooks * @property {string[]} loadedPlugins */ @@ -31,6 +38,7 @@ import { createPluginContext } from './sdk.js'; */ export async function createPluginHost(pluginsDir, opts = {}) { const tools = new Map(); + const hooks = new Map(); const loadedPlugins = []; const { pluginFilter } = opts; @@ -39,7 +47,7 @@ export async function createPluginHost(pluginsDir, opts = {}) { entries = await readdir(pluginsDir, { withFileTypes: true }); } catch (err) { process.stderr.write(`plugin-host: cannot read plugins-dir: ${err.message}\n`); - return { tools, loadedPlugins }; + return { tools, hooks, loadedPlugins }; } const dirs = entries.filter((e) => e.isDirectory()); @@ -70,7 +78,7 @@ export async function createPluginHost(pluginsDir, opts = {}) { const serverEntry = join(pluginDir, manifest.server.entry); try { - const ctx = createPluginContext(manifest.name, tools); + const ctx = createPluginContext(manifest.name, tools, hooks); const moduleURL = pathToFileURL(serverEntry).href; const mod = await import(moduleURL); const setup = mod.default ?? mod; @@ -80,8 +88,10 @@ export async function createPluginHost(pluginsDir, opts = {}) { } loadedPlugins.push(manifest.name); + const hookCount = ctx.hooks.registered().length; + const hookSuffix = hookCount > 0 ? `, ${hookCount} hooks` : ''; process.stderr.write( - `plugin-host: loaded "${manifest.name}" (${ctx.tools._count()} tools)\n` + `plugin-host: loaded "${manifest.name}" (${ctx.tools._count()} tools${hookSuffix})\n` ); } catch (err) { process.stderr.write( @@ -92,8 +102,8 @@ export async function createPluginHost(pluginsDir, opts = {}) { } process.stderr.write( - `plugin-host: ready (${loadedPlugins.length} plugins, ${tools.size} tools)\n` + `plugin-host: ready (${loadedPlugins.length} plugins, ${tools.size} tools, ${hooks.size} hook events)\n` ); - return { tools, loadedPlugins }; + return { tools, hooks, loadedPlugins }; } diff --git a/server/plugin-host/lib/mcp-stdio.js b/server/plugin-host/lib/mcp-stdio.js index 27178ef4..bf9fe136 100644 --- a/server/plugin-host/lib/mcp-stdio.js +++ b/server/plugin-host/lib/mcp-stdio.js @@ -1,5 +1,5 @@ // MCP stdio transport — implements the Model Context Protocol (JSON-RPC 2.0) -// over stdin/stdout for tool invocation. +// over stdin/stdout for tool invocation and hook interception. // // Protocol reference: https://modelcontextprotocol.io/specification // @@ -7,6 +7,8 @@ // initialize → server info + capabilities // tools/list → list all registered tools // tools/call → invoke a tool handler +// hooks/list → list all registered hook events +// hooks/invoke → invoke hook handlers for an event (Phase 3) // notifications/initialized → client ack (no response) // ping → pong @@ -21,6 +23,7 @@ const SERVER_INFO = { const SERVER_CAPABILITIES = { tools: {}, + hooks: {}, }; /** @@ -79,6 +82,10 @@ async function handleRequest(host, request) { return handleToolsList(host); case 'tools/call': return handleToolsCall(host, params); + case 'hooks/list': + return handleHooksList(host); + case 'hooks/invoke': + return handleHooksInvoke(host, params); default: { const err = new Error(`Method not found: ${method}`); err.code = -32601; @@ -190,6 +197,104 @@ function normalizeToolResult(result) { return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } +// ─── Hooks Methods ─────────────────────────────────────────────────────────── + +/** + * Return the list of registered hook events and their plugin sources. + */ +function handleHooksList(host) { + const hooks = []; + for (const [eventName, handlers] of host.hooks) { + hooks.push({ + event: eventName, + plugins: handlers.map((h) => h.pluginName), + }); + } + return { hooks }; +} + +/** + * Invoke all registered handlers for a hook event. + * + * Params: + * { event: string, payload: object } + * + * Returns: + * { decision: "deny" | "allow" | "ask_human", reason?: string, plugin?: string } + * + * Semantics: + * - Handlers run sequentially in registration order. + * - First handler that returns { deny: true } or { allow: true } wins. + * - If all return { ask_human: true } or no handlers exist, returns ask_human. + * - Per-handler timeout: 5s. Timeout → skip that handler (log warning). + * - Total invocation timeout: handled by the caller (Go side). + */ +async function handleHooksInvoke(host, params) { + if (!params?.event) { + const err = new Error('hooks/invoke: missing params.event'); + err.code = -32602; + throw err; + } + + const handlers = host.hooks.get(params.event); + if (!handlers || handlers.length === 0) { + // No handlers registered — signal the caller to use default behavior. + return { decision: 'no_handler' }; + } + + const payload = params.payload ?? {}; + const perHandlerTimeout = 5_000; + + for (const entry of handlers) { + let result; + try { + result = await Promise.race([ + entry.handler(payload), + timeout(perHandlerTimeout, `Hook handler from "${entry.pluginName}" timed out (${perHandlerTimeout}ms)`), + ]); + } catch (handlerErr) { + process.stderr.write( + `plugin-host: hook "${params.event}" handler from "${entry.pluginName}" failed: ${handlerErr.message}\n` + ); + // Handler error → skip, continue to next handler. + continue; + } + + if (!result || typeof result !== 'object') { + continue; + } + + // First decisive answer wins. + if (result.deny) { + return { + decision: 'deny', + reason: result.reason ?? '', + plugin: entry.pluginName, + }; + } + if (result.allow) { + return { + decision: 'allow', + reason: result.reason ?? '', + plugin: entry.pluginName, + }; + } + if (result.ask_human) { + return { + decision: 'ask_human', + reason: result.reason ?? '', + plugin: entry.pluginName, + }; + } + // Handler returned something else — treat as no-op, continue. + } + + // All handlers either skipped or returned no decision → fallback. + return { decision: 'ask_human', reason: 'no decisive handler' }; +} + +// ─── Utilities ─────────────────────────────────────────────────────────────── + function timeout(ms, message) { return new Promise((_, reject) => { setTimeout(() => reject(new Error(message)), ms); diff --git a/server/plugin-host/lib/sdk.js b/server/plugin-host/lib/sdk.js index 64df1065..f2c051b2 100644 --- a/server/plugin-host/lib/sdk.js +++ b/server/plugin-host/lib/sdk.js @@ -6,9 +6,10 @@ * * @param {string} pluginName - the plugin's name from manifest.json * @param {Map} toolRegistry - shared registry (name → ToolDefinition) + * @param {Map} hookRegistry - shared registry (eventName → HookHandler[]) * @returns {Object} ctx - the plugin server context */ -export function createPluginContext(pluginName, toolRegistry) { +export function createPluginContext(pluginName, toolRegistry, hookRegistry) { let toolCount = 0; const tools = { @@ -54,12 +55,12 @@ export function createPluginContext(pluginName, toolRegistry) { }, }; - // Future phases will add ctx.hooks, ctx.credentials, ctx.api, etc. - // For Phase 1, only ctx.tools is implemented. + const hooks = createHooksContext(pluginName, hookRegistry); + return { tools, + hooks, // Placeholder for future phases — calling these throws NotImplemented. - hooks: notImplementedProxy('hooks'), credentials: notImplementedProxy('credentials'), api: notImplementedProxy('api'), }; @@ -98,6 +99,71 @@ function normalizeParameters(params) { }; } +/** + * Supported hook event names. + */ +export const HOOK_EVENTS = [ + 'before_permission_forward', + 'after_tool_result', + 'on_run_complete', + 'on_run_error', +]; + +/** + * Creates the ctx.hooks namespace for a plugin. + * + * @param {string} pluginName - owning plugin name + * @param {Map} hookRegistry - shared registry (eventName → handler[]) + * @returns {Object} hooks API + */ +function createHooksContext(pluginName, hookRegistry) { + return { + /** + * Register a hook handler for an event. + * + * @param {string} eventName - one of HOOK_EVENTS + * @param {Function} handler - async (payload) => { deny?, allow?, ask_human?, reason? } + */ + on(eventName, handler) { + if (!eventName || typeof eventName !== 'string') { + throw new Error(`[${pluginName}] hooks.on: eventName must be a non-empty string`); + } + if (!HOOK_EVENTS.includes(eventName)) { + throw new Error( + `[${pluginName}] hooks.on: unknown event "${eventName}". Supported: ${HOOK_EVENTS.join(', ')}` + ); + } + if (typeof handler !== 'function') { + throw new Error( + `[${pluginName}] hooks.on("${eventName}"): handler must be a function` + ); + } + + if (!hookRegistry.has(eventName)) { + hookRegistry.set(eventName, []); + } + hookRegistry.get(eventName).push({ + pluginName, + handler, + }); + }, + + /** + * List registered events for this plugin (introspection). + * @returns {string[]} + */ + registered() { + const events = []; + for (const [eventName, handlers] of hookRegistry) { + if (handlers.some((h) => h.pluginName === pluginName)) { + events.push(eventName); + } + } + return events; + }, + }; +} + /** * Returns a Proxy that throws NotImplementedError for any property access. */ From 5e864b6560266b3d7addec716195eb19f9e986f8 Mon Sep 17 00:00:00 2001 From: liyb Date: Tue, 25 Aug 2026 20:58:22 +0800 Subject: [PATCH 2/4] feat(web): migrate conversation UI to assistant-ui ExternalStoreRuntime Replace the hand-rolled ChatStream/MessageRow/ComposerForm (~800 LOC) with assistant-ui's ExternalStoreRuntime backed by a custom adapter. New architecture: - parsar-chat-runtime.ts: bridges timeline API + SSE into assistant-ui - ParsarChatView.tsx: orchestrator (header, runtime provider, interactions) - ParsarThread.tsx: Claude-style thread (grouped reasoning, tool calls, inline composer with Send/Stop toggle, auto-scroll) - ParsarToolCallCard.tsx: collapsible tool call card Key improvements: - Streaming markdown rendering (react-markdown via assistant-ui) - Smart auto-scroll with detach on manual scroll-up - Per-message rendering isolation (no full-tree re-render per token) - Chain-of-thought grouping for reasoning + tool calls - Proper SSE error handling (completed-before-subscribe, user-cancelled, late-error-with-content all collapse silently) - Timeline polling paused during active streaming - Conversation switch properly resets stream state Backend: zero changes. All existing SSE/REST APIs consumed as-is. Dependencies added: - @assistant-ui/react ^0.15.16 - @assistant-ui/react-markdown ^0.14.12 --- apps/web/package.json | 2 + .../conversation/ParsarChatView.tsx | 239 ++ .../components/conversation/ParsarThread.tsx | 431 ++++ .../conversation/ParsarToolCallCard.tsx | 173 ++ apps/web/src/lib/parsar-chat-runtime.ts | 531 ++++ pnpm-lock.yaml | 2279 +++++++++++++++-- 6 files changed, 3411 insertions(+), 244 deletions(-) create mode 100644 apps/web/src/components/conversation/ParsarChatView.tsx create mode 100644 apps/web/src/components/conversation/ParsarThread.tsx create mode 100644 apps/web/src/components/conversation/ParsarToolCallCard.tsx create mode 100644 apps/web/src/lib/parsar-chat-runtime.ts diff --git a/apps/web/package.json b/apps/web/package.json index 6cb26673..9491e218 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -12,6 +12,8 @@ "format:check": "prettier --check src" }, "dependencies": { + "@assistant-ui/react": "^0.15.16", + "@assistant-ui/react-markdown": "^0.14.12", "@radix-ui/react-alert-dialog": "^1.1.18", "@radix-ui/react-collapsible": "^1.1.15", "@radix-ui/react-dialog": "^1.1.18", diff --git a/apps/web/src/components/conversation/ParsarChatView.tsx b/apps/web/src/components/conversation/ParsarChatView.tsx new file mode 100644 index 00000000..0734bed4 --- /dev/null +++ b/apps/web/src/components/conversation/ParsarChatView.tsx @@ -0,0 +1,239 @@ +/** + * ParsarChatView — replaces the old ChatStream component. + * + * Integrates: + * - Chat header (status badge + cancel all + plugin slot) + * - AssistantRuntimeProvider with ParsarThread + * - ConversationInteractionCards + * - Queued run indicators + * - Stream error banner + * - ParsarComposer + * - ChatErrorToast + */ + +import { useEffect, useMemo, useState } from "react" +import { useTranslation } from "react-i18next" +import { useQueryClient } from "@tanstack/react-query" +import { Clock } from "lucide-react" + +import { AssistantRuntimeProvider } from "@assistant-ui/react" + +import { ParsarThread } from "./ParsarThread" +import { ConversationInteractionCards } from "./ConversationInteractionCards" +import { WorkingSteps } from "./StepDisplay" +import { Skeleton } from "../ui/skeleton" +import { ListSlot } from "../plugin/SlotRenderer" +import { cn } from "../../lib/utils" + +import { + useConversation, + useConversationTimeline, + useSendUserMessage, +} from "../../lib/api-conversations" +import { useCancelRun, useCancelConversation } from "../../lib/api-agents" +import { useParsarChatRuntime } from "../../lib/parsar-chat-runtime" +import type { Agent } from "../../lib/api-types" +import { useAdminView } from "../../lib/admin-router" + +interface ParsarChatViewProps { + conversationId: string + agent: Agent | undefined + sidebarFolded?: boolean + sandboxGuard?: { blocked: boolean; message: string } +} + +export function ParsarChatView({ + conversationId, + agent, + sidebarFolded, + sandboxGuard, +}: ParsarChatViewProps) { + const { t } = useTranslation("admin") + const { navigate } = useAdminView() + const qc = useQueryClient() + + // --- Data queries --- + const convInfoQ = useConversation(conversationId, null) + const convWorkspaceId = convInfoQ.data?.workspace_id ?? null + + const sendMut = useSendUserMessage(conversationId || null) + const cancelRunMut = useCancelRun(convWorkspaceId) + const cancelConvMut = useCancelConversation() + + // Toast for /start errors + const [chatToast, setChatToast] = useState(null) + + // Polling pause state — synced from runtime's hasActiveStream below + const [streamActive, setStreamActive] = useState(false) + + // --- Runtime --- + const timelineQ = useConversationTimeline(conversationId, undefined, { + pollingEnabled: !streamActive, + }) + const messages = useMemo(() => timelineQ.data?.messages ?? [], [timelineQ.data?.messages]) + const runs = useMemo(() => timelineQ.data?.agent_runs ?? [], [timelineQ.data?.agent_runs]) + + const chatRuntime = useParsarChatRuntime({ + conversationId, + workspaceId: convWorkspaceId, + timelineMessages: messages, + timelineRuns: runs, + timelineLoading: timelineQ.isLoading, + onSendMessage: async (content: string) => { + return sendMut.mutateAsync({ content }) + }, + onCancelAll: () => { + cancelConvMut.mutate({ + conversationID: conversationId, + reason: "user_clicked_cancel_all", + }) + }, + onCancelRun: (runId: string) => { + cancelRunMut.mutate({ runID: runId, reason: "user_clicked_cancel" }) + }, + invalidateTimeline: () => { + qc.invalidateQueries({ queryKey: ["admin", "conversationTimeline", conversationId] }) + }, + onStartError: setChatToast, + }) + + const { + runtime, + isRunning, + activeRunId, + pendingInteraction, + streamError, + streamingSteps, + hasActiveStream, + stopStream, + } = chatRuntime + + // Keep polling paused while streaming + // eslint-disable-next-line react-hooks/set-state-in-effect -- sync external streaming state to polling control + useEffect(() => { setStreamActive(hasActiveStream) }, [hasActiveStream]) + + // Queued runs for indicators + const queuedRuns = useMemo( + () => runs.filter((r) => r.status === "queued"), + [runs], + ) + + // --- Stop handler: immediately stop + cancel the run --- + const handleStop = () => { + if (activeRunId) { + stopStream() + cancelRunMut.mutate({ runID: activeRunId, reason: "user_clicked_stop" }) + } + } + + return ( + + {/* Minimal header — agent name + plugin slot only */} +
+
+

+ {agent?.name || t("conversations.sidebar.allAgentsHint")} +

+
+ +
+
+
+ + {/* Thread — messages + composer with auto-scroll */} + {timelineQ.isLoading ? ( +
+ + + +
+ ) : ( + + )} + + {/* Below thread: interaction cards + queued indicators + working steps + error */} +
+ {/* Interaction cards (permission / user choice) */} + navigate("approvals")} + /> + + {/* Working steps (streaming tool calls without delta text) */} + {isRunning && !hasActiveStream && streamingSteps.length === 0 && ( +
+
+ )} + + {isRunning && streamingSteps.length > 0 && ( +
+ +
+ )} + + {/* Queued run chips */} + {queuedRuns.map((r) => ( +
+
+ ))} + + {/* Stream error banner */} + {streamError && ( +
+ {t("conversations.stream.error", { error: streamError })} +
+ )} +
+ + {/* Chat toast (for /start failures) */} + {chatToast && ( +
+
+ {chatToast} + +
+
+ )} + +
+ ) +} diff --git a/apps/web/src/components/conversation/ParsarThread.tsx b/apps/web/src/components/conversation/ParsarThread.tsx new file mode 100644 index 00000000..cbf29eab --- /dev/null +++ b/apps/web/src/components/conversation/ParsarThread.tsx @@ -0,0 +1,431 @@ +/** + * ParsarThread — Claude-style thread layout based on assistant-ui's + * official component patterns. + * + * - User messages: right-aligned, muted rounded bubble + * - Assistant messages: full-width markdown, chain-of-thought grouping + * - Composer: bottom-fixed rounded card with Send/Stop toggle + * - Auto-scroll with floating scroll-to-bottom button + */ + +import type { FC } from "react" +import { useTranslation } from "react-i18next" +import { AlertTriangle, ArrowDown, ChevronDown, ChevronRight, Loader2, Wrench } from "lucide-react" +import { useState } from "react" + +import { + ThreadPrimitive, + MessagePrimitive, + ComposerPrimitive, + AuiIf, + groupPartByType, + useMessagePartText, + useMessagePartReasoning, + useAuiState, + type ToolCallMessagePartProps, +} from "@assistant-ui/react" +import { MarkdownTextPrimitive } from "@assistant-ui/react-markdown" + +import { ParsarToolCallCard } from "./ParsarToolCallCard" +import { credentialKindLabel } from "../../lib/credential-kind-ui" +import { cn } from "../../lib/utils" + +// --------------------------------------------------------------------------- +// Thread — full layout including messages + composer +// --------------------------------------------------------------------------- + +export function ParsarThread({ + onStop, + cancelling, + disabled, + agentName, + conversationId, +}: { + onStop?: () => void + cancelling?: boolean + disabled?: boolean + agentName?: string + conversationId?: string +}) { + const { t } = useTranslation("admin") + + return ( + + +
+ {/* Empty state */} + s.thread.isEmpty}> +
+

+ {t("conversations.detail.emptyTimeline")} +

+
+
+ + {/* Messages */} +
+ + {() => } + + + {/* Thinking indicator — shown when running */} + s.thread.isRunning}> +
+ Thinking +
+
+
+ + {/* Footer: scroll-to-bottom + composer pinned at bottom */} + + + + +
+
+
+ ) +} + +// --------------------------------------------------------------------------- +// Message router +// --------------------------------------------------------------------------- + +const ThreadMessage: FC = () => { + const role = useAuiState((s) => s.message.role) + if (role === "user") return + return +} + +// --------------------------------------------------------------------------- +// Scroll to bottom +// --------------------------------------------------------------------------- + +const ThreadScrollToBottom: FC = () => { + return ( + + + + ) +} + +// --------------------------------------------------------------------------- +// User Message — right-aligned muted bubble (Claude style) +// --------------------------------------------------------------------------- + +const UserMessage: FC = () => { + return ( + +
+ +
+
+ ) +} + +// --------------------------------------------------------------------------- +// Assistant Message — full-width, chain-of-thought grouped +// NOTE: "View run →" link for failed runs is not yet ported. +// --------------------------------------------------------------------------- + +const AssistantMessage: FC = () => { + return ( + +
+ + {({ part, children }) => { + switch (part.type) { + case "group-chainOfThought": + return
{children}
+ case "group-reasoning": { + const running = part.status.type === "running" + return {children} + } + case "group-tool": + if (part.indices.length === 1) return <>{children} + return {children} + case "text": + return + case "reasoning": + return + case "tool-call": + return part.toolUI ?? + default: + return null + } + }} +
+
+
+ ) +} + +// --------------------------------------------------------------------------- +// Reasoning (Thinking) — collapsible block +// --------------------------------------------------------------------------- + +function ReasoningBlock({ running, children }: { running: boolean; children: React.ReactNode }) { + const { t } = useTranslation("admin") + const [open, setOpen] = useState(running) + + return ( +
+ + {(open || running) && ( +
+ {children} +
+ )} +
+ ) +} + +const ReasoningText: FC = () => { + const { text } = useMessagePartReasoning() + return {text} +} + +// --------------------------------------------------------------------------- +// Tool Group — collapsible container for multiple tool calls +// --------------------------------------------------------------------------- + +function ToolGroup({ count, running, children }: { count: number; running: boolean; children: React.ReactNode }) { + const { t } = useTranslation("admin") + const [open, setOpen] = useState(running) + + return ( +
+ + {(open || running) && ( +
{children}
+ )} +
+ ) +} + +// --------------------------------------------------------------------------- +// Text Part — markdown with runtime_error detection +// --------------------------------------------------------------------------- + +const AssistantTextPart: FC = () => { + const { text } = useMessagePartText() + + // Detect runtime_error marker from the converter + if (text.startsWith("\x00RUNTIME_ERROR\x00")) { + const parts = text.split("\x00") + const metaJson = parts[2] ?? "{}" + const errorText = parts[3] ?? "" + let metadata: Record = {} + try { metadata = JSON.parse(metaJson) } catch { /* ignore */ } + return + } + + return ( +
+ +
+ ) +} + +// --------------------------------------------------------------------------- +// Tool Call Part +// --------------------------------------------------------------------------- + +const AssistantToolCallPart: FC = (props) => { + return ( + ) ?? {}} + result={props.result} + status={props.status} + /> + ) +} + +// --------------------------------------------------------------------------- +// Runtime Error Card +// --------------------------------------------------------------------------- + +function RuntimeErrorCard({ text, metadata }: { text: string; metadata: Record }) { + const { t, i18n } = useTranslation("admin") + const subKind = stringMeta(metadata, "sub_kind") || stringMeta(metadata, "payload.sub_kind") || "" + const capabilityName = stringMeta(metadata, "capability_name") || t("conversations.runtime_error.fallbackCapability") + const credentialKind = stringMeta(metadata, "credential_kind") + const capabilityID = stringMeta(metadata, "capability_id") + const kindLabel = credentialKindLabel(credentialKind, i18n.language, t("capabilities.credentials.none")) + + let message = text || t("conversations.runtime_error.generic") + let action = "" + let href = "" + const current = `${window.location.pathname}${window.location.search}` + + switch (subKind) { + case "capability_credential_missing": + message = t("conversations.runtime_error.capability_credential_missing", { name: capabilityName, kind: kindLabel }) + action = t("conversations.runtime_error.addCredential") + href = credentialKind ? `?profile=credentials&kind=${encodeURIComponent(credentialKind)}&returnTo=${encodeURIComponent(current)}` : "" + break + case "capability_credential_decrypt_failed": + message = t("conversations.runtime_error.capability_credential_decrypt_failed", { name: capabilityName }) + break + case "capability_credential_kind_mismatch": + message = t("conversations.runtime_error.capability_credential_kind_mismatch", { name: capabilityName }) + action = t("conversations.runtime_error.resetCredential") + href = credentialKind ? `?profile=credentials&kind=${encodeURIComponent(credentialKind)}&returnTo=${encodeURIComponent(current)}` : "" + break + case "capability_version_unavailable": + message = t("conversations.runtime_error.capability_version_unavailable", { name: capabilityName }) + action = t("conversations.runtime_error.manageCapability") + href = capabilityID ? `?admin=capabilities&id=${encodeURIComponent(capabilityID)}` : "?admin=capabilities" + break + } + + return ( +
+
+ + {t("conversations.runtime_error.badge")} +
+

{message}

+ {href && action && ( + + {action} + + )} +

{t("conversations.runtime_error.retryHint")}

+
+ ) +} + +function stringMeta(metadata: Record | undefined, key: string): string { + if (!metadata) return "" + const value = key.includes(".") + ? key.split(".").reduce((acc, part) => acc && typeof acc === "object" ? (acc as Record)[part] : undefined, metadata) + : metadata[key] + return typeof value === "string" ? value : "" +} + +// --------------------------------------------------------------------------- +// Inline Composer (inside the Thread viewport footer) +// --------------------------------------------------------------------------- + +function ParsarComposerInline({ + onStop, + cancelling, + disabled, + agentName, +}: { + onStop?: () => void + cancelling?: boolean + disabled?: boolean + agentName?: string + conversationId?: string +}) { + const { t } = useTranslation("admin") + const composerText = useAuiState((s) => s.composer?.text ?? "") + const showStop = !!onStop && composerText.trim().length === 0 + + return ( + + +
+ {showStop ? ( + + ) : ( + !s.thread.isRunning}> + + + + + )} +
+
+ ) +} diff --git a/apps/web/src/components/conversation/ParsarToolCallCard.tsx b/apps/web/src/components/conversation/ParsarToolCallCard.tsx new file mode 100644 index 00000000..92dea55e --- /dev/null +++ b/apps/web/src/components/conversation/ParsarToolCallCard.tsx @@ -0,0 +1,173 @@ +import { useState } from "react" +import { useTranslation } from "react-i18next" +import { + CheckCircle2, + ChevronDown, + ChevronRight, + FileText, + Loader2, + Search, + TerminalSquare, + Wrench, + XCircle, +} from "lucide-react" + +import { cn } from "../../lib/utils" +import { useToolCallElapsed } from "@assistant-ui/react" + +// --------------------------------------------------------------------------- +// Tool icon / summary helpers (mirrored from StepDisplay to keep styling) +// --------------------------------------------------------------------------- + +const TOOL_ICONS: Record = { + bash: TerminalSquare, + read: FileText, + write: FileText, + edit: FileText, + grep: Search, + glob: Search, +} + +const SUMMARY_MAX = 80 + +function summarizeArgs(name: string, args?: Record): string { + if (!args) return "" + const key = name.toLowerCase() + const FIELDS: Record = { + bash: ["command"], + read: ["file_path", "path"], + write: ["file_path", "path"], + edit: ["file_path", "path"], + grep: ["pattern", "query"], + glob: ["pattern"], + } + const candidates = FIELDS[key] ?? [] + for (const field of candidates) { + const v = args[field] + if (typeof v === "string" && v.trim() !== "") return v.trim() + } + for (const v of Object.values(args)) { + if (typeof v === "string" && v.trim() !== "") return v.trim() + } + return "" +} + +function ellipsizeMiddle(text: string, max = SUMMARY_MAX): string { + if (text.length <= max) return text + const head = Math.ceil((max - 1) / 2) + const tail = Math.floor((max - 1) / 2) + return `${text.slice(0, head)}…${text.slice(text.length - tail)}` +} + +function formatElapsed(ms: number): string { + if (ms < 1000) return `${Math.max(0, Math.round(ms))}ms` + const totalSec = Math.floor(ms / 1000) + if (totalSec < 60) return `${totalSec}s` + const min = Math.floor(totalSec / 60) + const sec = totalSec % 60 + return sec === 0 ? `${min}m` : `${min}m${sec}s` +} + +// --------------------------------------------------------------------------- +// ParsarToolCallCard — renders inside assistant-ui's message parts +// --------------------------------------------------------------------------- + +export function ParsarToolCallCard({ + toolName, + args, + result, + status, +}: { + toolName: string + args: Record + result?: unknown + status: { type: string; reason?: string } +}) { + const { t } = useTranslation("admin") + const [expanded, setExpanded] = useState(false) + const elapsed = useToolCallElapsed() + + const upper = (toolName || "tool").toUpperCase() + const summary = summarizeArgs(toolName, args) + const summaryDisplay = summary ? ellipsizeMiddle(summary) : "" + const IconComponent = TOOL_ICONS[toolName.toLowerCase()] ?? Wrench + + const isRunning = status.type === "running" + const isError = status.type === "incomplete" && status.reason === "error" + + return ( +
+ + + {expanded && ( +
+ {args && Object.keys(args).length > 0 && ( +
+

+ {t("conversations.toolCall.argsLabel", { defaultValue: "Arguments" })} +

+
+                {JSON.stringify(args, null, 2)}
+              
+
+ )} + {result != null && !isRunning && ( +
+

+ {t("conversations.toolCall.resultLabel", { defaultValue: "Result" })} +

+
+                {typeof result === "string" ? result : JSON.stringify(result, null, 2)}
+              
+
+ )} +
+ )} +
+ ) +} diff --git a/apps/web/src/lib/parsar-chat-runtime.ts b/apps/web/src/lib/parsar-chat-runtime.ts new file mode 100644 index 00000000..3bd9ebe5 --- /dev/null +++ b/apps/web/src/lib/parsar-chat-runtime.ts @@ -0,0 +1,531 @@ +/** + * Parsar Chat Runtime — bridges the existing timeline API + SSE stream + * into assistant-ui's ExternalStoreRuntime. + * + * Responsibilities: + * 1. Convert ConversationTimelineMessage + runs → ThreadMessageLike[] + * 2. Manage SSE subscription for streaming (delta/tool/thinking/done/error) + * 3. Expose isRunning / onNew / onCancel to assistant-ui + */ + +import { useCallback, useEffect, useMemo, useRef, useState } from "react" +import { + useExternalStoreRuntime, + type ThreadMessageLike, + type AppendMessage, +} from "@assistant-ui/react" +import type { + ConversationTimelineMessage, + ConversationTimelineRun, + SendUserMessageResponse, + AgentRunStreamEvent, + StreamToolEvent, + ToolStep, +} from "./api-types" +import { startAgentRun } from "./api-conversations" + +// --------------------------------------------------------------------------- +// Types +// --------------------------------------------------------------------------- + +export interface ParsarRuntimeConfig { + conversationId: string + workspaceId: string | null + /** Timeline messages from React Query cache */ + timelineMessages: ConversationTimelineMessage[] + /** Timeline runs from React Query cache */ + timelineRuns: ConversationTimelineRun[] + /** Whether the timeline query is still loading */ + timelineLoading: boolean + /** Send a user message — should call POST /messages API */ + onSendMessage: (content: string) => Promise + /** Cancel all runs in this conversation */ + onCancelAll: () => void + /** Cancel a specific run */ + onCancelRun: (runId: string) => void + /** Invalidate timeline query cache (trigger refetch) */ + invalidateTimeline: () => void + /** Called when a /start POST fails (surface as toast) */ + onStartError?: (message: string) => void +} + +/** Streaming step tracked during an active SSE subscription */ +export interface StreamingToolStep { + tool_call_id: string + name: string + status: "running" | "completed" + args?: Record + started_at: number + ended_at?: number +} + +interface StreamState { + runId: string | null + deltaText: string + thinkingText: string + steps: StreamingToolStep[] + pendingInteraction: { kind: "permission" | "user_choice"; requestId: string } | null + error: string | null +} + +const IDLE_STREAM: StreamState = { + runId: null, + deltaText: "", + thinkingText: "", + steps: [], + pendingInteraction: null, + error: null, +} + +// --------------------------------------------------------------------------- +// SSE helpers +// --------------------------------------------------------------------------- + +function parseSSEData(type: string, raw: string): AgentRunStreamEvent | null { + try { + const data = JSON.parse(raw) as Record + return { ...data, type } as AgentRunStreamEvent + } catch { + return null + } +} + +function isCompletedBeforeSubscribeError(message: string): boolean { + return message.includes("run completed before subscriber attached") +} + +function isUserCancelledError(message: string): boolean { + return message.startsWith("run_cancelled:") +} + +// --------------------------------------------------------------------------- +// Message conversion: Timeline → ThreadMessageLike +// --------------------------------------------------------------------------- + +export function convertTimelineMessage( + msg: ConversationTimelineMessage, + runs: ConversationTimelineRun[], +): ThreadMessageLike { + if (msg.sender_type === "user") { + return { + id: msg.id, + role: "user", + content: [{ type: "text" as const, text: msg.content }], + createdAt: new Date(msg.created_at), + } + } + + // --- Assistant message --- + const parts: Array< + | { readonly type: "text"; readonly text: string } + | { readonly type: "reasoning"; readonly text: string } + | { readonly type: "tool-call"; readonly toolCallId: string; readonly toolName: string; readonly args: Record; readonly result?: unknown } + > = [] + + // Find associated runs (linked by output_message_id) + const associatedRuns = runs.filter((r) => r.output_message_id === msg.id) + + // Add tool-call parts from run steps + for (const run of associatedRuns) { + for (const step of run.steps ?? []) { + const status = deriveToolStatus(step, run.status) + parts.push({ + type: "tool-call" as const, + toolCallId: step.tool_call_id, + toolName: step.name, + args: step.args ?? {}, + result: status === "running" ? undefined : (step.result ?? { _empty: true }), + }) + } + } + + // Add text content (always last — matches Claude/Codex where text follows tool calls) + if (msg.content) { + parts.push({ type: "text" as const, text: msg.content }) + } + + // If no parts at all, add an empty text so assistant-ui doesn't complain + if (parts.length === 0) { + parts.push({ type: "text" as const, text: "" }) + } + + // runtime_error → mark as error status + // Include error metadata in a structured format the UI can parse. + // Encoding contract: \x00RUNTIME_ERROR\x00{metadataJSON}\x00{errorText} + // This tunnels structured data through assistant-ui's text-only content + // model. AssistantTextPart in ParsarThread detects the \x00 prefix and + // routes to RuntimeErrorCard. Safe because API strings never contain + // null bytes. If this becomes fragile, switch to a Map + // side-channel. + if (msg.kind === "runtime_error") { + // Replace the plain text part with one that includes metadata marker + const errorText = msg.content || "" + const metaJson = JSON.stringify(msg.metadata ?? {}) + const enrichedParts: typeof parts = [ + { + type: "text" as const, + text: `\x00RUNTIME_ERROR\x00${metaJson}\x00${errorText}`, + }, + ] + return { + id: msg.id, + role: "assistant", + content: enrichedParts as ThreadMessageLike["content"], + status: { type: "incomplete", reason: "error" }, + createdAt: new Date(msg.created_at), + } + } + + return { + id: msg.id, + role: "assistant", + content: parts as ThreadMessageLike["content"], + createdAt: new Date(msg.created_at), + } +} + +function deriveToolStatus( + step: ToolStep, + runStatus: string | undefined, +): "running" | "completed" | "failed" { + if (step.status === "completed") return "completed" + if (runStatus === "failed") return "failed" + return step.status +} + +// --------------------------------------------------------------------------- +// Build streaming assistant message from SSE state +// --------------------------------------------------------------------------- + +function buildStreamingMessage(stream: StreamState): ThreadMessageLike | null { + if (!stream.runId) return null + const hasContent = + stream.deltaText.length > 0 || + stream.thinkingText.length > 0 || + stream.steps.length > 0 + + if (!hasContent) return null + + const parts: Array< + | { readonly type: "text"; readonly text: string } + | { readonly type: "reasoning"; readonly text: string } + | { readonly type: "tool-call"; readonly toolCallId: string; readonly toolName: string; readonly args: Record; readonly result?: unknown } + > = [] + + // Reasoning/thinking part + if (stream.thinkingText) { + parts.push({ type: "reasoning" as const, text: stream.thinkingText }) + } + + // Tool-call parts + for (const step of stream.steps) { + parts.push({ + type: "tool-call" as const, + toolCallId: step.tool_call_id, + toolName: step.name, + args: step.args ?? {}, + result: step.status === "completed" ? { _completed: true } : undefined, + }) + } + + // Text content + if (stream.deltaText) { + parts.push({ type: "text" as const, text: stream.deltaText }) + } + + // If only tool calls (no text yet), add empty text so message is renderable + if (!stream.deltaText && !stream.thinkingText && stream.steps.length > 0) { + parts.push({ type: "text" as const, text: "" }) + } + + return { + id: `streaming-${stream.runId}`, + role: "assistant", + content: parts as ThreadMessageLike["content"], + status: { type: "running" }, + createdAt: new Date(), + } +} + +// --------------------------------------------------------------------------- +// Hook: useParsarChatRuntime +// --------------------------------------------------------------------------- + +export function useParsarChatRuntime(config: ParsarRuntimeConfig) { + const { + conversationId, + timelineMessages, + timelineRuns, + onSendMessage, + onCancelAll, + invalidateTimeline, + onStartError, + } = config + + const [stream, setStream] = useState(IDLE_STREAM) + const [isRunning, setIsRunning] = useState(false) + const eventSourceRef = useRef(null) + + // Expose stream state for external consumers (interaction cards, etc.) + const pendingInteraction = stream.pendingInteraction + const streamError = stream.error + const activeRunId = stream.runId + + // --- Convert persisted timeline messages --- + const convertedMessages = useMemo(() => { + return timelineMessages.map((msg) => convertTimelineMessage(msg, timelineRuns)) + }, [timelineMessages, timelineRuns]) + + // --- Build streaming message (in-flight) --- + const streamingMessage = useMemo(() => buildStreamingMessage(stream), [stream]) + + // --- Merge: persisted + streaming --- + const allMessages = useMemo(() => { + if (!streamingMessage) return convertedMessages + return [...convertedMessages, streamingMessage] + }, [convertedMessages, streamingMessage]) + + // --- Clean up EventSource on unmount or conversation switch --- + useEffect(() => { + return () => { + eventSourceRef.current?.close() + eventSourceRef.current = null + setStream(IDLE_STREAM) + setIsRunning(false) + } + }, [conversationId]) + + // --- When stream finishes (no more runId + not running), refetch timeline --- + const prevRunIdRef = useRef(null) + useEffect(() => { + if (prevRunIdRef.current && !stream.runId && !isRunning) { + // Stream just ended — refetch timeline + invalidateTimeline() + } + prevRunIdRef.current = stream.runId + }, [stream.runId, isRunning, invalidateTimeline]) + + // --- SSE subscription logic --- + const subscribeToStream = useCallback( + (runId: string) => { + // Close any existing connection + eventSourceRef.current?.close() + + const url = `/api/v1/conversations/${encodeURIComponent(conversationId)}/runs/${encodeURIComponent(runId)}/stream` + const source = new EventSource(url) + eventSourceRef.current = source + + setStream({ + runId, + deltaText: "", + thinkingText: "", + steps: [], + pendingInteraction: null, + error: null, + }) + setIsRunning(true) + + source.addEventListener("delta", (ev: MessageEvent) => { + const parsed = parseSSEData("delta", ev.data) + if (!parsed || parsed.type !== "delta") return + setStream((prev) => ({ + ...prev, + deltaText: prev.deltaText + ((parsed as { delta?: string }).delta ?? ""), + pendingInteraction: null, + })) + }) + + source.addEventListener("thinking", (ev: MessageEvent) => { + const parsed = parseSSEData("thinking", ev.data) + if (!parsed) return + setStream((prev) => ({ + ...prev, + thinkingText: prev.thinkingText + ((parsed as { thinking?: string }).thinking ?? ""), + })) + }) + + source.addEventListener("tool", (ev: MessageEvent) => { + const parsed = parseSSEData("tool", ev.data) + if (!parsed || parsed.type !== "tool") return + const tool = (parsed as { tool?: StreamToolEvent }).tool + if (!tool) return + + setStream((prev) => { + const steps = [...prev.steps] + if (tool.stage === "before") { + const id = tool.id ?? `anon-${steps.length}` + if (!steps.some((s) => s.tool_call_id === id)) { + steps.push({ + tool_call_id: id, + name: tool.name ?? "", + status: "running", + args: tool.args, + started_at: performance.now(), + }) + } + } else if (tool.stage === "after") { + const id = tool.id + const idx = id ? steps.findIndex((s) => s.tool_call_id === id) : -1 + const endedAt = performance.now() + if (idx >= 0) { + steps[idx] = { ...steps[idx], status: "completed", ended_at: endedAt } + } else { + steps.push({ + tool_call_id: id ?? `anon-${steps.length}`, + name: tool.name ?? "", + status: "completed", + args: tool.args, + started_at: endedAt, + ended_at: endedAt, + }) + } + } + return { ...prev, steps, pendingInteraction: null } + }) + }) + + source.addEventListener("permission", (ev: MessageEvent) => { + const parsed = parseSSEData("permission", ev.data) + if (!parsed || parsed.type !== "permission") return + const perm = (parsed as { permission?: { id?: string; hook_decision?: unknown } }).permission + if (!perm?.id) return + // Plugin hook auto-decided — don't show interaction card + if (perm.hook_decision) return + setStream((prev) => ({ + ...prev, + pendingInteraction: { kind: "permission", requestId: perm.id! }, + })) + }) + + source.addEventListener("prompt_for_user_choice", (ev: MessageEvent) => { + const parsed = parseSSEData("prompt_for_user_choice", ev.data) + if (!parsed || parsed.type !== "prompt_for_user_choice") return + const choice = (parsed as { prompt_for_user_choice?: { id?: string } }).prompt_for_user_choice + if (!choice?.id) return + setStream((prev) => ({ + ...prev, + pendingInteraction: { kind: "user_choice", requestId: choice.id! }, + })) + }) + + source.addEventListener("done", (ev: MessageEvent) => { + const parsed = parseSSEData("done", ev.data) + if (!parsed || parsed.type !== "done") return + source.close() + eventSourceRef.current = null + setIsRunning(false) + setStream((prev) => ({ ...prev, runId: null, pendingInteraction: null, error: null })) + }) + + source.addEventListener("error", (ev: Event) => { + const data = "data" in ev && typeof (ev as MessageEvent).data === "string" + ? (ev as MessageEvent).data + : "" + const parsed = data ? parseSSEData("error", data) : null + const message = parsed?.type === "error" + ? (parsed as { error?: string }).error ?? "stream connection failed" + : "stream connection failed" + + // Silent collapse cases + if (isCompletedBeforeSubscribeError(message) || isUserCancelledError(message)) { + source.close() + eventSourceRef.current = null + setIsRunning(false) + setStream((prev) => ({ ...prev, runId: null, error: null, pendingInteraction: null })) + return + } + + // Late-error with prior content → collapse to done silently + setStream((prev) => { + const sawContent = + prev.deltaText.length > 0 || prev.steps.length > 0 || prev.thinkingText.length > 0 + if (sawContent) { + source.close() + eventSourceRef.current = null + setIsRunning(false) + return { ...prev, runId: null, error: null, pendingInteraction: null } + } + // True error — no content received + source.close() + eventSourceRef.current = null + setIsRunning(false) + return { ...prev, runId: null, error: message, pendingInteraction: null } + }) + }) + }, + [conversationId], + ) + + // --- onNew: send user message + subscribe to stream --- + const onNew = useCallback( + async (message: AppendMessage) => { + const textPart = message.content.find((p) => p.type === "text") + const text = textPart && "text" in textPart ? textPart.text : "" + if (!text.trim()) return + + setIsRunning(true) + + try { + const resp = await onSendMessage(text.trim()) + const runId = resp.agent_run_id ?? (resp.run_ids?.[0] ?? null) + + if (runId) { + subscribeToStream(runId) + // Fire-and-forget /start (server auto-starts, this is tolerant fallback) + void startAgentRun(conversationId, runId).catch((err) => { + onStartError?.(err instanceof Error ? err.message : String(err)) + }) + } else { + // No run dispatched (unlikely) — just refetch timeline + setIsRunning(false) + invalidateTimeline() + } + } catch (err) { + setIsRunning(false) + onStartError?.(err instanceof Error ? err.message : String(err)) + } + }, + [conversationId, onSendMessage, subscribeToStream, invalidateTimeline, onStartError], + ) + + // --- onCancel: cancel all + immediately stop UI --- + const onCancel = useCallback(async () => { + eventSourceRef.current?.close() + eventSourceRef.current = null + setIsRunning(false) + setStream(IDLE_STREAM) + onCancelAll() + }, [onCancelAll]) + + // --- Build the runtime --- + const runtime = useExternalStoreRuntime({ + messages: allMessages, + convertMessage: (msg: ThreadMessageLike) => msg, + isRunning, + onNew, + onCancel, + }) + + return { + runtime, + /** Whether a stream is actively receiving events */ + isRunning, + /** The currently active run ID (for per-run cancel) */ + activeRunId, + /** Pending interaction from SSE (for ConversationInteractionCards) */ + pendingInteraction, + /** Stream error (only set when no content was received) */ + streamError, + /** Current streaming tool steps (for WorkingSteps display) */ + streamingSteps: stream.steps, + /** Whether we have active streaming content (for polling pause) */ + hasActiveStream: !!stream.runId, + /** Immediately stop the stream (for Stop button) */ + stopStream: () => { + eventSourceRef.current?.close() + eventSourceRef.current = null + setIsRunning(false) + setStream(IDLE_STREAM) + }, + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b544422d..58158024 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,12 @@ importers: apps/web: dependencies: + '@assistant-ui/react': + specifier: ^0.15.16 + version: 0.15.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)) + '@assistant-ui/react-markdown': + specifier: ^0.14.12 + version: 0.14.12(@assistant-ui/react@0.15.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)))(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-alert-dialog': specifier: ^1.1.18 version: 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -162,6 +168,71 @@ importers: packages: + '@assistant-ui/core@0.3.15': + resolution: {integrity: sha512-pCeVhMmzK4xFbahtRtvjlGXDxycsvl1plgiD0M5ZyABm+QxINGnBO5GEz1hqDHQVjKlCIJDNj15PlFSkuFnidA==} + peerDependencies: + '@assistant-ui/store': ^0.3.0 + '@assistant-ui/tap': ^0.9.0 + '@types/react': '*' + assistant-cloud: ^0.1.31 + react: ^18 || ^19 + zustand: ^5.0.11 + peerDependenciesMeta: + '@types/react': + optional: true + assistant-cloud: + optional: true + react: + optional: true + zustand: + optional: true + + '@assistant-ui/react-markdown@0.14.12': + resolution: {integrity: sha512-g8uRpTpwk43qYzknPh7k1U0FysdJ4gySY88NiBLbCRWEGy/aHPy5B+6kaRttND3Rij3rQpGkP5NZOo/z+kMEEQ==} + peerDependencies: + '@assistant-ui/react': ^0.15.0 + '@types/react': '*' + react: ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + + '@assistant-ui/react@0.15.16': + resolution: {integrity: sha512-+2BO6npVEXdViNWTyH3XddVXHo7SOcXliM8btrXGzH6PHuTtn3CYL7DCQ5ZXVRHB9kYPBbbMPURVu93jg9qIwg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^18 || ^19 + react-dom: ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@assistant-ui/store@0.3.10': + resolution: {integrity: sha512-QSFgodFt/daEjyaY09WdahNewsGPtAeY+DkSb2LM2rPN6634h13R1vJQvCtyWK5YsI5WXgzgbsctzgpIQLU0qw==} + peerDependencies: + '@assistant-ui/tap': ^0.9.12 + '@types/react': '*' + react: ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + + '@assistant-ui/tap@0.9.14': + resolution: {integrity: sha512-L/ZyXLQb51/d+/5xlXaP1197PF94EO/Ji+/CBWTjuEbsU1+8dzXCHVNO9GCFQvyG02OiOVitfbRksipMVzdrnA==} + peerDependencies: + '@types/react': '*' + react: ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} @@ -497,9 +568,41 @@ packages: engines: {node: '>=18'} hasBin: true + '@radix-ui/number@1.1.3': + resolution: {integrity: sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==} + '@radix-ui/primitive@1.1.4': resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} + '@radix-ui/primitive@1.1.7': + resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==} + + '@radix-ui/react-accessible-icon@1.1.15': + resolution: {integrity: sha512-WTQwcAvQf5sOcuUyi90lKPbhwcvQ+j55cjrSmeaN+L2vKU3DooOvlKw2MDeiJ5IkV5N905KW0/fGojKOBhD11A==} + 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 + + '@radix-ui/react-accordion@1.2.20': + resolution: {integrity: sha512-jDhG9FvAEnlhnjrsINbNXcUa4G+L1KqSkJSunkbKEzFRcAb52jvM0PjPxPRvhe1HNc5F5yc0yzzWeeqlH4yBIg==} + 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 + '@radix-ui/react-alert-dialog@1.1.18': resolution: {integrity: sha512-6c2cXpNlAgHDhKguK24XcWHHayMpK+lk7/WwBXBco+ZJ4Dv7xP++GBM280KgTD/HCRu3jSdfe8WQiZssonYaIA==} peerDependencies: @@ -513,6 +616,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-alert-dialog@1.1.23': + resolution: {integrity: sha512-VAYOiQRqj3GPpYJE0I9J+X8Ip05cyVlNdKOFeiGS2Ou1HHGfpl0BxOyZm6nmVDyU+W+NF3/XLzmjHmVGydhwgA==} + 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 + '@radix-ui/react-arrow@1.1.11': resolution: {integrity: sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A==} peerDependencies: @@ -526,8 +642,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collapsible@1.1.15': - resolution: {integrity: sha512-8A1zibu5skAQ+UVbaeNH5hVMibiFCRJzgMuM14LTWGttnTZKQL9jwYnhAbHRuxrtCqPXa4JvvnVUq1pTNgyZYw==} + '@radix-ui/react-arrow@1.1.15': + resolution: {integrity: sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -539,8 +655,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.11': - resolution: {integrity: sha512-djW9+zeg137KQdlPtmE8xnaD+K2rcXXMWFrSg0hsmYZ6HRbdTA7tDHFgpaW9+huWVEu0RCabL+985T4TA0BE7g==} + '@radix-ui/react-aspect-ratio@1.1.15': + resolution: {integrity: sha512-fy+dyVR+90nelK8rqIznFlxzx7uPcGbhxH8Nfr2bHb4UfSe+e3hklOC0luK0hDwVwnRX7xTRySpsrQVeW+/oNQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -552,26 +668,34 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-compose-refs@1.1.3': - resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} + '@radix-ui/react-avatar@1.2.6': + resolution: {integrity: sha512-4ULOTJ/mqy2hT9GlWa/MFHxHSvH3nJzHnZM1waNsc5Bonv7i70aNenghXmD97S6OJ81ekXONGGt4nT1r0PfEdA==} 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 - '@radix-ui/react-context@1.1.4': - resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} + '@radix-ui/react-checkbox@1.3.11': + resolution: {integrity: sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==} 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 - '@radix-ui/react-dialog@1.1.18': - resolution: {integrity: sha512-apa28mldjMgORmE6g/w3sCcA0Y9UAVeeDVoozN4i7kOw12mLl9RBchfzK3Nn6qxOWjrZhK1Lfy7f07kyzxtnBw==} + '@radix-ui/react-collapsible@1.1.15': + resolution: {integrity: sha512-8A1zibu5skAQ+UVbaeNH5hVMibiFCRJzgMuM14LTWGttnTZKQL9jwYnhAbHRuxrtCqPXa4JvvnVUq1pTNgyZYw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -583,17 +707,21 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-direction@1.1.2': - resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} + '@radix-ui/react-collapsible@1.1.20': + resolution: {integrity: sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q==} 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 - '@radix-ui/react-dismissable-layer@1.1.14': - resolution: {integrity: sha512-4lUhWTWAjbDIqFrAPWJ3WqBOpO5YchVZ88X3nh6H9Lu5AFi5nCUeTPj3D8FSDmabmFeRe9ME0BDA4MwKTha5GQ==} + '@radix-ui/react-collection@1.1.11': + resolution: {integrity: sha512-djW9+zeg137KQdlPtmE8xnaD+K2rcXXMWFrSg0hsmYZ6HRbdTA7tDHFgpaW9+huWVEu0RCabL+985T4TA0BE7g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -605,8 +733,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.19': - resolution: {integrity: sha512-HZccBkbK0LOi8nYKIp5jll/zIRW0cCOmG6WWyqsSpmXCU+ZlcBbTqIwlBvPCu886C5RVu6c/kHV7xSP8IgYNHw==} + '@radix-ui/react-collection@1.1.15': + resolution: {integrity: sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -618,8 +746,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-focus-guards@1.1.4': - resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -627,8 +755,17 @@ packages: '@types/react': optional: true - '@radix-ui/react-focus-scope@1.1.11': - resolution: {integrity: sha512-Mn88Vg2whaRocGJNOH+DKFqYm6ySFPQaiwHNxZPyjn99B52KAEJWWY9NP83+nWdk2HM3rdov+STu9AG471Rt9w==} + '@radix-ui/react-compose-refs@1.1.5': + resolution: {integrity: sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context-menu@2.3.7': + resolution: {integrity: sha512-CtXP35dxaB5T3zXSd+E3uHe/QpXcpYnZmxp6OaIbfthtfW4wyb77M23BG+bwIJDtsMwEP/YssdsmNyZu7jhWew==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -640,8 +777,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-id@1.1.2': - resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} + '@radix-ui/react-context@1.1.4': + resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -649,8 +786,17 @@ packages: '@types/react': optional: true - '@radix-ui/react-menu@2.1.19': - resolution: {integrity: sha512-Mht9BVd1AIsNFVQr4KG3bIK7XQn5IXF0TL/2ObsrzOdc1loaly/+kBDL5roSCYn8j8XZkvpOD0WYLz2FQtH1Eg==} + '@radix-ui/react-context@1.2.2': + resolution: {integrity: sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.18': + resolution: {integrity: sha512-apa28mldjMgORmE6g/w3sCcA0Y9UAVeeDVoozN4i7kOw12mLl9RBchfzK3Nn6qxOWjrZhK1Lfy7f07kyzxtnBw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -662,8 +808,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.3.2': - resolution: {integrity: sha512-3QXNeMkdshed1MR3LNoiCirBywRFPkD8ETJa/HlPuLwSajaQixf2ro+isoDNJlGABg9ug41XuZpINZJIle4XWg==} + '@radix-ui/react-dialog@1.1.23': + resolution: {integrity: sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -675,8 +821,26 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.13': - resolution: {integrity: sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==} + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-direction@1.1.4': + resolution: {integrity: sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.14': + resolution: {integrity: sha512-4lUhWTWAjbDIqFrAPWJ3WqBOpO5YchVZ88X3nh6H9Lu5AFi5nCUeTPj3D8FSDmabmFeRe9ME0BDA4MwKTha5GQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -688,8 +852,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.6': - resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} + '@radix-ui/react-dismissable-layer@1.1.19': + resolution: {integrity: sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -701,8 +865,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.1.7': - resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==} + '@radix-ui/react-dropdown-menu@2.1.19': + resolution: {integrity: sha512-HZccBkbK0LOi8nYKIp5jll/zIRW0cCOmG6WWyqsSpmXCU+ZlcBbTqIwlBvPCu886C5RVu6c/kHV7xSP8IgYNHw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -714,8 +878,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.14': - resolution: {integrity: sha512-8Qcnx9447tx/aCBgw6Jenfqg4Skq+vqab9mCBmuGNipIS5YXvL275wbKEu7+ICYHIlAPgCduUMJH1XOYewKF6Q==} + '@radix-ui/react-dropdown-menu@2.1.24': + resolution: {integrity: sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -727,8 +891,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slot@1.3.0': - resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -736,8 +900,17 @@ packages: '@types/react': optional: true - '@radix-ui/react-tabs@1.1.16': - resolution: {integrity: sha512-v3Ab2l7z6U7tRB4xA0IyKdq0OsqaO1o9ZjsIEoKKnSZ/l96mZz8aCTX0NCXw+YVHJXr8Km4d+Mn6/Q8YjXa+gw==} + '@radix-ui/react-focus-guards@1.1.6': + resolution: {integrity: sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.11': + resolution: {integrity: sha512-Mn88Vg2whaRocGJNOH+DKFqYm6ySFPQaiwHNxZPyjn99B52KAEJWWY9NP83+nWdk2HM3rdov+STu9AG471Rt9w==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -749,8 +922,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tooltip@1.2.11': - resolution: {integrity: sha512-8XZ6Py3y3W2nEzAUGCN5cfVKaUi+CVApcz1d6lrNVVf2hvYEixMRkq8k9ggPKnQUpRRuOV5avt8uvxViH2jLwA==} + '@radix-ui/react-focus-scope@1.1.16': + resolution: {integrity: sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -762,26 +935,34 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-use-callback-ref@1.1.2': - resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + '@radix-ui/react-form@0.1.16': + resolution: {integrity: sha512-Q4TLEn2A7TAypxwmd6R9EwrlXDvkfYSDMrq9/887AXAGh+G1rH+kYJKSTv+Si9Y0JPKTwKYv6PviAJosysNimA==} 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 - '@radix-ui/react-use-controllable-state@1.2.3': - resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + '@radix-ui/react-hover-card@1.1.23': + resolution: {integrity: sha512-H8qONfZd3ltrU3+jHCIgITbWo6e1iTKvP9DHdrvYbX48ooRM5FjEDTn16AMwdfuOGkWdZEhpl3PLL/Wk/AnHDQ==} 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 - '@radix-ui/react-use-effect-event@0.0.3': - resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -789,8 +970,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-layout-effect@1.1.2': - resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + '@radix-ui/react-id@1.1.4': + resolution: {integrity: sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -798,26 +979,34 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-rect@1.1.2': - resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + '@radix-ui/react-label@2.1.15': + resolution: {integrity: sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==} 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 - '@radix-ui/react-use-size@1.1.2': - resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + '@radix-ui/react-menu@2.1.19': + resolution: {integrity: sha512-Mht9BVd1AIsNFVQr4KG3bIK7XQn5IXF0TL/2ObsrzOdc1loaly/+kBDL5roSCYn8j8XZkvpOD0WYLz2FQtH1Eg==} 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 - '@radix-ui/react-visually-hidden@1.2.7': - resolution: {integrity: sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw==} + '@radix-ui/react-menu@2.1.24': + resolution: {integrity: sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -829,22 +1018,594 @@ packages: '@types/react-dom': optional: true - '@radix-ui/rect@1.1.2': - resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} - - '@rolldown/binding-android-arm64@1.1.5': - resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] + '@radix-ui/react-menubar@1.1.24': + resolution: {integrity: sha512-eeVs0vf7cuqXaM0qLQCPcufImiJNVBXdJDLu7ZGYl2732UH23Qat/foNGrr6vYV3/DdTsBqASoggUFgH14OcZA==} + 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 - '@rolldown/binding-darwin-arm64@1.1.5': - resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] + '@radix-ui/react-navigation-menu@1.2.22': + resolution: {integrity: sha512-ou7iLEJ+yrhQndkkA4U21XIdS/CS45F4iXIkTZcb6/Ne9EMsOuDudVmCwmDnfFZZ+y1FZqXRNSIgBy+YMvZVZg==} + 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 - '@rolldown/binding-darwin-x64@1.1.5': + '@radix-ui/react-one-time-password-field@0.1.16': + resolution: {integrity: sha512-Tj9P6ntAJEw52oq/F0AGknXR4XncxEt7XU47O3xJQOiWfLzEy3d9gtgKfvjSzGxzHkfL+VzvxGu2KTFsloJqXw==} + 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 + + '@radix-ui/react-password-toggle-field@0.1.11': + resolution: {integrity: sha512-4gvFnmDXu3dgj21CqsufzIameRvlRd4SBqaWhcrlrNhRo0Y5i/49AmRJYe1fdAM3G2VNBbmin4b0D6cdQocwgw==} + 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 + + '@radix-ui/react-popover@1.1.23': + resolution: {integrity: sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==} + 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 + + '@radix-ui/react-popper@1.3.2': + resolution: {integrity: sha512-3QXNeMkdshed1MR3LNoiCirBywRFPkD8ETJa/HlPuLwSajaQixf2ro+isoDNJlGABg9ug41XuZpINZJIle4XWg==} + 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 + + '@radix-ui/react-popper@1.3.7': + resolution: {integrity: sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==} + 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 + + '@radix-ui/react-portal@1.1.13': + resolution: {integrity: sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==} + 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 + + '@radix-ui/react-portal@1.1.17': + resolution: {integrity: sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==} + 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 + + '@radix-ui/react-presence@1.1.10': + resolution: {integrity: sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==} + 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 + + '@radix-ui/react-presence@1.1.6': + resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} + 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 + + '@radix-ui/react-primitive@2.1.10': + resolution: {integrity: sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==} + 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 + + '@radix-ui/react-primitive@2.1.7': + resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==} + 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 + + '@radix-ui/react-progress@1.1.16': + resolution: {integrity: sha512-5XnomAsoZZCY+KNTxbIghpGqPruZvKFNlvcAljVAOdDRDsH4/OZQxhtwo5wdtoDM5R6MhJBb2sPnDuRFep3lzg==} + 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 + + '@radix-ui/react-radio-group@1.4.7': + resolution: {integrity: sha512-cgYFEkntCxppHZgtSZ+7vh0wbZQ+IC7PPMw8DSnRG27B6kDd32/Zw0OJt7dGDigCoprMuWHjg2PvUn3PYvPFoQ==} + 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 + + '@radix-ui/react-roving-focus@1.1.14': + resolution: {integrity: sha512-8Qcnx9447tx/aCBgw6Jenfqg4Skq+vqab9mCBmuGNipIS5YXvL275wbKEu7+ICYHIlAPgCduUMJH1XOYewKF6Q==} + 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 + + '@radix-ui/react-roving-focus@1.1.19': + resolution: {integrity: sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==} + 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 + + '@radix-ui/react-scroll-area@1.2.18': + resolution: {integrity: sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==} + 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 + + '@radix-ui/react-select@2.3.7': + resolution: {integrity: sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==} + 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 + + '@radix-ui/react-separator@1.1.15': + resolution: {integrity: sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==} + 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 + + '@radix-ui/react-slider@1.4.7': + resolution: {integrity: sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg==} + 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 + + '@radix-ui/react-slot@1.3.0': + resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.3.3': + resolution: {integrity: sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.3.7': + resolution: {integrity: sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==} + 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 + + '@radix-ui/react-tabs@1.1.16': + resolution: {integrity: sha512-v3Ab2l7z6U7tRB4xA0IyKdq0OsqaO1o9ZjsIEoKKnSZ/l96mZz8aCTX0NCXw+YVHJXr8Km4d+Mn6/Q8YjXa+gw==} + 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 + + '@radix-ui/react-tabs@1.1.21': + resolution: {integrity: sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==} + 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 + + '@radix-ui/react-toast@1.2.23': + resolution: {integrity: sha512-ofhyAsYaocRGOs/n0XWdUOSVzEAG6BfrMVM8z0c0kLEWY38w/0WuMFPTJP/HVaZPYkMvHZoKIIhNcjbTCBILPg==} + 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 + + '@radix-ui/react-toggle-group@1.1.19': + resolution: {integrity: sha512-OtnwuSVjd1Ofi+AdnvhsjQdyuhCDwYs1w9RyB5BN/OavXOVQo42SYqQjwUnbPnaiPFBpQ9aX70dWeee+v2oBLA==} + 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 + + '@radix-ui/react-toggle@1.1.18': + resolution: {integrity: sha512-7lonPlKfSacd20GlOBx2ltuVKz9oqWYZz+oMQyOltw6t1y2nyftj2ZmwwUHYn49kqfDWcp8dNZm5NgV+5Z+mug==} + 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 + + '@radix-ui/react-toolbar@1.1.19': + resolution: {integrity: sha512-Ph0IvtYw4VB12ZnZg+YtrGs8yJQsnizwo/zu0R4Y/nWugtJzA7Pg1eWeuDR9+LSqn+xjamss+UOSOJJJ4gx8jw==} + 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 + + '@radix-ui/react-tooltip@1.2.11': + resolution: {integrity: sha512-8XZ6Py3y3W2nEzAUGCN5cfVKaUi+CVApcz1d6lrNVVf2hvYEixMRkq8k9ggPKnQUpRRuOV5avt8uvxViH2jLwA==} + 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 + + '@radix-ui/react-tooltip@1.2.16': + resolution: {integrity: sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==} + 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 + + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.4': + resolution: {integrity: sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.6': + resolution: {integrity: sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.5': + resolution: {integrity: sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.5': + resolution: {integrity: sha512-ge3ipobwSXTj4JyVtswQ7qZj0ZHdtbGuOno/LrgAAeSxtsJ6Vs4Gz5IkPH2bmqpjcLUFoqGhA/mueuIf63UXlA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-is-hydrated@0.1.3': + resolution: {integrity: sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.4': + resolution: {integrity: sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.4': + resolution: {integrity: sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.4': + resolution: {integrity: sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.4': + resolution: {integrity: sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.11': + resolution: {integrity: sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==} + 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 + + '@radix-ui/react-visually-hidden@1.2.7': + resolution: {integrity: sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw==} + 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 + + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} + + '@radix-ui/rect@1.1.3': + resolution: {integrity: sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==} + + '@rolldown/binding-android-arm64@1.1.5': + resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.1.5': + resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.1.5': resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] @@ -1252,6 +2013,20 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + assistant-cloud@0.1.41: + resolution: {integrity: sha512-lrH9USOoNaAWAAbujeHa/PEiWoqNQHjIPIAeeA3y3GUXOdlmTjIyR/7GbEZBKbHhm1Lyt7aAYKvdxHFkhuEbJw==} + + assistant-stream@0.3.39: + resolution: {integrity: sha512-Ad28saCwQxqaB69w4WNaS3uW5OW5BgTYnzTfbgFnz1CPXZjcWFsMrHhcJUpR4+lfYPTNal0oHEb47VUZH4Zz1g==} + peerDependencies: + ioredis: ^5.10.1 || ^6.0.0 + redis: ^5.12.1 + peerDependenciesMeta: + ioredis: + optional: true + redis: + optional: true + attr-accept@2.2.5: resolution: {integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==} engines: {node: '>=4'} @@ -1306,6 +2081,9 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} @@ -1848,6 +2626,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@6.0.1: + resolution: {integrity: sha512-3wVS3i51pE2pi1k5FFL/95BGfVS0kSsvDVuGXHOtxox/TywUmtgq+3qiTOTbs9J7KfHaXPiN171k/A6dBnaXFw==} + engines: {node: ^22 || ^24 || >=26} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -1953,6 +2736,19 @@ packages: engines: {node: '>=10.13.0'} hasBin: true + radix-ui@1.6.7: + resolution: {integrity: sha512-QBdhh1arIEUvPC0dQ5+nwWAxt7+N+oP/9jPwjJkGFoSk/sqxg32gJtSXGtFh8frAIcS6oC9cx2Q+7KYCQLOAeA==} + 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 + react-dom@19.2.7: resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} peerDependencies: @@ -2020,6 +2816,12 @@ packages: '@types/react': optional: true + react-textarea-autosize@8.5.9: + resolution: {integrity: sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react@19.2.7: resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} @@ -2051,9 +2853,15 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + safe-content-frame@0.0.27: + resolution: {integrity: sha512-IFUSMjElIp8q46wUU5HViHNEqoDWRlCzqqThQmhOLimuXPe/QC6Jr/AQ+BKxSYziQdNXUpapBq4Ir/t6dS0Ldg==} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + secure-json-parse@4.1.0: + resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2212,6 +3020,33 @@ packages: '@types/react': optional: true + use-composed-ref@1.4.0: + resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-isomorphic-layout-effect@1.2.1: + resolution: {integrity: sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-latest@1.3.0: + resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + use-sidecar@1.1.3: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} @@ -2366,13 +3201,104 @@ packages: peerDependencies: zod: ^3.25.0 || ^4.0.0 - zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zustand@5.0.15: + resolution: {integrity: sha512-MpSEjRiBkA9crSYeOUH32rJC7SVqAbm0Fqcqge/bUi2PPoLcBWKOsG+C8mevmpr8TwXHBVkChbbJiyvkE+i/3A==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@assistant-ui/core@0.3.15(@assistant-ui/store@0.3.10(@assistant-ui/tap@0.9.14(@types/react@19.2.17)(react@19.2.7))(@types/react@19.2.17)(react@19.2.7))(@assistant-ui/tap@0.9.14(@types/react@19.2.17)(react@19.2.7))(@types/react@19.2.17)(assistant-cloud@0.1.41)(react@19.2.7)(zustand@5.0.15(@types/react@19.2.17)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)))': + dependencies: + '@assistant-ui/store': 0.3.10(@assistant-ui/tap@0.9.14(@types/react@19.2.17)(react@19.2.7))(@types/react@19.2.17)(react@19.2.7) + '@assistant-ui/tap': 0.9.14(@types/react@19.2.17)(react@19.2.7) + assistant-stream: 0.3.39 + nanoid: 6.0.1 + optionalDependencies: + '@types/react': 19.2.17 + assistant-cloud: 0.1.41 + react: 19.2.7 + zustand: 5.0.15(@types/react@19.2.17)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)) + transitivePeerDependencies: + - ioredis + - redis + + '@assistant-ui/react-markdown@0.14.12(@assistant-ui/react@0.15.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)))(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@assistant-ui/react': 0.15.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + classnames: 2.5.1 + react: 19.2.7 + react-markdown: 10.1.0(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + transitivePeerDependencies: + - '@types/react-dom' + - react-dom + - supports-color + + '@assistant-ui/react@0.15.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7))': + dependencies: + '@assistant-ui/core': 0.3.15(@assistant-ui/store@0.3.10(@assistant-ui/tap@0.9.14(@types/react@19.2.17)(react@19.2.7))(@types/react@19.2.17)(react@19.2.7))(@assistant-ui/tap@0.9.14(@types/react@19.2.17)(react@19.2.7))(@types/react@19.2.17)(assistant-cloud@0.1.41)(react@19.2.7)(zustand@5.0.15(@types/react@19.2.17)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7))) + '@assistant-ui/store': 0.3.10(@assistant-ui/tap@0.9.14(@types/react@19.2.17)(react@19.2.7))(@types/react@19.2.17)(react@19.2.7) + '@assistant-ui/tap': 0.9.14(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-escape-keydown': 1.1.5(@types/react@19.2.17)(react@19.2.7) + assistant-cloud: 0.1.41 + assistant-stream: 0.3.39 + radix-ui: 1.6.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-textarea-autosize: 8.5.9(@types/react@19.2.17)(react@19.2.7) + safe-content-frame: 0.0.27 + zod: 4.4.3 + zustand: 5.0.15(@types/react@19.2.17)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + transitivePeerDependencies: + - immer + - ioredis + - redis + - use-sync-external-store - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + '@assistant-ui/store@0.3.10(@assistant-ui/tap@0.9.14(@types/react@19.2.17)(react@19.2.7))(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@assistant-ui/tap': 0.9.14(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + react: 19.2.7 -snapshots: + '@assistant-ui/tap@0.9.14(@types/react@19.2.17)(react@19.2.7)': + optionalDependencies: + '@types/react': 19.2.17 + react: 19.2.7 '@babel/code-frame@7.29.7': dependencies: @@ -2570,216 +3496,791 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.6.0(jiti@2.7.0))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.6.0(jiti@2.7.0))': + dependencies: + eslint: 10.6.0(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.5': + dependencies: + '@eslint/object-schema': 3.0.5 + debug: 4.4.3 + minimatch: 10.2.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.6.0': + dependencies: + '@eslint/core': 1.2.1 + + '@eslint/core@1.2.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@3.0.5': {} + + '@eslint/plugin-kit@0.7.2': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/dom': 1.7.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@floating-ui/utils@0.2.11': {} + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@oxc-project/types@0.139.0': {} + + '@playwright/test@1.61.1': + dependencies: + playwright: 1.61.1 + + '@radix-ui/number@1.1.3': {} + + '@radix-ui/primitive@1.1.4': {} + + '@radix-ui/primitive@1.1.7': {} + + '@radix-ui/react-accessible-icon@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-accordion@1.2.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collapsible': 1.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-alert-dialog@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dialog': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-alert-dialog@1.1.23(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dialog': 1.1.23(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-arrow@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-arrow@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-aspect-ratio@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-avatar@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-checkbox@1.3.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-collapsible@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-collapsible@1.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-collection@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-collection@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-compose-refs@1.1.5(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-context-menu@2.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-context@1.2.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - eslint: 10.6.0(jiti@2.7.0) - eslint-visitor-keys: 3.4.3 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 - '@eslint-community/regexpp@4.12.2': {} + '@radix-ui/react-dialog@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@eslint/config-array@0.23.5': + '@radix-ui/react-dialog@1.1.23(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@eslint/object-schema': 3.0.5 - debug: 4.4.3 - minimatch: 10.2.5 - transitivePeerDependencies: - - supports-color + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 - '@eslint/config-helpers@0.6.0': + '@radix-ui/react-direction@1.1.4(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@eslint/core': 1.2.1 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 - '@eslint/core@1.2.1': + '@radix-ui/react-dismissable-layer@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@types/json-schema': 7.0.15 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@eslint/js@9.39.4': {} + '@radix-ui/react-dismissable-layer@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@eslint/object-schema@3.0.5': {} + '@radix-ui/react-dropdown-menu@2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@eslint/plugin-kit@0.7.2': + '@radix-ui/react-dropdown-menu@2.1.24(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@eslint/core': 1.2.1 - levn: 0.4.1 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@floating-ui/core@1.7.5': + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@floating-ui/utils': 0.2.11 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 - '@floating-ui/dom@1.7.6': + '@radix-ui/react-focus-guards@1.1.6(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@floating-ui/core': 1.7.5 - '@floating-ui/utils': 0.2.11 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 - '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-focus-scope@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@floating-ui/dom': 1.7.6 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@floating-ui/utils@0.2.11': {} - - '@humanfs/core@0.19.2': + '@radix-ui/react-focus-scope@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@humanfs/types': 0.15.0 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@humanfs/node@0.16.8': + '@radix-ui/react-form@0.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@humanfs/core': 0.19.2 - '@humanfs/types': 0.15.0 - '@humanwhocodes/retry': 0.4.3 - - '@humanfs/types@0.15.0': {} + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-label': 2.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@humanwhocodes/module-importer@1.0.1': {} + '@radix-ui/react-hover-card@1.1.23(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@humanwhocodes/retry@0.4.3': {} + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 - '@jridgewell/gen-mapping@0.3.13': + '@radix-ui/react-id@1.1.4(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 - '@jridgewell/remapping@2.3.5': + '@radix-ui/react-label@2.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@jridgewell/resolve-uri@3.1.2': {} + '@radix-ui/react-menu@2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@jridgewell/sourcemap-codec@1.5.5': {} + '@radix-ui/react-menu@2.1.24(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + '@radix-ui/react-menubar@1.1.24(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': - dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 - '@tybys/wasm-util': 0.10.3 - optional: true + '@radix-ui/react-navigation-menu@1.2.22(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@oxc-project/types@0.139.0': {} + '@radix-ui/react-one-time-password-field@0.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/number': 1.1.3 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@playwright/test@1.61.1': + '@radix-ui/react-password-toggle-field@0.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - playwright: 1.61.1 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/primitive@1.1.4': {} + '@radix-ui/react-popover@1.1.23(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-alert-dialog@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-popper@1.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dialog': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-popper@1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.3 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-arrow@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-portal@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collapsible@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-portal@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collection@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-presence@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': + '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)': + '@radix-ui/react-primitive@2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-dialog@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) - aria-hidden: 1.2.6 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)': + '@radix-ui/react-progress@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-dismissable-layer@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-radio-group@1.4.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-dropdown-menu@2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-roving-focus@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-menu': 2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -2787,112 +4288,137 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)': - dependencies: + '@radix-ui/react-roving-focus@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-focus-scope@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-scroll-area@1.2.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/number': 1.1.3 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-select@2.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/number': 1.1.3 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + aria-hidden: 1.2.6 react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-menu@2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-separator@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-dismissable-layer': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-focus-scope': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-popper': 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) - aria-hidden: 1.2.6 + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-popper@1.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/rect': 1.1.2 + '@radix-ui/react-slider@1.4.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/number': 1.1.3 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-portal@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-slot@1.3.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-switch@1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-roving-focus@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-tabs@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -2900,23 +4426,77 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)': + '@radix-ui/react-tabs@1.1.21(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-tabs@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-toast@1.2.23(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-toggle-group@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-roving-focus': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-toggle@1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-toolbar@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-separator': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle-group': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: @@ -2943,12 +4523,39 @@ snapshots: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@radix-ui/react-tooltip@1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.17 + '@radix-ui/react-use-callback-ref@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)': dependencies: '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) @@ -2957,6 +4564,15 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 + '@radix-ui/react-use-controllable-state@1.2.6(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) @@ -2964,12 +4580,44 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 + '@radix-ui/react-use-effect-event@0.0.5(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-escape-keydown@1.1.5(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-is-hydrated@0.1.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: '@types/react': 19.2.17 + '@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-previous@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: '@radix-ui/rect': 1.1.2 @@ -2977,6 +4625,13 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 + '@radix-ui/react-use-rect@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/rect': 1.1.3 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) @@ -2984,6 +4639,22 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 + '@radix-ui/react-use-size@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-visually-hidden@1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@radix-ui/react-visually-hidden@1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -2995,6 +4666,8 @@ snapshots: '@radix-ui/rect@1.1.2': {} + '@radix-ui/rect@1.1.3': {} + '@rolldown/binding-android-arm64@1.1.5': optional: true @@ -3383,6 +5056,19 @@ snapshots: assertion-error@2.0.1: {} + assistant-cloud@0.1.41: + dependencies: + assistant-stream: 0.3.39 + transitivePeerDependencies: + - ioredis + - redis + + assistant-stream@0.3.39: + dependencies: + '@standard-schema/spec': 1.1.0 + nanoid: 6.0.1 + secure-json-parse: 4.1.0 + attr-accept@2.2.5: {} bail@2.0.2: {} @@ -3423,6 +5109,8 @@ snapshots: dependencies: clsx: 2.1.1 + classnames@2.5.1: {} + cliui@6.0.0: dependencies: string-width: 4.2.3 @@ -4078,6 +5766,8 @@ snapshots: nanoid@3.3.16: {} + nanoid@6.0.1: {} + natural-compare@1.4.0: {} node-releases@2.0.50: {} @@ -4171,6 +5861,69 @@ snapshots: pngjs: 5.0.0 yargs: 15.4.1 + radix-ui@1.6.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-accessible-icon': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-accordion': 1.2.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-alert-dialog': 1.1.23(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-aspect-ratio': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-avatar': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-checkbox': 1.3.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collapsible': 1.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context-menu': 2.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dialog': 1.1.23(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dropdown-menu': 2.1.24(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-form': 0.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-hover-card': 1.1.23(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-label': 2.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-menubar': 1.1.24(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-navigation-menu': 1.2.22(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-one-time-password-field': 0.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-password-toggle-field': 0.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popover': 1.1.23(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-progress': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-radio-group': 1.4.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-scroll-area': 1.2.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-select': 2.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-separator': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slider': 1.4.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-switch': 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tabs': 1.1.21(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toast': 1.2.23(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle-group': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toolbar': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tooltip': 1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-escape-keydown': 1.1.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + react-dom@19.2.7(react@19.2.7): dependencies: react: 19.2.7 @@ -4240,6 +5993,15 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 + react-textarea-autosize@8.5.9(@types/react@19.2.17)(react@19.2.7): + dependencies: + '@babel/runtime': 7.29.2 + react: 19.2.7 + use-composed-ref: 1.4.0(@types/react@19.2.17)(react@19.2.7) + use-latest: 1.3.0(@types/react@19.2.17)(react@19.2.7) + transitivePeerDependencies: + - '@types/react' + react@19.2.7: {} regex-recursion@6.0.2: @@ -4294,8 +6056,12 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.1.5 '@rolldown/binding-win32-x64-msvc': 1.1.5 + safe-content-frame@0.0.27: {} + scheduler@0.27.0: {} + secure-json-parse@4.1.0: {} + semver@6.3.1: {} semver@7.8.5: {} @@ -4454,6 +6220,25 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 + use-composed-ref@1.4.0(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + use-isomorphic-layout-effect@1.2.1(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + use-latest@1.3.0(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + use-isomorphic-layout-effect: 1.2.1(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.7): dependencies: detect-node-es: 1.1.0 @@ -4569,4 +6354,10 @@ snapshots: zod@4.4.3: {} + zustand@5.0.15(@types/react@19.2.17)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)): + optionalDependencies: + '@types/react': 19.2.17 + react: 19.2.7 + use-sync-external-store: 1.6.0(react@19.2.7) + zwitch@2.0.4: {} From 853b744e72ee5d952bfc6e5a1f94c4d685daa5b8 Mon Sep 17 00:00:00 2001 From: liyb Date: Wed, 26 Aug 2026 13:48:16 +0800 Subject: [PATCH 3/4] fix: marketplace tab filter, agents sort order, typography plugin - fix(capabilities): marketplace tab now shows PublishedMarketplaceTab instead of MCPDirectory when no item is selected. Switching to the marketplace tab with mcp filter no longer incorrectly displays the MCP directory catalog. - fix(agents): sort agents list by created_at descending (newest first) both in the SQL query and as a frontend fallback sort. - feat(web): add @tailwindcss/typography plugin so prose classes render proper markdown formatting (paragraph spacing, list bullets, code blocks, headings) in the conversation thread. --- apps/web/package.json | 1 + apps/web/src/pages/admin/AgentsPage.tsx | 10 +++++- .../admin/capabilities/MarketplaceTab.tsx | 13 ++----- apps/web/src/style.css | 1 + pnpm-lock.yaml | 34 +++++++++++++++++++ server/internal/db/queries/store.sql | 2 +- server/internal/db/sqlc/store.sql.go | 2 +- 7 files changed, 49 insertions(+), 14 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 9491e218..5db9c15c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -21,6 +21,7 @@ "@radix-ui/react-slot": "^1.3.0", "@radix-ui/react-tabs": "^1.1.16", "@radix-ui/react-tooltip": "^1.2.11", + "@tailwindcss/typography": "^0.5.20", "@tanstack/react-query": "^5.101.2", "@types/react": "^19.2.17", "@types/react-dom": "^19.1.0", diff --git a/apps/web/src/pages/admin/AgentsPage.tsx b/apps/web/src/pages/admin/AgentsPage.tsx index 42ed8f44..91157453 100644 --- a/apps/web/src/pages/admin/AgentsPage.tsx +++ b/apps/web/src/pages/admin/AgentsPage.tsx @@ -82,7 +82,15 @@ export function AgentsPage() { const updateProfileMut = useUpdateAgentProfile(wid) const deleteMut = useDeleteAgent(wid) const workspacesQ = useMyWorkspaces() - const agents = useMemo(() => query.data?.agents ?? [], [query.data]) + const agents = useMemo(() => { + const list = query.data?.agents ?? [] + // Sort: newest first (by enabled_at / created_at descending) + return [...list].sort((a, b) => { + const ta = a.enabled_at ?? "" + const tb = b.enabled_at ?? "" + return tb.localeCompare(ta) + }) + }, [query.data]) const models = modelsQ.data?.models ?? [] const currentWorkspace = workspacesQ.data?.workspaces.find((w) => w.id === wid) const workspaceRole = currentWorkspace?.role diff --git a/apps/web/src/pages/admin/capabilities/MarketplaceTab.tsx b/apps/web/src/pages/admin/capabilities/MarketplaceTab.tsx index 9fdfc5e0..8d03f853 100644 --- a/apps/web/src/pages/admin/capabilities/MarketplaceTab.tsx +++ b/apps/web/src/pages/admin/capabilities/MarketplaceTab.tsx @@ -50,17 +50,8 @@ export function MarketplaceTab(props: MarketplaceTabProps) { return } - return props.onSelectItem(id ? `mcp:${id}` : null)} - onSelectMarketplaceItem={props.onSelectItem} - onInstallMarketplace={props.onInstall} - canManageMarketplace={props.canManage} - onDeleteMarketplace={props.onDelete} - onViewCapability={props.onViewCapability} - /> + // Default: show the published marketplace listing (not the MCP directory) + return } function PublishedMarketplaceTab({ itemID, query, typeFilter, canManage, onSelectItem, onInstall, onDelete, onViewCapability }: MarketplaceTabProps) { diff --git a/apps/web/src/style.css b/apps/web/src/style.css index 8568533e..4ec76c33 100644 --- a/apps/web/src/style.css +++ b/apps/web/src/style.css @@ -1,4 +1,5 @@ @import "tailwindcss"; +@plugin "@tailwindcss/typography"; /* Tailwind v4 does not emit a utility for this custom scale entry unless it is declared explicitly. Keep the compact file-tree size in the design diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 58158024..020277bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: '@radix-ui/react-tooltip': specifier: ^1.2.11 version: 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tailwindcss/typography': + specifier: ^0.5.20 + version: 0.5.20(tailwindcss@4.3.2) '@tanstack/react-query': specifier: ^5.101.2 version: 5.101.2(react@19.2.7) @@ -1814,6 +1817,11 @@ packages: resolution: {integrity: sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==} engines: {node: '>= 20'} + '@tailwindcss/typography@0.5.20': + resolution: {integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==} + peerDependencies: + tailwindcss: '>=3.0.0 || >=4.0.0 || insiders' + '@tailwindcss/vite@4.3.2': resolution: {integrity: sha512-eHpMeX4JXfVNJDEcsouTeCBubJBTcTLigeaw/NTUW6PB5ATKKXdyonnXgTBX2VuRbjz1hjfz6C5XAhr52ImQXA==} peerDependencies: @@ -2112,6 +2120,11 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -2711,6 +2724,10 @@ packages: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss@8.5.19: resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} engines: {node: ^10 || ^12 || >=14} @@ -3062,6 +3079,9 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} @@ -4822,6 +4842,11 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.2 '@tailwindcss/oxide-win32-x64-msvc': 4.3.2 + '@tailwindcss/typography@0.5.20(tailwindcss@4.3.2)': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 4.3.2 + '@tailwindcss/vite@4.3.2(vite@8.1.4(@types/node@26.1.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4))': dependencies: '@tailwindcss/node': 4.3.2 @@ -5137,6 +5162,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + cssesc@3.0.0: {} + csstype@3.2.3: {} debug@4.4.3: @@ -5841,6 +5868,11 @@ snapshots: pngjs@5.0.0: {} + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss@8.5.19: dependencies: nanoid: 3.3.16 @@ -6251,6 +6283,8 @@ snapshots: dependencies: react: 19.2.7 + util-deprecate@1.0.2: {} + vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 diff --git a/server/internal/db/queries/store.sql b/server/internal/db/queries/store.sql index 3d891dd7..55a1627f 100644 --- a/server/internal/db/queries/store.sql +++ b/server/internal/db/queries/store.sql @@ -934,7 +934,7 @@ left join sandboxes sb on sb.agent_id = a.id and sb.killed_at is null where a.workspace_id = @workspace_id::uuid and a.deleted_at is null -order by case when a.status = 'active' then 0 else 1 end, a.name asc; +order by case when a.status = 'active' then 0 else 1 end, a.created_at desc; -- name: ListConversationMessages :many select diff --git a/server/internal/db/sqlc/store.sql.go b/server/internal/db/sqlc/store.sql.go index 6f73a0ac..1497dc99 100644 --- a/server/internal/db/sqlc/store.sql.go +++ b/server/internal/db/sqlc/store.sql.go @@ -9671,7 +9671,7 @@ left join sandboxes sb on sb.agent_id = a.id and sb.killed_at is null where a.workspace_id = $1::uuid and a.deleted_at is null -order by case when a.status = 'active' then 0 else 1 end, a.name asc +order by case when a.status = 'active' then 0 else 1 end, a.created_at desc ` type ListWorkspaceAgentsAdminRow struct { From e785b506fc6e98fdc7e1fbbdfdd6ecbebc8ccdec Mon Sep 17 00:00:00 2001 From: liyb Date: Wed, 26 Aug 2026 14:09:03 +0800 Subject: [PATCH 4/4] fix(web): pass active state to working steps --- apps/web/src/components/conversation/ParsarChatView.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/src/components/conversation/ParsarChatView.tsx b/apps/web/src/components/conversation/ParsarChatView.tsx index 0734bed4..2f6d653d 100644 --- a/apps/web/src/components/conversation/ParsarChatView.tsx +++ b/apps/web/src/components/conversation/ParsarChatView.tsx @@ -191,6 +191,7 @@ export function ParsarChatView({