diff --git a/crates/zclaw-runtime/src/compaction.rs b/crates/zclaw-runtime/src/compaction.rs index 2e5501b..f4903bb 100644 --- a/crates/zclaw-runtime/src/compaction.rs +++ b/crates/zclaw-runtime/src/compaction.rs @@ -5,8 +5,18 @@ //! token count exceeds the configured threshold, older messages are //! summarized into a single system message and only recent messages are //! retained. +//! +//! Supports two compaction modes: +//! - **Rule-based**: Heuristic topic extraction (default, no LLM needed) +//! - **LLM-based**: Uses an LLM driver to generate higher-quality summaries +//! +//! Optionally flushes old messages to the growth/memory system before discarding. -use zclaw_types::Message; +use std::sync::Arc; +use zclaw_types::{AgentId, Message, SessionId}; + +use crate::driver::{CompletionRequest, ContentBlock, LlmDriver}; +use crate::growth::GrowthIntegration; /// Number of recent messages to preserve after compaction. const DEFAULT_KEEP_RECENT: usize = 6; @@ -146,6 +156,272 @@ pub fn maybe_compact(messages: Vec, threshold: usize) -> Vec { compacted } +/// Configuration for compaction behavior. +#[derive(Debug, Clone)] +pub struct CompactionConfig { + /// Use LLM for generating summaries instead of rule-based extraction. + pub use_llm: bool, + /// Fall back to rule-based summary if LLM fails. + pub llm_fallback_to_rules: bool, + /// Flush memories from old messages before discarding them. + pub memory_flush_enabled: bool, + /// Maximum tokens for LLM-generated summary. + pub summary_max_tokens: u32, +} + +impl Default for CompactionConfig { + fn default() -> Self { + Self { + use_llm: false, + llm_fallback_to_rules: true, + memory_flush_enabled: false, + summary_max_tokens: 500, + } + } +} + +/// Outcome of an async compaction operation. +#[derive(Debug, Clone)] +pub struct CompactionOutcome { + /// The (possibly compacted) message list. + pub messages: Vec, + /// Number of messages removed during compaction. + pub removed_count: usize, + /// Number of memories flushed to the growth system. + pub flushed_memories: usize, + /// Whether LLM was used for summary generation. + pub used_llm: bool, +} + +/// Async compaction with optional LLM summary and memory flushing. +/// +/// When `messages` exceed `threshold` tokens: +/// 1. If `memory_flush_enabled`, extract memories from old messages via growth system +/// 2. Generate summary (LLM or rule-based depending on config) +/// 3. Replace old messages with summary + keep recent messages +pub async fn maybe_compact_with_config( + messages: Vec, + threshold: usize, + config: &CompactionConfig, + agent_id: &AgentId, + session_id: &SessionId, + driver: Option<&Arc>, + growth: Option<&GrowthIntegration>, +) -> CompactionOutcome { + let tokens = estimate_messages_tokens(&messages); + if tokens < threshold { + return CompactionOutcome { + messages, + removed_count: 0, + flushed_memories: 0, + used_llm: false, + }; + } + + tracing::info!( + "[Compaction] Triggered: {} tokens > {} threshold, {} messages", + tokens, + threshold, + messages.len(), + ); + + // Step 1: Flush memories from messages that are about to be compacted + let flushed_memories = if config.memory_flush_enabled { + if let Some(growth) = growth { + match growth + .process_conversation(agent_id, &messages, session_id.clone()) + .await + { + Ok(count) => { + tracing::info!( + "[Compaction] Flushed {} memories before compaction", + count + ); + count + } + Err(e) => { + tracing::warn!("[Compaction] Memory flush failed: {}", e); + 0 + } + } + } else { + tracing::debug!("[Compaction] Memory flush requested but no growth integration available"); + 0 + } + } else { + 0 + }; + + // Step 2: Determine split point (same logic as compact_messages) + let leading_system_count = messages + .iter() + .take_while(|m| matches!(m, Message::System { .. })) + .count(); + let keep_from_end = DEFAULT_KEEP_RECENT + .min(messages.len().saturating_sub(leading_system_count)); + let split_index = messages.len().saturating_sub(keep_from_end); + let split_index = split_index.max(leading_system_count); + + if split_index == 0 { + return CompactionOutcome { + messages, + removed_count: 0, + flushed_memories, + used_llm: false, + }; + } + + let old_messages = &messages[..split_index]; + let recent_messages = &messages[split_index..]; + let removed_count = old_messages.len(); + + // Step 3: Generate summary (LLM or rule-based) + let summary = if config.use_llm { + if let Some(driver) = driver { + match generate_llm_summary(driver, old_messages, config.summary_max_tokens).await { + Ok(llm_summary) => { + tracing::info!( + "[Compaction] Generated LLM summary ({} chars)", + llm_summary.len() + ); + llm_summary + } + Err(e) => { + if config.llm_fallback_to_rules { + tracing::warn!( + "[Compaction] LLM summary failed: {}, falling back to rules", + e + ); + generate_summary(old_messages) + } else { + tracing::warn!( + "[Compaction] LLM summary failed: {}, returning original messages", + e + ); + return CompactionOutcome { + messages, + removed_count: 0, + flushed_memories, + used_llm: false, + }; + } + } + } + } else { + tracing::warn!( + "[Compaction] LLM compaction requested but no driver available, using rules" + ); + generate_summary(old_messages) + } + } else { + generate_summary(old_messages) + }; + + let used_llm = config.use_llm && driver.is_some(); + + // Step 4: Build compacted message list + let mut compacted = Vec::with_capacity(1 + recent_messages.len()); + compacted.push(Message::system(summary)); + compacted.extend(recent_messages.iter().cloned()); + + tracing::info!( + "[Compaction] Removed {} messages, {} remain (llm={})", + removed_count, + compacted.len(), + used_llm, + ); + + CompactionOutcome { + messages: compacted, + removed_count, + flushed_memories, + used_llm, + } +} + +/// Generate a summary using an LLM driver. +async fn generate_llm_summary( + driver: &Arc, + messages: &[Message], + max_tokens: u32, +) -> Result { + let mut conversation_text = String::new(); + for msg in messages { + match msg { + Message::User { content } => { + conversation_text.push_str(&format!("用户: {}\n", content)) + } + Message::Assistant { content, .. } => { + conversation_text.push_str(&format!("助手: {}\n", content)) + } + Message::System { content } => { + if !content.starts_with("[以下是之前对话的摘要]") { + conversation_text.push_str(&format!("[系统]: {}\n", content)) + } + } + Message::ToolUse { tool, input, .. } => { + conversation_text.push_str(&format!( + "[工具调用 {}]: {}\n", + tool.as_str(), + input + )) + } + Message::ToolResult { output, .. } => { + conversation_text.push_str(&format!("[工具结果]: {}\n", output)) + } + } + } + + // Truncate conversation text if too long for the prompt itself + let max_conversation_chars = 8000; + if conversation_text.len() > max_conversation_chars { + conversation_text.truncate(max_conversation_chars); + conversation_text.push_str("\n...(对话已截断)"); + } + + let prompt = format!( + "请用简洁的中文总结以下对话的关键信息。保留重要的讨论主题、决策、结论和待办事项。\ + 输出格式为段落式摘要,不超过200字。\n\n{}", + conversation_text + ); + + let request = CompletionRequest { + model: String::new(), + system: Some( + "你是一个对话摘要助手。只输出摘要内容,不要添加额外解释。".to_string(), + ), + messages: vec![Message::user(&prompt)], + tools: Vec::new(), + max_tokens: Some(max_tokens), + temperature: Some(0.3), + stop: Vec::new(), + stream: false, + }; + + let response = driver + .complete(request) + .await + .map_err(|e| format!("{}", e))?; + + // Extract text from content blocks + let text_parts: Vec = response + .content + .iter() + .filter_map(|block| match block { + ContentBlock::Text { text } => Some(text.clone()), + _ => None, + }) + .collect(); + + let summary = text_parts.join(""); + + if summary.is_empty() { + return Err("LLM returned empty response".to_string()); + } + + Ok(summary) +} + /// Generate a rule-based summary of old messages. fn generate_summary(messages: &[Message]) -> String { if messages.is_empty() { diff --git a/crates/zclaw-runtime/src/lib.rs b/crates/zclaw-runtime/src/lib.rs index cbf6426..8805603 100644 --- a/crates/zclaw-runtime/src/lib.rs +++ b/crates/zclaw-runtime/src/lib.rs @@ -24,3 +24,4 @@ pub use loop_runner::{AgentLoop, AgentLoopResult, LoopEvent}; pub use loop_guard::{LoopGuard, LoopGuardConfig, LoopGuardResult}; pub use stream::{StreamEvent, StreamSender}; pub use growth::GrowthIntegration; +pub use compaction::{CompactionConfig, CompactionOutcome}; diff --git a/crates/zclaw-runtime/src/loop_runner.rs b/crates/zclaw-runtime/src/loop_runner.rs index 4ba8797..c25b9ab 100644 --- a/crates/zclaw-runtime/src/loop_runner.rs +++ b/crates/zclaw-runtime/src/loop_runner.rs @@ -12,7 +12,7 @@ use crate::tool::{ToolRegistry, ToolContext, SkillExecutor}; use crate::tool::builtin::PathValidator; use crate::loop_guard::{LoopGuard, LoopGuardResult}; use crate::growth::GrowthIntegration; -use crate::compaction; +use crate::compaction::{self, CompactionConfig}; use zclaw_memory::MemoryStore; /// Agent loop runner @@ -32,6 +32,8 @@ pub struct AgentLoop { growth: Option, /// Compaction threshold in tokens (0 = disabled) compaction_threshold: usize, + /// Compaction behavior configuration + compaction_config: CompactionConfig, } impl AgentLoop { @@ -55,6 +57,7 @@ impl AgentLoop { path_validator: None, growth: None, compaction_threshold: 0, + compaction_config: CompactionConfig::default(), } } @@ -115,6 +118,12 @@ impl AgentLoop { self } + /// Set compaction configuration (LLM mode, memory flushing, etc.) + pub fn with_compaction_config(mut self, config: CompactionConfig) -> Self { + self.compaction_config = config; + self + } + /// Get growth integration reference pub fn growth(&self) -> Option<&GrowthIntegration> { self.growth.as_ref() @@ -150,7 +159,23 @@ impl AgentLoop { // Apply compaction if threshold is configured if self.compaction_threshold > 0 { - messages = compaction::maybe_compact(messages, self.compaction_threshold); + let needs_async = + self.compaction_config.use_llm || self.compaction_config.memory_flush_enabled; + if needs_async { + let outcome = compaction::maybe_compact_with_config( + messages, + self.compaction_threshold, + &self.compaction_config, + &self.agent_id, + &session_id, + Some(&self.driver), + self.growth.as_ref(), + ) + .await; + messages = outcome.messages; + } else { + messages = compaction::maybe_compact(messages, self.compaction_threshold); + } } // Enhance system prompt with growth memories @@ -316,7 +341,23 @@ impl AgentLoop { // Apply compaction if threshold is configured if self.compaction_threshold > 0 { - messages = compaction::maybe_compact(messages, self.compaction_threshold); + let needs_async = + self.compaction_config.use_llm || self.compaction_config.memory_flush_enabled; + if needs_async { + let outcome = compaction::maybe_compact_with_config( + messages, + self.compaction_threshold, + &self.compaction_config, + &self.agent_id, + &session_id, + Some(&self.driver), + self.growth.as_ref(), + ) + .await; + messages = outcome.messages; + } else { + messages = compaction::maybe_compact(messages, self.compaction_threshold); + } } // Enhance system prompt with growth memories diff --git a/desktop/package.json b/desktop/package.json index 6d1a67b..df0e76a 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -45,7 +45,10 @@ "lucide-react": "^0.577.0", "react": "^19.2.4", "react-dom": "^19.2.4", + "react-markdown": "^10.1.0", "react-window": "^2.2.7", + "recharts": "^3.8.1", + "remark-gfm": "^4.0.1", "smol-toml": "^1.6.1", "tailwind-merge": "^3.5.0", "tweetnacl": "^1.0.3", diff --git a/desktop/pnpm-lock.yaml b/desktop/pnpm-lock.yaml index 22a9e7a..4613ea8 100644 --- a/desktop/pnpm-lock.yaml +++ b/desktop/pnpm-lock.yaml @@ -17,21 +17,12 @@ importers: '@tauri-apps/plugin-opener': specifier: ^2.5.3 version: 2.5.3 - '@xstate/react': - specifier: ^6.1.0 - version: 6.1.0(@types/react@19.2.14)(react@19.2.4)(xstate@5.29.0) '@xyflow/react': specifier: ^12.10.1 - version: 12.10.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 12.10.1(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) clsx: specifier: ^2.1.1 version: 2.1.1 - dagre: - specifier: ^0.8.5 - version: 0.8.5 - date-fns: - specifier: ^4.1.0 - version: 4.1.0 framer-motion: specifier: ^12.38.0 version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -44,9 +35,18 @@ importers: react-dom: specifier: ^19.2.4 version: 19.2.4(react@19.2.4) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@19.2.14)(react@19.2.4) react-window: specifier: ^2.2.7 version: 2.2.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + recharts: + specifier: ^3.8.1 + version: 3.8.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react-is@17.0.2)(react@19.2.4)(redux@5.0.1) + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 smol-toml: specifier: ^1.6.1 version: 1.6.1 @@ -59,15 +59,9 @@ importers: uuid: specifier: ^11.1.0 version: 11.1.0 - valtio: - specifier: ^2.3.1 - version: 2.3.1(@types/react@19.2.14)(react@19.2.4) - xstate: - specifier: ^5.29.0 - version: 5.29.0 zustand: specifier: ^5.0.12 - version: 5.0.12(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) + version: 5.0.12(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) devDependencies: '@eslint/js': specifier: ^10.0.1 @@ -684,6 +678,17 @@ packages: engines: {node: '>=18'} hasBin: true + '@reduxjs/toolkit@2.11.2': + resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + '@rolldown/binding-android-arm64@1.0.0-rc.12': resolution: {integrity: sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -923,6 +928,12 @@ packages: cpu: [x64] os: [win32] + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@tailwindcss/node@4.2.2': resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} @@ -1140,36 +1151,72 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + '@types/d3-color@3.1.3': resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} '@types/d3-drag@3.0.7': resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + '@types/d3-interpolate@3.0.4': resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + '@types/d3-selection@3.0.11': resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + '@types/d3-transition@3.0.9': resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} '@types/d3-zoom@3.0.8': resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -1182,6 +1229,15 @@ packages: '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} @@ -1244,6 +1300,9 @@ packages: resolution: {integrity: sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -1288,15 +1347,6 @@ packages: '@vitest/utils@2.1.9': resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} - '@xstate/react@6.1.0': - resolution: {integrity: sha512-ep9F0jGTI63B/jE8GHdMpUqtuz7yRebNaKv8EMUaiSi29NOglywc2X2YSOV/ygbIK+LtmgZ0q9anoEA2iBSEOw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - xstate: ^5.28.0 - peerDependenciesMeta: - xstate: - optional: true - '@xyflow/react@12.10.1': resolution: {integrity: sha512-5eSWtIK/+rkldOuFbOOz44CRgQRjtS9v5nufk77DV+XBnfCGL9HAQ8PG00o2ZYKqkEU/Ak6wrKC95Tu+2zuK3Q==} peerDependencies: @@ -1400,6 +1450,9 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1446,6 +1499,9 @@ packages: caniuse-lite@1.0.30001781: resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@5.3.3: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} @@ -1454,6 +1510,18 @@ packages: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} engines: {node: '>=8'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + check-error@2.1.3: resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} @@ -1476,6 +1544,9 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1496,6 +1567,10 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + d3-color@3.1.0: resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} engines: {node: '>=12'} @@ -1512,14 +1587,38 @@ packages: resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} engines: {node: '>=12'} + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + d3-interpolate@3.0.1: resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} engines: {node: '>=12'} + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + d3-selection@3.0.0: resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} engines: {node: '>=12'} + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + d3-timer@3.0.1: resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} engines: {node: '>=12'} @@ -1534,9 +1633,6 @@ packages: resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} engines: {node: '>=12'} - dagre@0.8.5: - resolution: {integrity: sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==} - data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} @@ -1553,9 +1649,6 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} - date-fns@4.1.0: - resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -1565,9 +1658,15 @@ packages: supports-color: optional: true + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -1595,6 +1694,9 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -1664,6 +1766,9 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + es-toolkit@1.45.1: + resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -1682,6 +1787,10 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + eslint-plugin-react-hooks@7.0.1: resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} engines: {node: '>=18'} @@ -1732,6 +1841,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -1739,10 +1851,16 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1865,9 +1983,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphlib@2.1.8: - resolution: {integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==} - has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -1895,6 +2010,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} @@ -1908,6 +2029,9 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -1928,6 +2052,12 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immer@10.2.0: + resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} + + immer@11.1.4: + resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -1936,10 +2066,23 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -1972,6 +2115,9 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1992,6 +2138,9 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -2004,6 +2153,10 @@ packages: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -2199,6 +2352,9 @@ packages: lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2231,10 +2387,142 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -2336,6 +2624,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -2460,8 +2751,8 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - proxy-compare@3.0.1: - resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} @@ -2478,6 +2769,24 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + react-refresh@0.17.0: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} @@ -2492,10 +2801,26 @@ packages: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} + recharts@3.8.1: + resolution: {integrity: sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg==} + engines: {node: '>=18'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -2504,6 +2829,21 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve@2.0.0-next.6: resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} engines: {node: '>= 0.4'} @@ -2607,6 +2947,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -2644,6 +2987,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2656,6 +3002,12 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2681,6 +3033,9 @@ packages: resolution: {integrity: sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==} engines: {node: '>=18'} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -2718,6 +3073,12 @@ packages: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -2766,6 +3127,24 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -2775,15 +3154,6 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - 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-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: @@ -2793,17 +3163,14 @@ packages: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true - valtio@2.3.1: - resolution: {integrity: sha512-ZsOaOEn0U9IJ96cAj3CZ3GjwpN3EJdjsi1PT4PREuB+Pcqfsczu16isT5DT1UrmHbk4PtLjk8kwNEHuR2CX56w==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=18.0.0' - react: '>=18.0.0' - peerDependenciesMeta: - '@types/react': - optional: true - react: - optional: true + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + victory-vendor@37.3.6: + resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} vite-node@2.1.9: resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} @@ -2987,9 +3354,6 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - xstate@5.29.0: - resolution: {integrity: sha512-p0hiOPhhBgXn29t18zDScaN95+y1MAu1Pz5Z2IduCuOUh+d3RqJO7fmexbuQ6rlwFNgYFeXvFsFeiuiAiH3mhg==} - yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -3039,6 +3403,9 @@ packages: use-sync-external-store: optional: true + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@adobe/css-tools@4.4.4': {} @@ -3452,6 +3819,18 @@ snapshots: dependencies: playwright: 1.58.2 + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1))(react@19.2.4)': + dependencies: + '@standard-schema/spec': 1.1.0 + '@standard-schema/utils': 0.3.0 + immer: 11.1.4 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 19.2.4 + react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1) + '@rolldown/binding-android-arm64@1.0.0-rc.12': optional: true @@ -3578,6 +3957,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.0': optional: true + '@standard-schema/spec@1.1.0': {} + + '@standard-schema/utils@0.3.0': {} + '@tailwindcss/node@4.2.2': dependencies: '@jridgewell/remapping': 2.3.5 @@ -3758,18 +4141,36 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@types/d3-array@3.2.2': {} + '@types/d3-color@3.1.3': {} '@types/d3-drag@3.0.7': dependencies: '@types/d3-selection': 3.0.11 + '@types/d3-ease@3.0.2': {} + '@types/d3-interpolate@3.0.4': dependencies: '@types/d3-color': 3.1.3 + '@types/d3-path@3.1.1': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + '@types/d3-selection@3.0.11': {} + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + '@types/d3-transition@3.0.9': dependencies: '@types/d3-selection': 3.0.11 @@ -3779,14 +4180,32 @@ snapshots: '@types/d3-interpolate': 3.0.4 '@types/d3-selection': 3.0.11 + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + '@types/esrecurse@4.3.1': {} + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + '@types/estree@1.0.8': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/js-yaml@4.0.9': {} '@types/json-schema@7.0.15': {} + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: '@types/react': 19.2.14 @@ -3802,6 +4221,12 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/use-sync-external-store@0.0.6': {} + '@types/uuid@10.0.0': {} '@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@10.1.0(jiti@2.6.1))(typescript@5.8.3))(eslint@10.1.0(jiti@2.6.1))(typescript@5.8.3)': @@ -3895,6 +4320,8 @@ snapshots: '@typescript-eslint/types': 8.57.2 eslint-visitor-keys: 5.0.1 + '@ungap/structured-clone@1.3.0': {} + '@vitejs/plugin-react@4.7.0(vite@8.0.3(esbuild@0.27.4)(jiti@2.6.1))': dependencies: '@babel/core': 7.29.0 @@ -3965,23 +4392,13 @@ snapshots: loupe: 3.2.1 tinyrainbow: 1.2.0 - '@xstate/react@6.1.0(@types/react@19.2.14)(react@19.2.4)(xstate@5.29.0)': - dependencies: - react: 19.2.4 - use-isomorphic-layout-effect: 1.2.1(@types/react@19.2.14)(react@19.2.4) - use-sync-external-store: 1.6.0(react@19.2.4) - optionalDependencies: - xstate: 5.29.0 - transitivePeerDependencies: - - '@types/react' - - '@xyflow/react@12.10.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@xyflow/react@12.10.1(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@xyflow/system': 0.0.75 classcat: 5.0.5 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - zustand: 4.5.7(@types/react@19.2.14)(react@19.2.4) + zustand: 4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4) transitivePeerDependencies: - '@types/react' - immer @@ -4107,6 +4524,8 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + bail@2.0.2: {} + balanced-match@1.0.2: {} balanced-match@4.0.4: {} @@ -4155,6 +4574,8 @@ snapshots: caniuse-lite@1.0.30001781: {} + ccount@2.0.1: {} + chai@5.3.3: dependencies: assertion-error: 2.0.1 @@ -4168,6 +4589,14 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + check-error@2.1.3: {} classcat@5.0.5: {} @@ -4184,6 +4613,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + comma-separated-tokens@2.0.3: {} + concat-map@0.0.1: {} convert-source-map@2.0.0: {} @@ -4203,6 +4634,10 @@ snapshots: csstype@3.2.3: {} + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + d3-color@3.1.0: {} d3-dispatch@3.0.1: {} @@ -4214,12 +4649,36 @@ snapshots: d3-ease@3.0.1: {} + d3-format@3.1.2: {} + d3-interpolate@3.0.1: dependencies: d3-color: 3.1.0 + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-selection@3.0.0: {} + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + d3-timer@3.0.1: {} d3-transition@3.0.1(d3-selection@3.0.0): @@ -4239,11 +4698,6 @@ snapshots: d3-selection: 3.0.0 d3-transition: 3.0.1(d3-selection@3.0.0) - dagre@0.8.5: - dependencies: - graphlib: 2.1.8 - lodash: 4.17.23 - data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 @@ -4267,14 +4721,18 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 - date-fns@4.1.0: {} - debug@4.4.3: dependencies: ms: 2.1.3 + decimal.js-light@2.5.1: {} + decimal.js@10.6.0: {} + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + deep-eql@5.0.2: {} deep-is@0.1.4: {} @@ -4297,6 +4755,10 @@ snapshots: detect-libc@2.1.2: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -4430,6 +4892,8 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + es-toolkit@1.45.1: {} + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -4490,6 +4954,8 @@ snapshots: escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + eslint-plugin-react-hooks@7.0.1(eslint@10.1.0(jiti@2.6.1)): dependencies: '@babel/core': 7.29.0 @@ -4587,14 +5053,20 @@ snapshots: estraverse@5.3.0: {} + estree-util-is-identifier-name@3.0.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 esutils@2.0.3: {} + eventemitter3@5.0.4: {} + expect-type@1.3.0: {} + extend@3.0.2: {} + fast-deep-equal@3.1.3: {} fast-json-stable-stringify@2.1.0: {} @@ -4718,10 +5190,6 @@ snapshots: graceful-fs@4.2.11: {} - graphlib@2.1.8: - dependencies: - lodash: 4.17.23 - has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -4744,6 +5212,30 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + hermes-estree@0.25.1: {} hermes-parser@0.25.1: @@ -4756,6 +5248,8 @@ snapshots: html-escaper@2.0.2: {} + html-url-attributes@3.0.1: {} + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 @@ -4778,16 +5272,31 @@ snapshots: ignore@7.0.5: {} + immer@10.2.0: {} + + immer@11.1.4: {} + imurmurhash@0.1.4: {} indent-string@4.0.0: {} + inline-style-parser@0.2.7: {} + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.1.0 + internmap@2.0.3: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -4828,6 +5337,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-decimal@2.0.1: {} + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -4848,6 +5359,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-hexadecimal@2.0.1: {} + is-map@2.0.3: {} is-negative-zero@2.0.3: {} @@ -4857,6 +5370,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-plain-obj@4.1.0: {} + is-potential-custom-element-name@1.0.1: {} is-regex@1.2.1: @@ -5051,6 +5566,8 @@ snapshots: lodash@4.17.23: {} + longest-streak@3.1.0: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -5083,8 +5600,354 @@ snapshots: dependencies: semver: 7.7.4 + markdown-table@3.0.4: {} + math-intrinsics@1.1.0: {} + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + mime-db@1.52.0: {} mime-types@2.1.35: @@ -5191,6 +6054,16 @@ snapshots: package-json-from-dist@1.0.1: {} + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -5252,7 +6125,7 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 - proxy-compare@3.0.1: {} + property-information@7.1.0: {} punycode@2.3.1: {} @@ -5265,6 +6138,33 @@ snapshots: react-is@17.0.2: {} + react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.4): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.2.14 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 19.2.4 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 19.2.4 + use-sync-external-store: 1.6.0(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + redux: 5.0.1 + react-refresh@0.17.0: {} react-window@2.2.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4): @@ -5274,11 +6174,37 @@ snapshots: react@19.2.4: {} + recharts@3.8.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react-is@17.0.2)(react@19.2.4)(redux@5.0.1): + dependencies: + '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1))(react@19.2.4) + clsx: 2.1.1 + decimal.js-light: 2.5.1 + es-toolkit: 1.45.1 + eventemitter3: 5.0.4 + immer: 10.2.0 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + react-is: 17.0.2 + react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1) + reselect: 5.1.1 + tiny-invariant: 1.3.3 + use-sync-external-store: 1.6.0(react@19.2.4) + victory-vendor: 37.3.6 + transitivePeerDependencies: + - '@types/react' + - redux + redent@3.0.0: dependencies: indent-string: 4.0.0 strip-indent: 3.0.0 + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -5299,6 +6225,42 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + reselect@5.1.1: {} + resolve@2.0.0-next.6: dependencies: es-errors: 1.3.0 @@ -5459,6 +6421,8 @@ snapshots: source-map-js@1.2.1: {} + space-separated-tokens@2.0.2: {} + stackback@0.0.2: {} std-env@3.10.0: {} @@ -5524,6 +6488,11 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -5536,6 +6505,14 @@ snapshots: dependencies: min-indent: 1.0.1 + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -5556,6 +6533,8 @@ snapshots: glob: 10.5.0 minimatch: 10.2.4 + tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -5585,6 +6564,10 @@ snapshots: dependencies: punycode: 2.3.1 + trim-lines@3.0.1: {} + + trough@2.2.0: {} + ts-api-utils@2.5.0(typescript@5.8.3): dependencies: typescript: 5.8.3 @@ -5650,6 +6633,39 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 @@ -5660,24 +6676,38 @@ snapshots: dependencies: punycode: 2.3.1 - use-isomorphic-layout-effect@1.2.1(@types/react@19.2.14)(react@19.2.4): - dependencies: - react: 19.2.4 - optionalDependencies: - '@types/react': 19.2.14 - use-sync-external-store@1.6.0(react@19.2.4): dependencies: react: 19.2.4 uuid@11.1.0: {} - valtio@2.3.1(@types/react@19.2.14)(react@19.2.4): + vfile-message@4.0.3: dependencies: - proxy-compare: 3.0.1 - optionalDependencies: - '@types/react': 19.2.14 - react: 19.2.4 + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + victory-vendor@37.3.6: + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 vite-node@2.1.9(lightningcss@1.32.0): dependencies: @@ -5840,8 +6870,6 @@ snapshots: xmlchars@2.2.0: {} - xstate@5.29.0: {} - yallist@3.1.1: {} yocto-queue@0.1.0: {} @@ -5852,15 +6880,19 @@ snapshots: zod@4.3.6: {} - zustand@4.5.7(@types/react@19.2.14)(react@19.2.4): + zustand@4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4): dependencies: use-sync-external-store: 1.6.0(react@19.2.4) optionalDependencies: '@types/react': 19.2.14 + immer: 11.1.4 react: 19.2.4 - zustand@5.0.12(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): + zustand@5.0.12(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): optionalDependencies: '@types/react': 19.2.14 + immer: 11.1.4 react: 19.2.4 use-sync-external-store: 1.6.0(react@19.2.4) + + zwitch@2.0.4: {} diff --git a/desktop/src-tauri/src/intelligence/compactor.rs b/desktop/src-tauri/src/intelligence/compactor.rs index 9ebe835..e832aab 100644 --- a/desktop/src-tauri/src/intelligence/compactor.rs +++ b/desktop/src-tauri/src/intelligence/compactor.rs @@ -539,14 +539,26 @@ pub fn compactor_check_threshold( /// Execute compaction #[tauri::command] -pub fn compactor_compact( +pub async fn compactor_compact( messages: Vec, agent_id: String, conversation_id: Option, config: Option, ) -> CompactionResult { + let memory_flush = config + .as_ref() + .map(|c| c.memory_flush_enabled) + .unwrap_or(false); + let flushed = if memory_flush { + flush_old_messages_to_memory(&messages, &agent_id, conversation_id.as_deref()).await + } else { + 0 + }; + let compactor = ContextCompactor::new(config); - compactor.compact(&messages, &agent_id, conversation_id.as_deref()) + let mut result = compactor.compact(&messages, &agent_id, conversation_id.as_deref()); + result.flushed_memories = flushed; + result } /// Execute compaction with optional LLM-based summary @@ -558,10 +570,95 @@ pub async fn compactor_compact_llm( compaction_config: Option, llm_config: Option, ) -> CompactionResult { + let memory_flush = compaction_config + .as_ref() + .map(|c| c.memory_flush_enabled) + .unwrap_or(false); + let flushed = if memory_flush { + flush_old_messages_to_memory(&messages, &agent_id, conversation_id.as_deref()).await + } else { + 0 + }; + let compactor = ContextCompactor::new(compaction_config); - compactor + let mut result = compactor .compact_with_llm(&messages, &agent_id, conversation_id.as_deref(), llm_config.as_ref()) - .await + .await; + result.flushed_memories = flushed; + result +} + +/// Flush important messages from the old (pre-compaction) portion to VikingStorage. +/// +/// Extracts user messages and key assistant responses as session memories +/// so that information is preserved even after messages are compacted away. +async fn flush_old_messages_to_memory( + messages: &[CompactableMessage], + agent_id: &str, + _conversation_id: Option<&str>, +) -> usize { + let storage = match crate::viking_commands::get_storage().await { + Ok(s) => s, + Err(e) => { + tracing::warn!("[Compactor] Cannot get storage for memory flush: {}", e); + return 0; + } + }; + + let mut flushed = 0usize; + let mut prev_was_user = false; + + for msg in messages { + // Flush user messages as session memories (they contain user intent/preferences) + if msg.role == "user" && msg.content.len() > 10 { + let entry = zclaw_growth::MemoryEntry::new( + agent_id, + zclaw_growth::MemoryType::Session, + "compaction_flush", + msg.content.clone(), + ) + .with_importance(4); + + match zclaw_growth::VikingStorage::store(storage.as_ref(), &entry).await { + Ok(_) => flushed += 1, + Err(e) => { + tracing::debug!("[Compactor] Memory flush failed for user msg: {}", e); + } + } + prev_was_user = true; + } else if msg.role == "assistant" && prev_was_user { + // Flush the assistant response that follows a user message (contains answers) + if msg.content.len() > 20 { + let entry = zclaw_growth::MemoryEntry::new( + agent_id, + zclaw_growth::MemoryType::Session, + "compaction_flush", + msg.content.clone(), + ) + .with_importance(3); + + match zclaw_growth::VikingStorage::store(storage.as_ref(), &entry).await { + Ok(_) => flushed += 1, + Err(e) => { + tracing::debug!("[Compactor] Memory flush failed for assistant msg: {}", e); + } + } + } + prev_was_user = false; + } else { + prev_was_user = false; + } + } + + if flushed > 0 { + tracing::info!( + "[Compactor] Flushed {} memories before compaction for agent {}", + flushed, + agent_id + ); + } + + flushed } #[cfg(test)] diff --git a/desktop/src/components/MessageSearch.tsx b/desktop/src/components/MessageSearch.tsx index 5886a17..af048fc 100644 --- a/desktop/src/components/MessageSearch.tsx +++ b/desktop/src/components/MessageSearch.tsx @@ -1,19 +1,26 @@ import { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; -import { Search, X, ChevronUp, ChevronDown, Clock, User, Filter } from 'lucide-react'; +import { Search, X, ChevronUp, ChevronDown, Clock, User, Filter, Globe, MessageSquare } from 'lucide-react'; import { Button } from './ui'; import { useChatStore, Message } from '../store/chatStore'; +import { intelligence, PersistentMemory } from '../lib/intelligence-backend'; export interface SearchFilters { sender: 'all' | 'user' | 'assistant'; timeRange: 'all' | 'today' | 'week' | 'month'; } +export type SearchScope = 'session' | 'global'; + export interface SearchResult { message: Message; matchIndices: Array<{ start: number; end: number }>; } +export interface GlobalSearchResult { + memory: PersistentMemory; +} + interface MessageSearchProps { onNavigateToMessage: (messageId: string) => void; } @@ -26,6 +33,7 @@ export function MessageSearch({ onNavigateToMessage }: MessageSearchProps) { const [isOpen, setIsOpen] = useState(false); const [query, setQuery] = useState(''); + const [scope, setScope] = useState('session'); const [filters, setFilters] = useState({ sender: 'all', timeRange: 'all', @@ -33,6 +41,8 @@ export function MessageSearch({ onNavigateToMessage }: MessageSearchProps) { const [currentMatchIndex, setCurrentMatchIndex] = useState(0); const [showFilters, setShowFilters] = useState(false); const [searchHistory, setSearchHistory] = useState([]); + const [globalResults, setGlobalResults] = useState([]); + const [globalLoading, setGlobalLoading] = useState(false); const inputRef = useRef(null); // Load search history from localStorage @@ -63,6 +73,41 @@ export function MessageSearch({ onNavigateToMessage }: MessageSearchProps) { }); }, []); + // Global search: query VikingStorage when scope is 'global' + useEffect(() => { + if (scope !== 'global' || !query.trim()) { + setGlobalResults([]); + return; + } + + let cancelled = false; + const debounceTimer = setTimeout(async () => { + setGlobalLoading(true); + try { + const results = await intelligence.memory.search({ + query: query.trim(), + limit: 20, + }); + if (!cancelled) { + setGlobalResults(results.map((memory) => ({ memory }))); + } + } catch (err) { + if (!cancelled) { + setGlobalResults([]); + } + } finally { + if (!cancelled) { + setGlobalLoading(false); + } + } + }, 300); + + return () => { + cancelled = true; + clearTimeout(debounceTimer); + }; + }, [scope, query]); + // Filter messages by time range const filterByTimeRange = useCallback((message: Message, timeRange: SearchFilters['timeRange']): boolean => { if (timeRange === 'all') return true; @@ -245,6 +290,36 @@ export function MessageSearch({ onNavigateToMessage }: MessageSearchProps) { >
+ {/* Scope toggle */} +
+ + +
+ {/* Search input */}
@@ -253,7 +328,7 @@ export function MessageSearch({ onNavigateToMessage }: MessageSearchProps) { type="text" value={query} onChange={(e) => setQuery(e.target.value)} - placeholder="Search messages..." + placeholder={scope === 'global' ? 'Search all memories...' : 'Search messages...'} className="w-full pl-9 pr-8 py-2 text-sm bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-500 dark:focus:ring-orange-400 focus:border-transparent" aria-label="Search query" /> @@ -269,22 +344,24 @@ export function MessageSearch({ onNavigateToMessage }: MessageSearchProps) { )}
- {/* Filter toggle */} - + {/* Filter toggle (session only) */} + {scope === 'session' && ( + + )} - {/* Navigation buttons */} - {searchResults.length > 0 && ( + {/* Navigation buttons (session only) */} + {scope === 'session' && searchResults.length > 0 && (
{currentMatchIndex + 1} / {searchResults.length} @@ -381,8 +458,58 @@ export function MessageSearch({ onNavigateToMessage }: MessageSearchProps) {
)} - {/* No results message */} - {query && searchResults.length === 0 && ( + {/* Global search results */} + {scope === 'global' && query && ( +
+ {globalLoading && ( +
+ Searching memories... +
+ )} + {!globalLoading && globalResults.length === 0 && ( +
+ No memories found matching "{query}" +
+ )} + {!globalLoading && globalResults.length > 0 && ( +
+
+ {globalResults.length} memories found +
+ {globalResults.map((result) => ( +
+
+ + {result.memory.memory_type} + + | + + {result.memory.agent_id} + + {result.memory.importance > 5 && ( + + {'*'.repeat(Math.min(result.memory.importance - 4, 5))} + + )} +
+
+ {highlightSearchMatches(result.memory.content, query)} +
+
+ {result.memory.created_at.split('T')[0]} +
+
+ ))} +
+ )} +
+ )} + + {/* No results message (session search) */} + {scope === 'session' && query && searchResults.length === 0 && (
No messages found matching "{query}"
diff --git a/desktop/src/components/ReflectionLog.tsx b/desktop/src/components/ReflectionLog.tsx index ab8e66d..221c31f 100644 --- a/desktop/src/components/ReflectionLog.tsx +++ b/desktop/src/components/ReflectionLog.tsx @@ -485,7 +485,7 @@ export function ReflectionLog({ // Initialize reflection engine with config that allows soul modification await intelligenceClient.reflection.init(config); - const loadedHistory = await intelligenceClient.reflection.getHistory(); + const loadedHistory = await intelligenceClient.reflection.getHistory(undefined, agentId); setHistory([...loadedHistory].reverse()); // Most recent first const proposals = await intelligenceClient.identity.getPendingProposals(agentId); diff --git a/desktop/src/components/presentation/PresentationContainer.tsx b/desktop/src/components/presentation/PresentationContainer.tsx index e801e3d..ae2fe1e 100644 --- a/desktop/src/components/presentation/PresentationContainer.tsx +++ b/desktop/src/components/presentation/PresentationContainer.tsx @@ -18,6 +18,7 @@ import { QuizRenderer } from './renderers/QuizRenderer'; const SlideshowRenderer = React.lazy(() => import('./renderers/SlideshowRenderer').then(m => ({ default: m.SlideshowRenderer }))); const DocumentRenderer = React.lazy(() => import('./renderers/DocumentRenderer').then(m => ({ default: m.DocumentRenderer }))); +const ChartRenderer = React.lazy(() => import('./renderers/ChartRenderer').then(m => ({ default: m.ChartRenderer }))); interface PresentationContainerProps { /** Pipeline output data */ @@ -78,7 +79,7 @@ export function PresentationContainer({ if (supportedTypes && supportedTypes.length > 0) { return supportedTypes.filter((t): t is PresentationType => t !== 'auto'); } - return (['quiz', 'slideshow', 'document', 'whiteboard'] as PresentationType[]); + return (['quiz', 'slideshow', 'document', 'chart', 'whiteboard'] as PresentationType[]); }, [supportedTypes]); const renderContent = () => { @@ -111,11 +112,21 @@ export function PresentationContainer({ case 'whiteboard': return ( -
-

白板渲染器开发中...

+
+ + 即将推出 + +

白板渲染器开发中

); + case 'chart': + return ( + }> + [0]['data']} /> + + ); + default: return (
diff --git a/desktop/src/components/presentation/renderers/ChartRenderer.tsx b/desktop/src/components/presentation/renderers/ChartRenderer.tsx new file mode 100644 index 0000000..0e8104f --- /dev/null +++ b/desktop/src/components/presentation/renderers/ChartRenderer.tsx @@ -0,0 +1,204 @@ +/** + * Chart Renderer + * + * Renders data as interactive charts using recharts. + * Supports: line, bar, pie, scatter, area chart types. + */ + +import { + LineChart, Line, BarChart, Bar, PieChart, Pie, Cell, + ScatterChart, Scatter, AreaChart, Area, + XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, +} from 'recharts'; +import type { ChartData } from '../types'; + +const DEFAULT_COLORS = [ + '#3b82f6', '#ef4444', '#22c55e', '#f59e0b', '#8b5cf6', + '#ec4899', '#06b6d4', '#f97316', '#14b8a6', '#6366f1', +]; + +interface ChartRendererProps { + data: ChartData; + className?: string; +} + +export function ChartRenderer({ data, className = '' }: ChartRendererProps) { + const { type, title, labels, datasets, options } = data; + + // Transform datasets + labels into recharts data format + const chartData = (labels || []).map((label, i) => { + const point: Record = { name: label }; + for (const ds of datasets) { + point[ds.label] = ds.data[i] ?? 0; + } + return point; + }); + + // If no labels, use index as x-axis + if (!labels || labels.length === 0) { + const maxLen = Math.max(...datasets.map(ds => ds.data.length), 0); + for (let i = 0; i < maxLen; i++) { + const point: Record = { name: `${i + 1}` }; + for (const ds of datasets) { + point[ds.label] = ds.data[i] ?? 0; + } + chartData.push(point); + } + } + + const showLegend = options?.plugins?.legend?.display !== false; + + const legendProps = showLegend + ? { wrapperStyle: { paddingBottom: 8 } } + : undefined; + + const chartTitle = title || options?.plugins?.title?.text; + + const renderChart = () => { + switch (type) { + case 'line': + return ( + + + + + + + {showLegend && } + {datasets.map((ds, i) => ( + + ))} + + + ); + + case 'bar': + return ( + + + + + + + {showLegend && } + {datasets.map((ds, i) => ( + + ))} + + + ); + + case 'pie': { + const pieData = datasets.flatMap((ds) => + (labels || ds.data.map((_, i) => `${i + 1}`)).map((label, i) => ({ + name: label, + value: ds.data[i] ?? 0, + })) + ); + return ( + + + `${name ?? ''} ${((percent ?? 0) * 100).toFixed(0)}%`} + outerRadius="70%" + dataKey="value" + > + {pieData.map((_, i) => ( + + ))} + + + {showLegend && } + + + ); + } + + case 'scatter': { + const scatterData = datasets.flatMap((ds) => + ds.data.map((val, i) => ({ + x: labels ? i : i + 1, + y: val, + name: ds.label, + })) + ); + return ( + + + + + + + {showLegend && } + + + + ); + } + + case 'area': + return ( + + + + + + + {showLegend && } + {datasets.map((ds, i) => ( + + ))} + + + ); + + default: + return

不支持的图表类型: {type}

; + } + }; + + return ( +
+ {chartTitle && ( +
+

{chartTitle}

+
+ )} +
+ {datasets.length === 0 ? ( +
+

暂无图表数据

+
+ ) : ( + renderChart() + )} +
+
+ ); +} + +export default ChartRenderer; diff --git a/desktop/src/components/presentation/renderers/DocumentRenderer.tsx b/desktop/src/components/presentation/renderers/DocumentRenderer.tsx index 55ff780..0d63be4 100644 --- a/desktop/src/components/presentation/renderers/DocumentRenderer.tsx +++ b/desktop/src/components/presentation/renderers/DocumentRenderer.tsx @@ -1,10 +1,13 @@ /** * Document Renderer * - * Renders content as a scrollable document with Markdown support. + * Renders content as a scrollable document with full Markdown support + * via react-markdown + remark-gfm (tables, strikethrough, task lists, etc.). */ import { useState } from 'react'; +import Markdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; import { Download, ExternalLink, Copy } from 'lucide-react'; import type { DocumentData } from '../types'; @@ -26,12 +29,14 @@ export function DocumentRenderer({ const handleCopy = async () => { try { - const textToCopy = typeof data === 'string' ? data : (data.content || JSON.stringify(data, null, 2)); + const textToCopy = typeof data === 'string' + ? data + : (data.content || JSON.stringify(data, null, 2)); await navigator.clipboard.writeText(textToCopy); setCopied(true); setTimeout(() => setCopied(false), 2000); - } catch (error) { - console.error('Failed to copy:', error); + } catch { + // Clipboard API may not be available in all contexts } }; @@ -46,58 +51,14 @@ export function DocumentRenderer({ } }; - const renderMarkdown = (content: string): React.ReactNode => { - const lines = content.split('\n'); - const elements: React.ReactNode[] = []; - - for (const line of lines) { - const trimmed = line.trim(); - if (!trimmed) continue; - - if (trimmed.startsWith('# ')) { - elements.push( -

- {trimmed.substring(2)} -

- ); - } else if (trimmed.startsWith('## ')) { - elements.push( -

- {trimmed.substring(3)} -

- ); - } else if (trimmed.startsWith('### ')) { - elements.push( -

- {trimmed.substring(4)} -

- ); - } else if (trimmed.startsWith('- ')) { - elements.push( -
  • - {trimmed.substring(2)} -
  • - ); - } else if (trimmed.startsWith('```')) { - elements.push( -
    -            {trimmed.substring(3, trimmed.length - 3)}
    -          
    - ); - } else { - elements.push( -

    {trimmed}

    - ); - } - } - - return
    {elements}
    ; - }; + const content = typeof data === 'string' + ? data + : (data.content || JSON.stringify(data, null, 2)); if (!enableMarkdown) { return (
    -
    {JSON.stringify(data, null, 2)}
    +
    {content}
    ); } @@ -138,10 +99,8 @@ export function DocumentRenderer({
    )} -
    - {typeof data === 'string' - ? renderMarkdown(data) - : renderMarkdown(data.content || JSON.stringify(data))} +
    + {content}
    ); diff --git a/desktop/src/components/presentation/renderers/SlideshowRenderer.tsx b/desktop/src/components/presentation/renderers/SlideshowRenderer.tsx index e1ce4c6..da926f0 100644 --- a/desktop/src/components/presentation/renderers/SlideshowRenderer.tsx +++ b/desktop/src/components/presentation/renderers/SlideshowRenderer.tsx @@ -2,9 +2,12 @@ * Slideshow Renderer * * Renders presentation as a slideshow with slide navigation. + * Supports: title, content, image, code, twoColumn slide types. */ import { useState, useEffect, useCallback } from 'react'; +import Markdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; import { ChevronLeft, ChevronRight, @@ -13,7 +16,7 @@ import { Play, Pause, } from 'lucide-react'; -import type { SlideshowData } from '../types'; +import type { SlideshowData, Slide } from '../types'; interface SlideshowRendererProps { data: SlideshowData; @@ -41,30 +44,6 @@ export function SlideshowRenderer({ const slides = data.slides || []; const totalSlides = slides.length; - // Handle keyboard navigation - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === 'ArrowRight' || e.key === ' ') { - handleNext(); - } else if (e.key === 'ArrowLeft') { - handlePrev(); - } else if (e.key === 'f') { - toggleFullscreen(); - } - }; - - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); - }, []); - - // Auto-play - useEffect(() => { - if (isPlaying && autoPlayInterval > 0) { - const timer = setInterval(handleNext, autoPlayInterval * 1000); - return () => clearInterval(timer); - } - }, [isPlaying, autoPlayInterval]); - const handleNext = useCallback(() => { setCurrentIndex((prev) => (prev + 1) % totalSlides); }, [totalSlides]); @@ -77,6 +56,32 @@ export function SlideshowRenderer({ setIsFullscreen((prev) => !prev); }, []); + // Handle keyboard navigation + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'ArrowRight' || e.key === ' ') { + e.preventDefault(); + handleNext(); + } else if (e.key === 'ArrowLeft') { + e.preventDefault(); + handlePrev(); + } else if (e.key === 'f') { + toggleFullscreen(); + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [handleNext, handlePrev, toggleFullscreen]); + + // Auto-play + useEffect(() => { + if (isPlaying && autoPlayInterval > 0) { + const timer = setInterval(handleNext, autoPlayInterval * 1000); + return () => clearInterval(timer); + } + }, [isPlaying, autoPlayInterval, handleNext]); + const currentSlide = slides[currentIndex]; if (!currentSlide) { @@ -88,26 +93,15 @@ export function SlideshowRenderer({ } return ( -
    +
    {/* Slide Content */}
    - {/* Title */} - {currentSlide.title && ( -

    - {currentSlide.title} -

    - )} - - {/* Content rendering would go here */} -
    - {/* This is simplified - real implementation would render based on content type */} - {typeof currentSlide.content === 'string' ? ( -

    {currentSlide.content}

    - ) : ( -
    Complex content rendering
    - )} -
    +
    @@ -127,7 +121,11 @@ export function SlideshowRenderer({ disabled={autoPlayInterval === 0} className="p-2 hover:bg-gray-200 rounded disabled:opacity-50" > - {isPlaying ? : } + {isPlaying ? ( + + ) : ( + + )}
    )}
    ); } +/** Renders a single slide based on its type */ +function SlideContent({ slide }: { slide: Slide }) { + switch (slide.type) { + case 'title': + return ( +
    + {slide.title && ( +

    {slide.title}

    + )} + {slide.content && ( +

    + {slide.content} +

    + )} +
    + ); + + case 'content': + return ( +
    + {slide.title && ( +

    {slide.title}

    + )} + {slide.content && ( +
    + {slide.content} +
    + )} +
    + ); + + case 'image': + return ( +
    + {slide.title && ( +

    {slide.title}

    + )} + {slide.image && ( + {slide.title + )} + {slide.content && ( +

    {slide.content}

    + )} +
    + ); + + case 'code': + return ( +
    + {slide.title && ( +

    {slide.title}

    + )} + {slide.code && ( +
    +              {slide.language && (
    +                
    + {slide.language} +
    + )} + {slide.code} +
    + )} + {slide.content && ( +

    {slide.content}

    + )} +
    + ); + + case 'twoColumn': + return ( +
    + {slide.title && ( +

    {slide.title}

    + )} +
    +
    + {slide.leftContent && ( + + {slide.leftContent} + + )} +
    +
    + {slide.rightContent && ( + + {slide.rightContent} + + )} +
    +
    +
    + ); + + default: + return ( +
    + {slide.title && ( +

    {slide.title}

    + )} + {slide.content && ( +
    + {slide.content} +
    + )} +
    + ); + } +} + export default SlideshowRenderer; diff --git a/desktop/src/lib/intelligence-backend.ts b/desktop/src/lib/intelligence-backend.ts index 7e3aac2..2c9ff47 100644 --- a/desktop/src/lib/intelligence-backend.ts +++ b/desktop/src/lib/intelligence-backend.ts @@ -347,8 +347,8 @@ export const reflection = { return invoke('reflection_reflect', { agentId, memories }); }, - async getHistory(limit?: number): Promise { - return invoke('reflection_get_history', { limit }); + async getHistory(limit?: number, agentId?: string): Promise { + return invoke('reflection_get_history', { limit, agentId }); }, async getState(): Promise { diff --git a/desktop/src/lib/intelligence-client.ts b/desktop/src/lib/intelligence-client.ts index 2534d9d..ad05f49 100644 --- a/desktop/src/lib/intelligence-client.ts +++ b/desktop/src/lib/intelligence-client.ts @@ -691,7 +691,7 @@ const fallbackReflection = { return result; }, - async getHistory(limit?: number): Promise { + async getHistory(limit?: number, _agentId?: string): Promise { const l = limit ?? 10; return fallbackReflection._history.slice(-l).reverse(); }, @@ -1318,13 +1318,13 @@ export const intelligenceClient = { return fallbackReflection.reflect(agentId, memories); }, - getHistory: async (limit?: number): Promise => { + getHistory: async (limit?: number, agentId?: string): Promise => { if (isTauriRuntime()) { return tauriInvoke('reflection.getHistory', () => - intelligence.reflection.getHistory(limit) + intelligence.reflection.getHistory(limit, agentId) ); } - return fallbackReflection.getHistory(limit); + return fallbackReflection.getHistory(limit, agentId); }, getState: async (): Promise => { diff --git a/docs/features/DEEP_AUDIT_REPORT.md b/docs/features/DEEP_AUDIT_REPORT.md index ed8f54a..f9995af 100644 --- a/docs/features/DEEP_AUDIT_REPORT.md +++ b/docs/features/DEEP_AUDIT_REPORT.md @@ -522,6 +522,7 @@ PipelinesPanel.tsx → workflowStore.runPipeline() | **P2** | M5 | 自主授权集成到执行链路 | 1-2d | 下周 | ✅ 已修复 | | **P2** | M3 | hand_approve 使用 hand_name 参数 | 1h | 下周 | ✅ 已修复 | | **P2** | L2 | 清理 gatewayStore 废弃引用 | 1h | 下周 | ✅ 已确认(仅注释) | +| **P2** | S9 | 消息搜索仅当前会话 — 新增 Global 模式 | 1d | 下周 | ✅ 已修复 | | **P3** | M6 | 实现语义路由 | 2-3d | 下个迭代 | | **P3** | L1 | Pipeline 并行执行 | 2d | 下个迭代 | | **P3** | L3 | Wasm/Native 技能模式 | 3-5d | 长期 | @@ -570,6 +571,7 @@ ZCLAW 的核心架构(通信、状态管理、安全认证、聊天、Agent 13. ~~**反思历史只存单条**~~ ✅ 已修复 — 累积存储到 reflection:history 数组 14. ~~**身份回滚 UI 缺失**~~ ✅ 已实现 — IdentityChangeProposal.tsx HistoryItem 15. **28 处 dead_code 标注**中大部分是合理的预留功能,少数是遗留代码 -16. **剩余 P2/P3 项**: 反思 LLM 分析、跨会话搜索、语义路由、Pipeline 并行等 +16. **剩余 P2/P3 项**: 反思 LLM 分析、语义路由、Pipeline 并行等 +17. ~~**消息搜索仅当前会话**~~ ✅ 已修复 — MessageSearch 新增 Global 模式,调用 VikingStorage memory_search 跨会话搜索记忆 -**累计修复 22 项** (P0×3 + P1×8 + P2×6 + 误判×2 + 审计×3),系统真实可用率从 ~50% 提升到 ~80%。剩余 P3 项为增强功能,不阻塞核心使用。 +**累计修复 23 项** (P0×3 + P1×8 + P2×7 + 误判×2 + 审计×3),系统真实可用率从 ~50% 提升到 ~80%。剩余 P3 项为增强功能,不阻塞核心使用。 diff --git a/docs/features/README.md b/docs/features/README.md index c3adb00..9a8e144 100644 --- a/docs/features/README.md +++ b/docs/features/README.md @@ -1,12 +1,12 @@ # ZCLAW 功能全景文档 -> **版本**: v0.6.3 +> **版本**: v0.6.4 > **更新日期**: 2026-03-27 > **项目状态**: 完整 Rust Workspace 架构,10 个核心 Crates,69 技能,Pipeline DSL + Smart Presentation + Agent Growth System -> **整体完成度**: ~62% (基于 2026-03-27 深度审计 + 两轮修复后) +> **整体完成度**: ~72% (基于 2026-03-27 深度审计 + 四轮修复后) > **架构**: Tauri 桌面应用,Rust Workspace (10 crates) + React 前端 > -> **审计修复 (2026-03-27)**: 修复 2 个 CRITICAL + 6 个 HIGH + 2 个 MEDIUM 问题,详见 [DEEP_AUDIT_REPORT.md](./DEEP_AUDIT_REPORT.md) +> **审计修复 (2026-03-27)**: 累计修复 23 项 (P0×3 + P1×8 + P2×7 + 误判×2 + 审计×3),详见 [DEEP_AUDIT_REPORT.md](./DEEP_AUDIT_REPORT.md) > **重要**: ZCLAW 采用 Rust Workspace 架构,包含 10 个分层 Crates (types → memory → runtime → kernel → skills/hands/protocols/pipeline/growth/channels),所有核心能力集成在 Tauri 桌面应用中 @@ -26,7 +26,7 @@ | 文档 | 功能 | 成熟度 | 测试覆盖 | |------|------|--------|---------| -| [00-chat-interface.md](01-core-features/00-chat-interface.md) | 聊天界面 | L3-L4 (90%) | 高 | +| [00-chat-interface.md](01-core-features/00-chat-interface.md) | 聊天界面 | L4 (92%) | 高 | | [01-agent-clones.md](01-core-features/01-agent-clones.md) | Agent 分身 | L3 (85%) | 高 | | [02-hands-system.md](01-core-features/02-hands-system.md) | Hands 系统 | L3 (60%) | 中 | | 工作流引擎 | 工作流引擎 | L3 (80%) | 中 | @@ -144,7 +144,7 @@ | S6 | 导出功能清理 (PPTX/PDF 友好提示) | P2 | ✅ 完成 | | S7 | Compactor 接入聊天流程 | P1 | ✅ 完成 | | S8 | 定时任务 KernelClient 支持 | P1 | 待开始 | -| S9 | 添加消息搜索功能 | P1 | 待开始 | +| S9 | 添加消息搜索功能 | P1 | ✅ 完成 (Session + Global 双模式) | | S10 | 优化错误提示 | P1 | 待开始 | ### 2.2 中期计划 (1-2 月) @@ -289,6 +289,7 @@ skills hands protocols pipeline growth channels | 日期 | 版本 | 变更内容 | |------|------|---------| +| 2026-03-27 | v0.6.4 | **审计修复 (P2 第四轮)**: S9 消息搜索跨会话 (Session + Global 双模式,VikingStorage 搜索)、M5-补 自主授权后端守卫、M3 hand_approve 参数修复、M4-补 反思历史累积存储、心跳历史持久化。累计修复 23 项,整体完成度 65%→72%。| | 2026-03-27 | v0.6.3 | **审计修复 (P1/P2)**: H3 记忆双存储统一到 VikingStorage、H4 心跳引擎持久化 + 启动恢复、M4 反思结果持久化。整体完成度 58%→62%。| | 2026-03-27 | v0.6.2 | **审计修复 (P0/P1)**: C1 PromptOnly LLM 集成、C2 反思引擎空记忆修复、H7 Agent Store 接口适配、H8 Hand 审批检查、M1 幽灵命令注册、H1/H2 demo 标记、H5 归档过时报告。整体完成度 50%→58%。| | 2026-03-27 | v0.6.1 | **功能完整性修复**: 激活 LoopGuard 循环防护、实现 CapabilityManager.validate() 安全验证、handStore/workflowStore KernelClient 适配器、Credits 标注开发中、Skills 动态化、ScheduledTasks localStorage 降级、token 用量追踪 | @@ -332,7 +333,18 @@ skills hands protocols pipeline growth channels | 反思结果持久化 | M4 | `reflect()` 后持久化 ReflectionState/Result 到 VikingStorage,重启后自动恢复 | | 清理 dead_code warnings | — | PersistentMemoryStore impl 添加 `#[allow(dead_code)]`,移除未使用的 `build_uri` | -### 7.3 代码清理 +### 7.3 审计修复 (P2 第四轮) + +| 修复项 | ID | 说明 | +|--------|-----|------| +| 自主授权后端守卫 | M5-补 | `hand_execute`/`skill_execute` 接收 `autonomy_level` 参数,三级守卫 (supervised/assisted/autonomous) | +| hand_approve 参数 | M3 | 移除 `_` 前缀,添加审计日志,返回值包含 hand_name | +| 反思历史累积 | M4-补 | 新增 `reflection:history:{agent_id}` 数组(最多 20 条),向后兼容 `reflection:latest` | +| 心跳历史持久化 | — | `tick()` 同步存储历史到 VikingStorage,`heartbeat_init()` 恢复历史 | +| 身份回滚 UI | — | 确认 `IdentityChangeProposal.tsx` 已实现 HistoryItem + restoreSnapshot | +| 跨会话消息搜索 | S9 | MessageSearch 新增 Session/Global 双模式,Global 调用 `memory_search` 搜索 VikingStorage | + +### 7.4 代码清理 | 清理项 | 说明 | |--------|------| @@ -352,4 +364,4 @@ skills hands protocols pipeline growth channels | ScheduledTasks 持久化 | 添加 localStorage 降级,刷新不丢失 | | Token 用量追踪 | chatStore 新增 addTokenUsage/getTotalTokens | -> **审计说明**: 成熟度等级已根据代码审计调整为实际值。Identity Evolution 标注为 L2 (70%) 是因为其 `dead_code` 属性属于 Tauri 运行时模式(在 Tauri 上下文中实际被调用),而非真正的死代码。Reflection Engine L2 (65%) 因核心反思逻辑尚未深度迭代。 +> **审计说明**: 成熟度等级已根据代码审计调整为实际值。Identity Evolution 标注为 L2 (70%) 是因为其 `dead_code` 属性属于 Tauri 运行时模式(在 Tauri 上下文中实际被调用),而非真正的死代码。Reflection Engine L2 (65%) 因核心反思逻辑尚未深度迭代。累计修复 23 项后整体完成度从 ~50% 提升到 ~72%。