Skip to content

Releases: jrcjrcc/deepseek-nyamu

使用新版本ui

Choose a tag to compare

@jrcjrcc jrcjrcc released this 08 Jun 20:41
e76a4ae
image

Windows Sub-agent 完成时 UI 渲染宽度减半 Bug

Choose a tag to compare

@jrcjrcc jrcjrcc released this 03 Jun 20:50

Windows Sub-agent 完成时 UI 渲染宽度减半 Bug

概述

在 Windows 平台下,当 sub-agent(子代理)完成并返回数据给主 agent 后,终端 UI 的渲染区域会缩小到约原本宽度的一半,导致文字换行错位、内容错乱。必须手动缩小窗口再全屏才能恢复。

  • 受影响平台:Windows(PowerShell / Windows Terminal)
  • 不受影响:WSL (zsh)、macOS、Linux
  • 触发命令/agent 0 今天天气怎么样 等涉及 sub-agent 交互的场景

症状

# 症状 说明
1 UI 渲染区域缩小到约 1/2 宽度 终端全屏,但内容只占一半宽度
2 文字换行错位 在错误的宽度上换行
3 滚动/刷新无效 needs_redraw 触发重绘后仍然错误
4 缩小窗口时"贪吃蛇"效果 右侧渲染内容跨行穿到左侧
5 必须重新缩放窗口才能恢复 缩小→全屏→滚动后恢复正常

探索过程

第一阶段:初步定位

最初以为是 transcript cache 的 revision 未更新导致的布局缓存问题。检查了 ensure_split()flatten_from() 的缓存逻辑。

已修复:在 subagent_routing.rs 中将 3 处 mark_history_updated() 改为 bump_history_cell(idx),强制 sub-agent 卡片所在 cell 的 revision 变化,使 cache 检测到 miss 并重建布局。

但问题仍未解决。

第二阶段:缩小范围

发现 WSL 下不触发、Windows 下必触发 → 平台相关。

排查方向:

  • ColorDepth::detect() — 新旧项目相同 ✅
  • ColorCompatBackend — 新旧项目代码一致 ✅
  • 依赖版本 (crossterm 0.28, ratatui 0.30) — 相同 ✅
  • 新旧项目的 ui.rs 事件循环 — 几乎相同 ✅

第三阶段:发现关键线索

对比新旧项目 size() 方法:

旧项目 (nyamu)

fn size(&self) -> io::Result<Size> {
    match self.forced_size {
        Some(size) => Ok(size),
        None => self.inner.size(),
    }
}

新项目

fn size(&self) -> io::Result<Size> {
    if let Some(size) = self.terminal_size.or(self.forced_size) {
        return Ok(size);
    }
    self.inner.size()
}

发现 set_terminal_size() 方法定义在 ColorCompatBackend 上,但整个代码库里没有任何调用者terminal_size 始终为 None

第四阶段:深入事件流程

跟踪 AgentComplete 事件处理器:

// ui.rs:2033
if should_recapture_terminal {
    resume_terminal(...)?;
}

对比 ResumeEvents 处理器:

// ui.rs:1957
if event_broker.is_paused() {
    resume_terminal(...)?;
}

发现 AgentComplete 缺少 is_paused() 守卫!

第五阶段:完整触发链确认

AgentComplete
    → should_recapture_terminal = true
    → resume_terminal() 无条件调用(缺少 is_paused 守卫)
    → EnterAlternateScreen(已在 alt screen 中时二次进入)
    → Windows 创建新的 alt screen buffer,尺寸可能 ≠ 窗口尺寸
    → 下一次 draw()
        → backend.size()
            → terminal_size = None(从未被设置)
            → forced_size = 可能过期或 None
            → crossterm::terminal::size() 返回 buffer 宽度(非窗口宽度)
    → Frame 使用错误宽度渲染
    → 内容换行在错误宽度 → "半宽"UI

根因分析

Bug 由三个因素共同导致:

因素 A:AgentCompleteis_paused() 守卫

AgentComplete 在 sub-agent 完成时无条件调用 resume_terminal(),即使终端从未被暂停过(sub-agent 使用非交互式工具,不会触发 PauseEvents)。

而同一文件中的 ResumeEvents 处理器就有 event_broker.is_paused() 守卫。

位置ui.rs:2033-2044

因素 B:二次 EnterAlternateScreen 导致缓冲区尺寸漂移

