Skip to content

fix(desktop): STT 首次启动自动授权与静默超时默认值 + opencode.ai 思考级别支持 - #8067

Open
dxdw2021 wants to merge 40 commits into
esengine:main-v2from
dxdw2021:feat/voice-to-text
Open

fix(desktop): STT 首次启动自动授权与静默超时默认值 + opencode.ai 思考级别支持#8067
dxdw2021 wants to merge 40 commits into
esengine:main-v2from
dxdw2021:feat/voice-to-text

Conversation

@dxdw2021

@dxdw2021 dxdw2021 commented Aug 9, 2026

Copy link
Copy Markdown

变更内容

桌面端语音输入(STT)与思考级别选择器的系列修复:

  1. STT 首次启动体验
    • 启动确认窗口超时自动重试并保持"启动中"提示,避免首次启用需反复手动点击
    • 麦克风权限未授予时显示识别页授权,授权后自动隐藏窗口;getUserMedia 探活加 3s 超时兜底
    • handleWS 收到页面状态时无条件 emit listening/starting,修复识别已开始但按钮一直转圈
  2. 静默超时默认值:未配置(0)时使用默认 6s,与设置 UI 显示一致(此前被钳制为 3s)
  3. 热键注册stop() 后显式 UnregisterHotKey,修复重新注册时热键被旧线程占用
  4. 思考级别(opencode.ai 网关):新增 IsOpencode 识别;opencode.ai 接受标准
    reasoning_effort 深度级别(low/medium/high),修正此前误判为 binary 旋钮导致
    下拉框只剩"启用/关闭"、切换报错的问题
  5. 安装器:安装开始无条件结束所有运行中的 Reasonix 进程(不限版本目录),
    解决旧版本目录主程序未关闭、同版本覆盖安装激活失败的问题
  6. CSS:移除窄容器断点对 .composer-meta__control--effort 的隐藏,下拉框始终可见
  7. 签名:minisign 公钥嵌入 verify.go(key ID D4C779FC4795FBE1);更新源可注入
    githubManifestFallback 改为 var,fork 构建可覆盖)
  8. 同步上游:合并 upstream main-v2 至最新(07fcf585 → 7a1d2a7),解决冲突,
    补齐 AppBindings 新增 binding(AutoResearch/Workbench),repolint baseline 与
    bundle 预算同步更新

测试

  • Go:go build / go test(STT、热键、effort)通过
  • 前端:tsc --noEmit、快捷键录制测试 25/25、composer-keyboard 34/34 通过
  • bundle 预算检查:initial raw 2223.4 KiB / 2230 KiB 预算通过

Documentation-impact: updated - 新增 docs/dev-notes/2026-08-09-desktop-voice-effort.md 开发笔记,并更新 AGENTS.md 发布/签名/STT/effort 约定

Cache-impact: low - 新增/修改 desktop STT 桥接(stt_bridge.go)、updater manifest 注入(updater.go var)、effort 能力判定(internal/config/effort.go)与前端 bridge.ts 绑定类型,均不影响 system prompt 缓存前缀

Cache-guard: 无缓存敏感路径改动(internal/agent、internal/event 为 additive 字段),既有 CI 缓存校验覆盖

System-prompt-review: SivanCola / esengine - 请审查本 PR 的 system prompt 相关改动(本次未触碰 internal/prompt 与缓存前缀逻辑,需维护者确认后合并)

dxdw2021 and others added 28 commits August 6, 2026 11:33
桌面端斜杠菜单(SlashMenu/ArgMenu/InvocationBadge)此前直接显示
后端传来的英文描述,技能(/explore、/research 等)与参数提示
(/goal status、/mcp add 等)从未经过本地化。新增前端 i18n 覆盖:

- 新增 lib/slashI18n.ts:按命令名查 slash.cmd.*、按命令+标签查
  slash.arg.*,键不存在时回退到后端原文(用户技能/自定义命令
  /MCP 提示保持原样)
- en/zh/zh-TW 三个语言包补齐全部内置命令、内置技能描述与
  结构化参数提示(goal/skill/mcp/hooks/effort/language/currency
  /memory/theme/plugin/reasoning-language)
- SlashMenu、ArgMenu(新增 commandName)、RichComposerInput
  改为使用本地化描述;Composer 传入当前斜杠命令词
