fix(growth,kernel,runtime,desktop): 50 轮功能链路审计 7 项断链修复
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

P0 修复:
- B-MEM-2: 跨会话记忆丢失 — 添加 IdentityRecall 查询意图检测,
  身份类查询绕过 FTS5/LIKE 文本搜索,直接按 scope 检索全部偏好+知识记忆;
  缓存 GrowthIntegration 到 Kernel 避免每次请求重建空 scorer
- B-HAND-1: Hands 未触发 — 创建 HandTool wrapper 实现 Tool trait,
  在 create_tool_registry() 中注册所有已启用 Hands 为 LLM 可调用工具

P1 修复:
- B-SCHED-4: 一次性定时未拦截 — 添加 RE_ONE_SHOT_TODAY 正则匹配
  "下午3点半提醒我..."类无日期前缀的同日触发模式
- B-CHAT-2: 工具调用循环 — ToolErrorMiddleware 添加连续失败计数器,
  3 次连续失败后自动 AbortLoop 防止无限重试
- B-CHAT-5: Stream 竞态 — cancelStream 后添加 500ms cancelCooldown,
  防止后端 active-stream 检查竞态
This commit is contained in:
iven
2026-04-20 09:43:38 +08:00
parent 24b866fc28
commit f2917366a8
10 changed files with 746 additions and 49 deletions

View File

@@ -41,12 +41,16 @@ pub struct Kernel {
skills: Arc<SkillRegistry>,
skill_executor: Arc<KernelSkillExecutor>,
hands: Arc<HandRegistry>,
/// Cached hand configs (populated at boot, used for tool registry)
hand_configs: Vec<zclaw_hands::HandConfig>,
trigger_manager: crate::trigger_manager::TriggerManager,
pending_approvals: Arc<Mutex<Vec<ApprovalEntry>>>,
/// Running hand runs that can be cancelled (run_id -> cancelled flag)
running_hand_runs: Arc<dashmap::DashMap<zclaw_types::HandRunId, Arc<std::sync::atomic::AtomicBool>>>,
/// Shared memory storage backend for Growth system
viking: Arc<zclaw_runtime::VikingAdapter>,
/// Cached GrowthIntegration — avoids recreating empty scorer per request
growth: std::sync::Mutex<Option<std::sync::Arc<zclaw_runtime::GrowthIntegration>>>,
/// Optional LLM driver for memory extraction (set by Tauri desktop layer)
extraction_driver: Option<Arc<dyn zclaw_runtime::LlmDriverForExtraction>>,
/// MCP tool adapters — shared with Tauri MCP manager, updated dynamically
@@ -95,6 +99,9 @@ impl Kernel {
hands.register(Arc::new(TwitterHand::new())).await;
hands.register(Arc::new(ReminderHand::new())).await;
// Cache hand configs for tool registry (sync access from create_tool_registry)
let hand_configs = hands.list().await;
// Create skill executor
let skill_executor = Arc::new(KernelSkillExecutor::new(skills.clone(), driver.clone()));
@@ -146,10 +153,12 @@ impl Kernel {
skills,
skill_executor,
hands,
hand_configs,
trigger_manager,
pending_approvals: Arc::new(Mutex::new(Vec::new())),
running_hand_runs: Arc::new(dashmap::DashMap::new()),
viking,
growth: std::sync::Mutex::new(None),
extraction_driver: None,
mcp_adapters: Arc::new(std::sync::RwLock::new(Vec::new())),
industry_keywords: Arc::new(tokio::sync::RwLock::new(Vec::new())),
@@ -158,7 +167,7 @@ impl Kernel {
})
}
/// Create a tool registry with built-in tools + MCP tools.
/// Create a tool registry with built-in tools + Hand tools + MCP tools.
/// When `subagent_enabled` is false, TaskTool is excluded to prevent
/// the LLM from attempting sub-agent delegation in non-Ultra modes.
pub(crate) fn create_tool_registry(&self, subagent_enabled: bool) -> ToolRegistry {
@@ -175,6 +184,20 @@ impl Kernel {
tools.register(Box::new(task_tool));
}
// Register Hand tools — expose registered Hands as LLM-callable tools
// (e.g., hand_quiz, hand_researcher, hand_browser, etc.)
for config in &self.hand_configs {
if !config.enabled {
continue;
}
let tool = zclaw_runtime::tool::hand_tool::HandTool::from_config(
&config.id,
&config.description,
config.input_schema.clone(),
);
tools.register(Box::new(tool));
}
// Register MCP tools (dynamically updated by Tauri MCP manager)
if let Ok(adapters) = self.mcp_adapters.read() {
for adapter in adapters.iter() {
@@ -249,11 +272,18 @@ impl Kernel {
chain.register(Arc::new(mw));
}
// Growth integration — shared VikingAdapter for memory middleware & compaction
let mut growth = zclaw_runtime::GrowthIntegration::new(self.viking.clone());
if let Some(ref driver) = self.extraction_driver {
growth = growth.with_llm_driver(driver.clone());
}
// Growth integration — cached to avoid recreating empty scorer per request
let growth = {
let mut cached = self.growth.lock().expect("growth lock");
if cached.is_none() {
let mut g = zclaw_runtime::GrowthIntegration::new(self.viking.clone());
if let Some(ref driver) = self.extraction_driver {
g = g.with_llm_driver(driver.clone());
}
*cached = Some(std::sync::Arc::new(g));
}
cached.as_ref().expect("growth present").clone()
};
// Evolution middleware — pushes evolution candidate skills into system prompt
// priority=78, executed first by chain (before ButlerRouter@80)
@@ -282,7 +312,7 @@ impl Kernel {
// Memory middleware — auto-extract memories + check evolution after conversations
{
use std::sync::Arc;
let mw = zclaw_runtime::middleware::memory::MemoryMiddleware::new(growth)
let mw = zclaw_runtime::middleware::memory::MemoryMiddleware::new(growth.clone())
.with_evolution(evolution_mw);
chain.register(Arc::new(mw));
}
@@ -415,6 +445,10 @@ impl Kernel {
pub fn set_viking(&mut self, viking: Arc<zclaw_runtime::VikingAdapter>) {
tracing::info!("[Kernel] Replacing in-memory VikingAdapter with persistent storage");
self.viking = viking;
// Invalidate cached GrowthIntegration so next request builds with new storage
if let Ok(mut g) = self.growth.lock() {
*g = None;
}
}
/// Get a reference to the shared VikingAdapter
@@ -429,6 +463,10 @@ impl Kernel {
pub fn set_extraction_driver(&mut self, driver: Arc<dyn zclaw_runtime::LlmDriverForExtraction>) {
tracing::info!("[Kernel] Extraction driver configured for Growth system");
self.extraction_driver = Some(driver);
// Invalidate cached GrowthIntegration so next request uses new driver
if let Ok(mut g) = self.growth.lock() {
*g = None;
}
}
/// Get a reference to the shared MCP adapters list.