Windows 上,已在 alternate screen 中时再次调用 EnterAlternateScreen 会创建新的 alt screen 缓冲区。新缓冲区的 dwSize.X(从 GetConsoleScreenBufferInfo() 获取)可能与实际窗口宽度不一致。

resume_terminal() 后没有重新查询终端尺寸。

位置ui.rs:7547-7548resume_terminal 中的 EnterAlternateScreen

因素 C:set_terminal_size() 从未被调用

ColorCompatBackend 定义了 set_terminal_size() 方法和 terminal_size 缓存字段,但没有任何代码调用它。size() 方法中 terminal_size.or(forced_size) 的回退链中 terminal_size 总是 None,实际只依赖 forced_size(可能过期)或实时 crossterm::terminal::size()(Windows WinAPI 返回 buffer 宽度而非窗口宽度)。

位置color_compat.rs:89, 180-185


修复方案

修复 1:AgentCompleteis_paused() 守卫

// 修复前
if should_recapture_terminal {
    resume_terminal(...)?;
}

// 修复后
if should_recapture_terminal && event_broker.is_paused() {
    resume_terminal(...)?;
}

作用:只在终端确实被暂停过时调用 resume_terminal(),防止 sub-agent 完成时二次 EnterAlternateScreen

修复 2:resume_terminal 后重新查询并缓存终端尺寸

// resume_terminal() 末尾添加
if let Ok((cols, rows)) = crossterm::terminal::size() {
    terminal.backend_mut().set_terminal_size(Size::new(cols, rows));
}

作用:重新进入 alt screen 后把当前真实终端尺寸缓存到 terminal_size,后续 draw() 使用缓存值避免过期的 forced_size 或 WinAPI 的 buffer 宽度。

修复 3:Resize 处理器也设置 terminal_size

// 修复前
backend.force_size(Size::new(final_w, final_h));

// 修复后
let new_size = Size::new(final_w, final_h);
backend.force_size(new_size);
backend.set_terminal_size(new_size);

作用:Resize 事件时同时设置 forced_sizeterminal_size,使 size() 方法的第一级缓存真正生效。


最终改动清单

文件 改动
ui.rs 2033 should_recapture_terminalshould_recapture_terminal && event_broker.is_paused()
ui.rs 2557-2565 resize handler 添加 set_terminal_size(),移除 clear_forced_size()
ui.rs 7562-7570 resume_terminal() 末尾添加 crossterm::terminal::size() 查询和 set_terminal_size() 缓存
color_compat.rs set_terminal_size() 方法已定义,无需改动
subagent_routing.rs 116, 132, 155 mark_history_updated()bump_history_cell()(补充修复)

平台安全性

平台 风险 说明
macOS ✅ 无影响 is_paused() 所有平台可用;TIOCGWINSZ 始终返回正确尺寸
Linux ✅ 无影响 同上
WSL ✅ 无影响 同上
Windows ✅ 已修复 三次改动共同防止 buffer/窗口尺寸漂移

致谢

  • 排查工具:CodeWhale + Rust 源码分析
  • 对比基准:deepseek-nyamu-master(喵梦.exe)
  • 测试环境:Windows Terminal + PowerShell, WSL2 + zsh

deepseek-nyamu v0.8.46-nyamu1

Choose a tag to compare

@jrcjrcc jrcjrcc released this 02 Jun 21:35

deepseek-nyamu v0.8.46-nyamu1

基于 CodeWhale v0.8.46 的深度定制分支,本次更新聚焦于安全加固稳态性能提升

🔒 安全修复

  • 计时攻击防护:app-server auth token == 短路比较替换为 subtle::ConstantTimeEq::ct_eq() 常数量级按位比较
  • XSS 防护:Mermaid 组件手写 regex sanitizer 替换为 DOMPurify.sanitize(),覆盖 data: URL、foreignObject 嵌入、SVG use 劫持等 regex 无法穷尽的 edge case
  • Token 泄露防护:auth token 不再通过 eprintln! 输出到 stderr,防止泄露到日志文件
  • 锁中毒级联崩溃防护:7 处 std::sync::Mutex::lock().unwrap() 改为 unwrap_or_else(|e| e.into_inner()),一线程 panic 不再崩全进程
  • serde 静默错误传播:3 处 unwrap_or(Value::Null) 改为 map_err 向上传播,数据损坏不再被静默吞掉
  • MCP 子进程隔离:Windows Job Object / Linux PR_SET_PDEATHSIG + atexit handler + shutdown 链三路保障,覆盖 panic=abort、SIGKILL、OOM killer、process::exit 全部异常退出路径