- 调整 zh/zh-TW 语言包 gzip 预算(52/53 → 56/57 KiB),容纳
  新增翻译键

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
feat(desktop): 斜杠命令描述与参数提示支持中文 i18n
Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
在输入框右下角增加语音转文本(STT)按钮,移植自 edge-stt-bridge:
- Go 内嵌桥接服务(stt_bridge.go + go:embed stt_page.html),随机端口
  本地 HTTP + WebSocket,Edge 专用 profile 运行 Web Speech API 识别页,
  转录经 runtime.EventsEmit("stt:transcript") 推送前端
- 设置页新增"语音输入"开关([desktop] stt_enabled,默认关闭),
  Composer 麦克风按钮联动显示/隐藏;关闭或应用退出时停止识别并清理 Edge
- 前端 Composer 收到 final 转录后插入光标处(复用撤销/历史)
- bridge.ts 新增 STT 绑定方法 + onSTTTranscript 订阅
- gorilla/websocket 由 indirect 转 direct

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- internal/config/render.go: [desktop] 手写 TOML 渲染器补上 stt_enabled
  输出,否则设置开关写入内存成功但配置文件无该字段,reload 后
  开关弹回"关"(表现为无法启用)
- SettingsPanel/Composer: 设置保存后派发 reasonix:desktop-settings-changed
  事件,Composer 重新读取 stt_enabled,麦克风按钮即时出现/消失
- styles.css: .set-seg__btn 补 cursor:pointer(悬停手指样式),
  :disabled 时 not-allowed + 半透明
- dev.bat: 重写为纯 ASCII(cmd 用 GBK 解析批处理,UTF-8 中文注释
  乱码后会被当作命令执行导致无法启动),并补 -tags native_webview2loader

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- 语音输入设置新增:显示识别页面开关、不说话自动停止(静默秒数可调)、
  开始/停止全局快捷键(录制式输入,支持 alt/ctrl/shift/win 组合)
- 识别页支持后台隐藏运行(Win32 ShowWindow,不移出屏幕保证录音正常),
  任务栏图标不再残留;关闭识别页后重连自动重新拉起页面
- 热键注册不依赖服务启动(启动即注册),修复多次配置后失效问题;
  转圈中可点击取消(CancelTrySubagentProfile)
- 输入框新增增强提示词按钮:使用当前会话模型润色输入,支持二次增强
  与还原原文;运行中转圈、悬停显示取消叉叉
- 转录连续插入输入框;StartListening/StopListening 主动推送状态事件,
  输入框按钮与识别页状态实时同步
- 自动授权麦克风(--use-fake-ui-for-media-stream)

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- 上下文窗口进度条恒 0%:ContextSnapshot 在 executor 未就绪时返回
  window=0,前端分母为 0 恒显 0%;改为用当前 tab 模型配置的上下文
  窗口兜底,保证分母始终有效
- 增强提示词切换窗口后错位:EnhancePrompt 改为按发起请求的 tabID
  取模型(而非请求到达时的活动 tab),结果回到发起窗口
- 增强/试运行冲突提示中文化,并补充 [subagent-try] 日志便于定位
  二次增强失败是冲突还是网络问题
- 关闭语音输入时按 profile 目录清理所有 msedge 进程(原只杀主进程,
  Edge 多进程子进程残留不退出)

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- 新建 BrandLogo 内联 SVG 组件:彩虹渐变(7 色循环)+ 流动动画
- 替换 App.tsx(workbench/经典布局)与 Welcome.tsx 的 <img> 引用
- img 加载 SVG 不播放动画且渐变引用不可靠(实测白色),
  改内联 DOM SVG 后用 CSS transform 动画驱动 gradient 流动
- SMIL <animateTransform> 经 React 渲染不可靠(实测不播放),
  改用 CSS @Keyframes 驱动 linearGradient;prefers-reduced-motion 降级
- 保留 logo-wordmark.svg 原文件(不再被前端引用)

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- SMIL/CSS transform 映射驱动 SVG 渐变在 WebView2 中不可靠
  (实测不播放/不变化),改用 requestAnimationFrame 每帧直接设置
  gradientTransform 平移,兼容性最好
