Jul 3 16:23 feat: image in ui 14:51 feat: timg -p kitty 06:09 fix: render timg images within Ink's virtual DOM to prevent cursor desync chenbhao +1: Claude Big Pickle timg Unicode-block output was written to stdout via process.stdout.write(),
bypassing Ink's rendering pipeline. Ink's virtual cursor didn't know about
the image rows, causing subsequent UI (prompt, input bar) to render at wrong
positions — overlapping the image and duplicating input bars.
Now the timg ANSI output is passed through the tool data and rendered via
the RawAnsi component within Ink's virtual DOM. Ink's Yoga layout accounts
for the image's width × height, keeping cursor position in sync.
Co-Authored-By: Claude Big Pickle <noreply@anthropic.com> 03:48 feat: add inline terminal image rendering with Kitty protocol support chenbhao +1: Claude Big Pickle Introduce InlineImage component, ImageShow tool, and image caching/detection
utilities for rendering images directly in the terminal via the Kitty graphics
protocol. Falls back to clickable hyperlinks when protocol is unavailable.
Co-Authored-By: Claude Big Pickle <noreply@anthropic.com> Jul 1 15:34 feat: remove result limits on LocationTool place searches (Amap/Google) chenbhao +1: Claude Big Pickle - Amap (高德): offset 25 → 1000 per page with pagination loop (≤20 pages)
- Google Places: remove .slice(0,20), add next_page_token pagination (≤3 pages)
- Photos list: remove .slice(0,3) limit
Co-Authored-By: Claude Big Pickle <noreply@anthropic.com> 14:02 feat: add LocationTool — geographic search with Amap/Google Maps chenbhao +1: Claude Big Pickle Add a new LocationTool supporting geocoding, POI search, directions,
and trip planning. Automatically selects Amap (China) or Google Maps
(international) based on region detection.
Co-Authored-By: Claude Big Pickle <noreply@anthropic.com> Jun 30 03:01 fix: /friend motion — interrupt hold on new action, drain queue, fix emotion mapping chenbhao +1: Claude Big Pickle - MotionController: interrupt hold when new playAction arrives instead of
queueing, so LLM text-message actions are not blocked by stale 10s hold
- MotionController: drain action queue in hold timer callback to prevent
silent drop of queued actions
- App.tsx: remove hold=true from emotion-only messages — action plays once,
emotion timer handles duration independently
- App.tsx: remove awkward emotion→action mappings (neutral→salute,
relaxed→salute, sad→shy) that caused unnatural gestures
Co-Authored-By: Claude Big Pickle <noreply@anthropic.com> 02:08 feat: /friend aliveness system, motion controller fix, VAD tuning, high-pass filter chenbhao +1: Claude Big Pickle - Remove redundant files (HistoryPanel, MoodIndicator, vmd-loader, vrm-ik-handler, web-file-picker, FriendScreenObserveTool)
- Add action parameter to friend_emotion for VRM animation playback
- Fix motion-controller: replace broken 'finished' event with frame-accurate completion detection and action queue
- Add aliveness system: breathing, postural sway, speech micro-movements, listening response, micro-expressions, enhanced blink
- Add Silero VAD threshold tuning and high-pass filter for low-frequency noise rejection
- Fix build scripts (vite build instead of tauri build)
- Add LipSync.isActive() for speech-driven animation blending
Co-Authored-By: Claude Big Pickle <noreply@anthropic.com> Jun 29 16:58 fix: prevent TTS replay of historical messages on session resume chenbhao +1: Claude Big Pickle useAutoTTS now detects session switches (resume) via UUID discontinuity
and treats all loaded messages as history to skip TTS. Only subsequent
real-time assistant replies will trigger speech.
Co-Authored-By: Claude Big Pickle <noreply@anthropic.com> 14:15 chore: remove obsolete test and vendor source, add CLI SDK docs chenbhao +1: Claude Big Pickle - Remove test_feishu_ws.ts (contained secrets)
- Remove vendor/audio-capture-src/index.ts (replaced by
packages/audio-capture-napi)
- Add docs/cli/ build system documentation
- Add src/cli/ SDK transport and handler modules
Co-Authored-By: Claude Big Pickle <noreply@anthropic.com> 12:44 feat: /debug v2.0 已将 VersperClaw 的 /debug 从被动日志阅读技能完全替换为 Openclaude /debug-mode 的主动插桩调试工作流。
改动文件
┌───────────────────────────────┬──────┬──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 文件 │ 操作 │ 说明 │
├───────────────────────────────┼──────┼──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ src/tools/DebugSessionTool.ts │ 新建 │ 移植自 Openclaude,管理 .versperclaw-debug/ 目录(init/begin_run/begin_verify/read_log/cleanup) │
├───────────────────────────────┼──────┼──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ src/tools.ts │ 修改 │ 导入并注册 DebugSessionTool 到工具列表 │
├───────────────────────────────┼──────┼─────────────────────────────────────────────────────… Jun 28 12:49 feat: Filter TTS rules based on language 09:21 fix: FriendEmotionTool.call() 08:59 feat: /friend cosplay 07:58 feat: Friend 消息 → 仅使用人设提示词 修改的文件 (5个)
核心流程:Friend 消息 → 仅使用人设提示词
1. src/types/textInputTypes.ts
- 在 QueuedCommand 中添加 overrideSystemPrompt?: string 字段,用于存储替代系统提示词
2. src/skills/bundled/friendPrompt.ts
- 导出 loadPersona() 和 buildVrmSystemPrompt(),供 FriendService 调用
3. src/friend/FriendService.ts
- 在 sendText() 中构建 VRM 人设提示词,作为 overrideSystemPrompt 随消息入队
4. src/utils/handlePromptSubmit.ts
- 在 onQuery 类型中添加 overrideSystemPrompt 参数
- 从 commands[0] 提取并传递给 onQuery
5. src/screens/REPL.tsx
- onQuery 和 onQueryImpl 均接收 overrideSystemPrompt 参数
- 传入 buildEffectiveSystemPrompt(),当该值存在时完全替代默认系统提示词
数据流
FriendService.sendText()
→ buildVrmSystemPrompt() ← 只构建 VRM 人设提示词
→ enqueue({ overrideSystemPrompt, ... })
→ processQueueIfReady()
→ handlePromptSubmit()
→ executeUserInput()
→ onQuery(..., overrideSystemPrompt)
→ onQueryImpl(..., overrideSystemPrompt)
→ buildEffectiveSystemPrompt({ overrideSystemPrompt })
→ 返回 [overrideSystemPrompt] ← 完全绕过 ~900 行 … 07:57 fix: tts not read tool message Jun 22 11:24 chore: questionnaire rename 11:05 feat: VAD Noise Suppression 代码正确。运行现有测试确保没有破坏。
● Bash(npx vitest run src/friend/__tests__/ 2>&1 | tail -30)
⎿ FAIL src/friend/__tests__/e2e-http-flow.test.ts [ src/friend/__tests__/e2e-http-flow.test.ts ]
Error: Cannot find package 'bun:test' imported from /home/yuki/Code/Agent/VersperClaw/src/friend/__tests__/e2e-http-flow.test.ts
❯ src/friend/__tests__/e2e-http-flow.test.ts:10:1
… +26 lines (ctrl+o to expand)
⎿ (timeout 30s)
● 测试使用 bun,需要 bun test。
● Bash(bun test src/friend/__tests__/ 2>&1 | tail -30)
⎿ bun test v1.3.14-canary.1 (0d9b296a)
src/friend/__tests__/e2e-http-flow.test.ts:
… +15 lines (ctrl+o to expand)
● 4 测试全部通过,15 个 expect() 均无失败。
修改总结
vad-service.ts — 核心 VAD 检测逻辑
阈值调整(减少假阳性):
┌─────────────────────────┬───────────┬────────────┬─────────────────────────────────────┐
│ 参数 │ 旧值 │ 新值 │ 作用 │
├─────────────────────────┼───────────┼────────────┼─────────────────────────────────────┤
│… 10:33 chore: no appimage 10:23 fix: tools call or think voice play will stop receive audio 用户说话 → VAD检测说话结束 → _flushVadSegment()
→ sendText(transcript) → 进入CLI对话
→ startAiTurnMute()
├─ muted = true
├─ VAD pause()
└─ 30秒超时(安全网,覆盖AI处理、工具调用、思考等)
AI处理中(WebSearch / 深度思考 / 等) → 音频静音,不收不送
AI回复就绪 → broadcastResponse()
→ TTS生成成功
→ extendMuteForTts(audioId)
├─ 清除30秒安全网
├─ 解析MP3算出精确音频时长
└─ 新定时器 = 精确时长
TTS播放中 → 音频静音
TTS播放完毕 → unmute()
├─ muted = false
├─ VAD start() → 恢复收音
└─ 下一轮对话开始
30 秒兜底防止 AI 不回复(错误等)导致永久静音,extendMuteForTts 在 TTS 生成后会精确定时到音频实际长度 10:12 fix: TTS voice play will stop receive audio 现在 muteForTts 不再靠估算,而是实际解析 MP3 文件头算出精确时长:
1. 找到前两个帧同步(0xFFE0),测量出实际帧间距(144 字节)
2. 用这个帧间距作为步长遍历文件统计帧数
3. 从第一帧头部解析出采样率和每帧采样数
4. duration = frameCount × samplesPerFrame / sampleRate
短句 "你好,这是一段测试语音。" → 2880ms ✓
中等 "你好,这是一段...voice message." → 6024ms ✓
长句 "你好!我是Claude..." → 14400ms ✓
全部精确匹配 ffprobe
静音时长不含推测余量,纯按实际 TTS 音频长度来,用户话音一落地就恢复收音 09:45 fix: echo in VAD STT TTS 回声问题修复
新增静音机制 — 在 AI 通过 TTS 回答期间,暂时阻断麦克风音频进入 STT 和 VAD:
1. muted 标志 + muteTimer — 控制音频采集的静音状态
2. muteForTts(text) — 根据回复文本长度估算 TTS 播放时长,自动管理静音期:
- 估算公式:max(3000ms, text.length × 100ms + 1500ms)
- 中文字符约 125ms/字,英文约 83ms/字符,+1s 生成/网络开销 + 0.5s 余量
- 最短 3 秒
3. clearMute() — 用户按 F2 结束通话时立即清除静音状态
4. arecord 回调中的 feedAudio(c) — 静音时直接 return,音频不进入 STT 也不进入 VAD
完整流程:
用户说话 → 音频 → STT → 转录 → AI → broadcastResponse()
↓ TTS生成成功 + 采集进行中
muteForTts(text)
↓
静音期间所有音频丢弃
↓ (TTS播放完毕)
setTimeout → unmute
↓
恢复音频采集 09:37 feat: VAD 08:51 feat: polling voice create stt 5s 架构变更:浏览器 VAD → 服务器端定时分段
根本问题
onnxruntime-web WASM 后端在 Tauri 的 WebKitGTK
中无法运行(即使所有文件都在本地提供)。这是 WebKitGTK 的一个已知限制。
解决方案
不再尝试在浏览器中运行 VAD。改为服务器端的定时音频分段:
F2 开始 → 服务器用 cpal/arecord 录制音频 → 每 5 秒:
├─ 结束当前 STT 连接 → Groq 转写 → sendText() → CLI 对话
└─ 创建新的 STT 连接,继续录制
F2 结束 → 清理
变更文件
┌──────────────────┬────────────────────────────────────────────────────────────┐
│ 文件 │ 变更 │
├──────────────────┼────────────────────────────────────────────────────────────┤
│ FriendService.ts │ 新增定时分段:_flushSegment() + captureTimer,将音频分段为 │
│ │ 5 秒窗口,自动发送到 CLI │
├──────────────────┼────────────────────────────────────────────────────────────┤
│ useServerStt.ts │ 完全重写:移除浏览器 VAD(@ricky0123/vad-web / │
│ │ onnxruntime-web),新增基于轮询的服务器端语音通话 │
├──────────────────┼─────────────────────────────────────────────────────────… 07:40 feat: VAD but just case choose 00:45 feat: cycle time activate check friend Jun 21 16:28 feat: /voice use best grop whisper provider free and fast 15:30 feat: grop free stt 14:31 feat: /friend tts add websearch 14:11 feat: friend text tts ok but websearch tts not ok 13:28 fix: Microphone access denied 12:07 feat: inter-process 架构对比
之前: 之后:
CLI TUI CLI TUI (单一进程)
└── /friend start ├── FriendService (in-process, cpal)
├── 服务器子进程 :3456 │ ├── STT via cpal
│ ├── chat-service.ts │ ├── enqueue() → 主 CLI
│ │ └── CLI SDK 子进程│ └── SSE → Tauri
│ ├── arecord 子进程 ├── 轻量 Bun.serve (in-process)
│ └── SSE └── Tauri (thin display only)
└── Tauri (getUserMedia) 08:11 feat: csp null 00:30 feat: full screen friend Jun 20 19:28 fix: friend render 18:20 chore: cancel exc /friend (no content) 17:50 fix: /friend start stop (no content) 17:32 fix: auto esc /friend start stop 17:20 fix: after /friend stop can esc 17:16 fix: esc in /friend start 1. 启动 Tauri 窗口(异步,后台运行)
2. 渲染 FriendStartView 组件,显示状态信息
3. 按 Esc 或 Enter 关闭 TUI 面板,Tauri 窗口继续在后台运行
launchTauri 之前用了 await 会阻塞 UI,现在改成了直接调用让它在后台异步运行。试试 /friend start 看看效果 16:45 fix: just friend desktop no browser 16:34 feat: /friend start tauri app 14:29 feat: friend refactor to src 10:31 tools: add prompt() to Friend tools to avoid undefined prompt runtime error chenbhao +1: Copilot Ensure tools expose prompt() to satisfy callers that invoke tool.prompt() at runtime.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> 08:46 feat: /friend Jun 19 16:34 chore: rename session title 16:14 feat: keep only color 15:16 feat: /release-notes and what's news /cache/changelog.md 14:42 chore: use pre-compiler code Jun 18 10:50 fix: /goal clear but /resume will get old goal /goal clear 只清了内存中的 AppState.goal,但在 reAppendSessionMetadata 退出时把旧缓存 currentSessionGoal 又写回了
transcript。而且 loadTranscriptFile 是 last-wins 逻辑,一条旧的 goal entry 永远不会被清除。
需要两个修复:
1. /goal clear 时写入一条 goal 且带 cleared 标记,或写入 null 来覆盖
2. reAppendSessionMetadata 不要在 goal 已清除时重新写入
最简洁的方案:写入一条 goal: null 的 entry,加载时遇到 null 就视为无 goal