⚡ 性能优化

  • Sub-agent 通知机制tokio::sync::Notify 替代 250ms 轮询 + RwLock 读锁争用,并发 N 个 agent 的锁竞争从 O(n) 降到 O(1),cancel 写锁立等可取
  • SSE 流式空闲超时:默认 300s→60s,config.toml 三级注入配置,keepalive 空 chunk 不再重置计时器,防静默挂起
  • 通道背压降采样try_send() 替代 send().await,满时丢弃瞬时状态事件(MessageDelta/Status),engine 不再因通道满而停转
  • SQLite 并发加固:WAL 模式 + busy_timeout=5000 + per-connection PRAGMA 配置,200 并发写入零 SQLITE_BUSY
  • APPROVAL_TIMEOUT 可配:硬编码 300s 改为 OnceLock + 环境变量 CODEWHALE_APPROVAL_TIMEOUT_SECS 覆盖

🧰 其他改进

  • 30 个 & 指令(记忆/项目分析/代码编辑/会话/文档/实用工具)
  • !<command> Bash 直通
  • 完整汉化(280+ MessageId)
  • 主题颜色撞色修复
  • Dream RAG 缓存优化 + Microcompact 折叠
  • 默认模型 deepseek-v4-prodeepseek-v4-flash

nyamu fork v0.8.46 — 缓存优化 + 微压缩 + 新指令系统

Choose a tag to compare

@jrcjrcc jrcjrcc released this 02 Jun 17:39

nyamu fork v0.8.46 — 缓存优化 + 微压缩 + 新指令系统


💾 缓存命中率大幅提升(核心优化)

系统 prompt 的 ~82K 前缀缓存从"每轮全 miss"修复为"每轮全 hit":

  • Dream RAG 时间戳修复:去掉 manifest 中的动态时间戳 <!-- loaded at {now} -->,改为 <!-- byte-stable across turns -->。系统 prompt 从每轮变化 5+ 秒恢复为完全稳定
  • Manifest mtime 缓存:添加 ManifestCache,按 max_mtime + file_count 做 cache key。记忆文件不变时输出字节完全相同,前缀缓存跨轮命中

收益:每轮节省 ~30K cache-miss → cache-hit 转换。长期会话的缓存命中率从 ~60% 提升至 ~90%+。


⚡ 微压缩算法(Microcompact)

借鉴 Claude Code 的 microcompact 管线,适配 DeepSeek API:

  • 位置:发送 API 请求前自动执行,无感运行
  • 触发:可压缩 tool result 超过 5 个时,自动折叠最旧的结果
  • 处理:保留最近 5 个完整内容,更旧的替换为折叠标记 + retrieve_tool_result 路径引用
  • 可压缩工具:read_file, exec_shell, grep_files, file_search, web_search, fetch_url, list_dir
  • 与 Spillover 互补:Spillover 管单个结果 > 100 KiB 的体积,Microcompact 管累积结果的数量,两者互不冲突

收益:请求 body 缩小 30-50%,重建缓存更快更便宜。


🆕 新功能

功能 说明
!<command> bash 直通 用户输入 ! 前缀直接执行 Shell 命令,不经过 AI。适合 git pushnpm installgcloud auth login 等交互式或用户想自己控制的命令。输出 10K 字符上限,超长自动截断。
&bak 递归备份工作区所有代码文件(40+ 扩展名,跳过 .git/node_modules/target),自动跳过已有 .bak
&rmb 删除所有 <name>.<ext>.bak 备份文件
&¥ 直调 DeepSeek /user/balance API 查询余额,支持 DEEPSEEK_API_KEY 环境变量

🔧 其他改进

  • 默认模型改为 deepseek-v4-flash(+ reasoning_effort = max),日常响应更快
  • 添加 academic-paper-pipeline 默认 skill
  • & 命令三处注册入口统一(帮助面板 / 命令面板 / 速选栏)
  • 修复 ! 前缀支持无空格写法(!hello! hello 均可)

Full Changelog: v0.8.46...v0.8.46-nyamu

基于 CodeWhale v0.8.46 的定制分支

Choose a tag to compare

基于 CodeWhale v0.8.46 的定制分支