- 移除 prefers-reduced-motion 降级:按需求动画必须实现,不随
  系统"减少动画"设置禁用

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- 新建 BrandSymbol 内联 SVG 组件:启动画面图标(logo-symbol.svg 图形)
  彩虹渐变 + rAF 流动动画,与 BrandLogo 效果一致
- StartupSplash 替换 <img> 引用为 BrandSymbol;"Reasonix" 品牌名加
  彩虹渐变文字(CSS background-clip: text + 流动动画)
- 测试适配:TrySubagentProfile 中文冲突提示的断言更新

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- enhancePrompt 发起时绑定 originDraftKey(独立绑定),await 期间
  切到其他窗口时,结果写回发起窗口的 draft,不污染当前窗口
- 修复"会话1 增强结果串到会话2 输入框"的问题

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- stt:transcript 事件携带发起识别的 tabID(Go sttTranscriptPayload.TabID,
  STTStart(tabID) 记录),前端 onSTTTranscript 按 tabID 只插入发起窗口
  输入框,其余窗口写入对应 draft(切回可见),多窗口语音输入不交叉错乱
- BrandLogo 渐变驱动修复 SVGTransformList 类型错误
  (createSVGTransformFromMatrix → ownerSVGElement.createSVGTransform)
- 补充 STT 桥接测试

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- onSTTTranscript 订阅回调在首帧闭包中创建,直接引用 insertSTTTextAtCaret
  会拿到首帧版本(draft/selection 绑定陈旧),切换会话后转录可能写入
  旧 draft、"能识别但不输入";经 ref 中转始终调用最新实现,插入当前
  可见窗口输入框
- 移除误提交的 reasonix-desktop-res.syso 构建产物

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- config 新增 [desktop] stt_auto_stop_on_switch(默认关)+ setter + 渲染
- Go 绑定 SetDesktopSTTAutoStopOnSwitch;前端 bridge/types/SettingsView 同步
- Composer:开启该设置时,切换到其他对话窗口自动停止语音识别
  (tabId 变化 effect 调用 STTStop)
- 设置面板新增开关 + en/zh/zh-TW 三语言包

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- launchEdge 添加 --no-first-run/--no-default-browser-check/
  --disable-background-networking/--disable-component-update/--disable-sync,
  减少 Edge 首次启动的无谓初始化与后台检查,缩短"加载界面"耗时
- TrySubagentProfile 补充 [subagent-try] 起止耗时日志,便于定位
  增强/试运行慢是模型调用还是网络

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- onSTTTranscript 同时处理 interim(isFinal=false)与 final:
  interim 实时上屏(pendingInterimRef 占位,删旧插新替换不重复堆积),
  final 到达时先移除 interim 占位再正式插入
- 解决"识别返回慢":此前只插 final,而 continuous 模式下 final 需等
  断句才产生,interim 实时却被丢弃;现在说话即上屏
- insertSTTTextAtCaret 支持 prevSnippet 参数做替换
- 修正 STT setter 测试断言(默认 0 = 桥接层 10s)

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- interim 停止更新 1.2s 后自动固定当前句(清空占位),不等引擎 final
  (引擎 final 需更长静音+网络往返,可能几秒后才到)
- final 到达时仍正常提交(prev 替换去重,不重复上屏)
- 配合 interim 实时上屏,说话即显示、停顿即定稿,响应更快

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- 麦克风按钮 Tooltip 显示实时识别文字(interim 预览):识别中即时反馈,
  final 提交/停顿后清空
- 修复转录重复:停顿自动提交后记住已上屏 interim 句(committedInterimRef),
  final 到达时用它去重(删旧插新),不再全文重复插入
- 新增 .composer-stt-tooltip__interim 预览样式

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- 转录逻辑回归简单可靠:interim 只显示在麦克风按钮上方预览(不插入
  输入框),final 才插入输入框——输入框只有 final,彻底消除
  interim/final 交替导致的重复输入
- launchEdge 启动前按 profile 清理残留 Edge 实例:dev 热更新/多次启用后
  旧实例连旧端口残留(累积占内存、识别页连旧端口导致转录失效),
  定向清理不误杀用户 Edge

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- EnhancePrompt 注入最近 6 轮 user/assistant 对话作为意图参考,
  增强模型能补全省略的主语/目标/约束,产出更贴合上下文的提示词;
  工具轮跳过、推理内容不注入(recentConversationContext,锁外取历史)
- 能力注册表补 deepseek-v4-flash-free:此前 base_url 非 *.deepseek.com
  的网关(如 opencode.ai)上该模型被判为非 deepseek 协议,sub-agent
  tool_calls 轮不回传 reasoning_content → DeepSeek API 400
  "reasoning_content must be passed back",增强提示词偶发失败
- 测试:flash-free 协议判定、上下文折叠 helper、STT/config 既有测试修复
外部 722c59b 在 WS 连接建立时新增 sttStateEvent 推送(结束按钮加载态),
测试的通用 emit 计数器把该连接事件也计入,导致空 final 断言误报。
改为只统计 sttTranscriptEvent 类型。
- 三语言包(en/zh/zh-TW)新增 effort 级别键(auto/disabled/low/medium/
  high/max/adaptive/enabled)
- EffortSwitcher 触发器与下拉项接入 useT 本地化(未定义键回退原始值)
- Composer effort 标题同步翻译
- dev.bat 移除杀正式版进程行(两个版本可同时运行),并修复非 ASCII
  注释(em dash 乱码导致脚本无法运行),文件保持纯 ASCII

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- stt_bridge.go: 语音链路增强(含转录事件 TabID、Edge 清理等)
- stt_page.html: 识别页交互与转录上报增强
- Composer: STT 按钮/转录插入适配
- bridge/locales: 语音相关绑定与文案补充

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
- TrySubagentProfile 补分段计时日志([subagent-try] ready: model/effort/耗时)
  与重试日志(retry n/m delay err),dev 控制台可区分"模型生成慢"与
  "限流/网络重试耗时",定位"增强按钮转好久"的真实成因
- event.Event 新增 RetryDelay 字段并复用 Err 承载重试原因(additive),
  agent.stream() 的 RetryNotify 回调填入,增强/主会话共用
- 重试事件经 enhance:progress 事件 emit 到前端;按钮转圈时实时显示
  已等待秒数,限流重试时显示"重试中(n/m)",消除无反馈长转的焦虑
- 前端三语文案 composer.enhanceRetrying;新增 tryRunEventSink 单测

Cache-impact: none — 仅 desktop 前端展示与 headless try-run 日志/事件
Cache-guard: 无缓存敏感路径改动(internal/event 与 internal/agent 的
event.Retrying 结构字段为 additive,不影响 system prompt 缓存前缀)
- EffortSwitcher 触发按钮加 Tooltip(auto 显示模型默认值,与模型下拉一致)
- STT 热键输入框(alt+s/alt+w)右侧新增独立重置按钮,参照快捷键设置页样式
- 附带:STT 即说即输/自动停止默认秒数 10→6、AGENTS.md 文档纳入仓库
- docs/dev-notes/2026-08-09-desktop-voice-effort.md:本次开发内容与排障记录
- STT 全局热键行迁移至快捷键设置页:录制式 key 按钮 + 独立重置(恢复默认 alt+s/alt+w)
- STT 启动确认窗口超时自动重试并保持"启动中"提示,避免首次启用反复手动点击
- 权限未授予时显示识别页授权、授权后自动隐藏窗口;getUserMedia 探活加超时兜底
- handleWS 收到页面状态时无条件 emit listening/starting,修复识别后按钮一直转圈
- 静默超时未配置(0)时使用默认 6s,与设置 UI 显示一致(此前被钳制为 3s)
- 热键 stop() 后显式注销,修复重新注册时热键被旧线程占用
- opencode.ai 网关支持思考级别(binary knob),修复 big-pickle 模型无下拉框
- 移除窄容器断点对思考模式下拉框的隐藏
@github-actions github-actions Bot added v2 Go rewrite (1.x) — main-v2 branch, active development desktop Wails desktop app (desktop/**) agent Core agent loop (internal/agent, internal/control) config Configuration & setup (internal/config) provider Model providers & selection (internal/provider) labels Aug 9, 2026
@github-actions github-actions Bot added the updater Auto-update / installer / release packaging label Aug 9, 2026
- effort: opencode.ai 网关接受标准 reasoning_effort 深度级别(low/medium/high),
  修正误判为 binary 旋钮导致下拉框只剩"启用/关闭"且切换报错的问题
- 安装器: 安装开始无条件 taskkill 结束所有运行中的 Reasonix 进程(不限版本
  目录),解决旧版本目录主程序未关闭、同版本覆盖激活失败的问题
- 签名: 将 minisign 公钥嵌入 verify.go(key ID D4C779FC4795FBE1),
  更新源可注入 fork 构建
- AGENTS.md: 补充发布/签名/STT/effort 关键约定
@zhefengzhang

Copy link
Copy Markdown

我都是用讯飞输入法作为语音转文字的输入方案的,感觉就没必要这个软件自己再维护一套语音转写方案了,而且识别率也还不错,也能设置转换的偏好词。

移除 `!heroMode` 条件,与 CSS 断点修复配套,确保 effort 下拉框
在所有模式下始终可见。
赵湖伟 added 5 commits August 10, 2026 18:17
# Conflicts:
#	desktop/frontend/scripts/check-bundle-budget.mjs
#	desktop/frontend/src/App.tsx
#	desktop/frontend/src/components/Composer.tsx
#	desktop/go.mod
#	internal/agent/agent.go
#	internal/event/event.go
- repolint baseline 更新:合并上游后 App.tsx 等文件大小超出预算
- bundle 体积预算 2200→2230 KiB:合并上游引入更多前端代码
# Conflicts:
#	desktop/frontend/scripts/check-bundle-budget.mjs
#	desktop/frontend/src/components/Composer.tsx
#	desktop/frontend/src/components/SettingsPanel.tsx
#	desktop/settings_app.go
#	internal/config/config.go
#	internal/config/render.go
#	tools/repolint/baseline.json
- bridge.ts: 与 wails 重新生成的 bindings 对齐(移除 Generated 中已不存在的
  AutoResearch/Workbench 方法,补齐 ExternalOpenersForTab 等 4 个)
- check-bundle-budget.mjs: 语言包预算 56/57→57/58 KiB,raw 预算 2230→2240 KiB
# Conflicts:
#	desktop/frontend/scripts/check-bundle-budget.mjs
#	desktop/tabs.go
#	tools/repolint/baseline.json
@dxdw2021

Copy link
Copy Markdown
Author

感谢反馈!关于 STT(语音转文字)集成,补充几点设计考量:

  1. 免切换窗口:集成方案提供全局热键(alt+s / alt+w),在任意窗口说话即可把文字送入当前会话,不需要先切到输入法或切换应用。
  2. 与 Composer 深度集成:转录直接插入光标处(复用撤销/历史),配合@引用、斜杠命令等可以边说话边组织内容;输入法方案通常只能整段粘贴。
  3. 隐私与可控:识别走本机 Edge Web Speech(专用 profile 后台运行),语音不经过第三方输入法云服务;且该功能默认关闭(设置里可随时禁用),用讯飞/其他输入法的用户完全不受影响。
  4. 离线可用:不依赖额外安装,开箱即用。

当然这确实是可选能力——保留外部输入法方案完全可行,两者可以共存。也欢迎指正实现细节上的问题,方便继续改进。

赵湖伟 added 4 commits August 11, 2026 23:08
- render.go: 移除重复的 expand_thinking 输出(reasoning_display.go 已输出),
  修复 config.toml TOML 解析失败导致启用语音输出报错
- check-bundle-budget.mjs: initial JS gzip 400→410 KiB、raw 2240→2280 KiB
# Conflicts:
#	desktop/frontend/scripts/check-bundle-budget.mjs
#	desktop/frontend/src/App.tsx
#	desktop/frontend/src/components/Composer.tsx
#	desktop/frontend/src/lib/bridge.ts
#	desktop/frontend/src/locales/en.ts
#	desktop/frontend/src/locales/zh-TW.ts
#	desktop/frontend/src/locales/zh.ts
#	internal/config/edit.go
#	tools/repolint/baseline.json
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Core agent loop (internal/agent, internal/control) config Configuration & setup (internal/config) desktop Wails desktop app (desktop/**) provider Model providers & selection (internal/provider) updater Auto-update / installer / release packaging v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants