Skip to content

feat(coding-agent): OpenCode CLI 适配器,语音 Agent 可选 OpenCode 后端 (Refs #579)#638

Open
appergb wants to merge 1 commit into
betafrom
feat/issue-579-opencode-adapter
Open

feat(coding-agent): OpenCode CLI 适配器,语音 Agent 可选 OpenCode 后端 (Refs #579)#638
appergb wants to merge 1 commit into
betafrom
feat/issue-579-opencode-adapter

Conversation

@appergb

@appergb appergb commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

User description

背景

Refs #579#579 要的「语音快捷键触发 Claude Code / OpenCode Agent」中,Claude Code 后端已随 fast-agent / less-computer 落地,但 OpenCode 后端只有占位:coding_agent_provider 已有 opencode-cli 选项(标「即将支持」),但后端从不读它、永远跑 claude。本 PR 把 OpenCode 真正接上,照既有 coding_agent 适配模式实现,不另造架构

改动清单

后端

  • coding_agent/opencode.rs(新增):OpenCode 适配器,与 Claude 适配器同形、复用同一套 CodingAgentRequest / CodingAgentEvent / CodingAgentError:
    • detect_opencode:opencode --version(复用 parse_claude_versionx.y.z)。
    • build_opencode_args:opencode run --format json --model <provider/model> --dir <cwd> [--dangerously-skip-permissions];prompt 作为 argv 位置参数(OpenCode 从命令行读,不像 claude 从 stdin)。
    • parse_opencode_json_line:解析 OpenCode NDJSON(text → Delta、tool_use → ToolUse、error → Error);OpenCode 的 text 是完整文本块且无带 cost 的终局 result 事件,故累计文本、EOF 处合成 Completed(cost_usd = None)。
    • run_opencode_agent:取消/超时 kill 子进程;护栏经 OPENCODE_CONFIG_CONTENT 注入。
  • coding_agent/args.rs:CodingAgentProvider 枚举(claude-code-cli / opencode-cli,未知回落 Claude)+ from_pref / default_exe
  • coding_agent/guard.rs:build_opencode_guard_config —— 把高风险 bash 前缀翻译成 OpenCode permission.bash 的 deny glob(如 "rm -rf *": "deny")+ webfetch: deny,审批放行的前缀显式 allow。这是 Claude --settings deny 护栏在 OpenCode 的等价物。
  • coordinator/dictation_voice_agent.rs:run_less_computer_oncecoding_agent_provider 分派 Claude / OpenCode 运行器。两条路径都 fail-closed(护栏配置生成失败即中止,绝不无护栏裸跑)。审批拦截探测 + 重跑放行链路本就 provider 无关,自动复用(OpenCode 重跑时把放行前缀传入 guard builder)。
  • coding_agent/commands.rs + lib.rs:coding_agent_detect_opencode 命令。

前端

  • CodingAgentSection.tsx:OpenCode 选项从「即将支持」改为可用;选中 OpenCode 后端时探测安装状态并提示(已安装显示版本 / 未安装提示先 npm i -g opencode-ai + opencode auth login)。
  • lib/ipc.ts:codingAgentDetectOpencode + OpenCodeDetection
  • i18n:opencodeReady / opencodeMissing(zh-CN / en / ja / ko / zh-TW;替换原 providerOpenCodeSoon)。

安全

  • 沿用语音→shell 路径既有安全姿态:bypassPermissions 在语音路径降级为 acceptEdits 等价(保留护栏);OpenCode 用 permission deny 高风险 bash + 禁 webfetch,fail-closed。
  • prompt 不进 argv 泄露? 注:OpenCode 设计上 prompt 走 argv(CLI 约定),这与 claude 走 stdin 不同 —— 会出现在进程列表里。语音转写一般非敏感,但已在 PR 描述标注;如需可后续探索 opencode serve + stdin 方案。

测试情况

  • cargo check(macOS)✅
  • cargo test --lib:492 通过(新增 10 个单测:provider 解析/默认 exe、OpenCode args/stream 解析、OpenCode 护栏 deny/放行)✅
  • npm run build(tsc + vite)✅
  • 真机实测待办:① 安装 opencodeopencode auth login;② 设置→Less Computer 选 OpenCode 后端,确认安装提示正确;③ 语音触发一次任务,确认流式输出进聊天浮窗、能落地改动;④ 触发一次高风险命令(如 rm -rf)确认被护栏拦截并弹审批卡,Approve 后重跑放行。

为什么是 Refs 而非 Closes #579

#579 还含「语音润色模型配置」一项:prefs 字段(coding_agent_use_voice_polish / voice_polish_model_mode / voice_polish_provider_id / voice_polish_thinking_enabled)已存在但全链路未实现——语音 Agent 当前把原始转写直接送 agent,未经语音润色模型整理,也无对应 UI。该项不在本 PR 范围,故用 Refs #579。建议作为 #579 的剩余子项单独跟进。

🤖 Generated with Claude Code


PR Type

Enhancement, Tests


Description

  • 新增 OpenCode CLI 适配器,语音 Agent 可选 OpenCode 后端

  • 护栏配置支持 OpenCode permission 格式(env 注入)

  • 运行器按 provider 分派 Claude/OpenCode,fail-closed

  • 前端设置页检测 OpenCode 安装并显示提示

  • 单元测试覆盖适配器、护栏、参数构造


Diagram Walkthrough

flowchart LR
  A["用户选择 OpenCode 后端"] --> B["检测 opencode 安装"]
  B --> C{"已安装?"}
  C -->|是| D["设置页显示 ready"]
  C -->|否| E["提示安装 npm i -g opencode-ai"]
  F["开始语音 Agent 任务"] --> G{"Provider?"}
  G --> H["Claude Code CLI"]
  G --> I["OpenCode CLI"]
  H --> J["构建 --settings 护栏文件"]
  I --> K["构建 OPENCODE_CONFIG_CONTENT"]
  J --> L["run_claude_agent"]
  K --> M["run_opencode_agent"]
  L --> N["流式事件到前端"]
  M --> N
Loading

File Walkthrough

Relevant files
Enhancement
9 files
args.rs
添加 CodingAgentProvider 枚举及解析                                                         
+55/-0   
commands.rs
添加 opencode 检测命令                                                                                 
+29/-0   
guard.rs
添加 OpenCode 护栏配置构建                                                                             
+72/-0   
mod.rs
导出 OpenCode 模块和函数                                                                               
+8/-4     
opencode.rs
新文件:OpenCode CLI 适配器核心                                                                     
+344/-0 
dictation_voice_agent.rs
按 provider 分派 Claude/OpenCode 运行器                                               
+94/-54 
lib.rs
注册 coding_agent_detect_opencode 命令                                             
+1/-0     
ipc.ts
添加 OpenCode 检测 IPC 函数                                                                       
+20/-0   
CodingAgentSection.tsx
OpenCode 选项变为可用,增加安装检测                                                                     
+36/-1   
Documentation
5 files
en.ts
新增 opencode 安装状态文案                                                                             
+2/-1     
ja.ts
更新日文 opencode 文案                                                                                 
+2/-1     
ko.ts
更新韩文 opencode 文案                                                                                 
+2/-1     
zh-CN.ts
更新中文 opencode 文案                                                                                 
+2/-1     
zh-TW.ts
更新繁体中文 opencode 文案                                                                             
+2/-1     

)

把 OpenCode 当作与 Claude Code 同类的 coding agent CLI 接入,照既有 coding_agent
适配模式(不另造架构),复用同一套 CodingAgentRequest / CodingAgentEvent /
CodingAgentError / 审批护栏链路。

后端:
- coding_agent/opencode.rs(新):detect_opencode(opencode --version)、
  build_opencode_args(opencode run --format json --model --dir
  [--dangerously-skip-permissions],prompt 作为 argv)、parse_opencode_json_line
  (NDJSON:text/tool_use/error;text 块累计,EOF 合成 Completed)、
  run_opencode_agent(取消/超时 kill,护栏经 OPENCODE_CONFIG_CONTENT 注入)。
- coding_agent/args.rs:CodingAgentProvider 枚举(claude-code-cli/opencode-cli,
  未知回落 Claude)+ from_pref/default_exe。
- coding_agent/guard.rs:build_opencode_guard_config —— 把高风险 bash 前缀翻译成
  OpenCode permission.bash deny glob + webfetch deny,审批放行的前缀显式 allow。
- coordinator/dictation_voice_agent: run_less_computer_once 按 coding_agent_provider
  分派 Claude / OpenCode 运行器;两路都 fail-closed(护栏生成失败即中止,不裸跑)。
  审批拦截探测/重跑放行链路 provider 无关,自动复用。
- commands: coding_agent_detect_opencode 命令(已注册 lib.rs)。

前端:
- CodingAgentSection: OpenCode 选项从「即将支持」改为可用 + 选中后探测安装状态并提示。
- ipc: codingAgentDetectOpencode + OpenCodeDetection。
- i18n: opencodeReady / opencodeMissing(5 语言,替换 providerOpenCodeSoon)。

验证:cargo check + cargo test(492 通过)+ npm run build 均通过。

Refs #579#579 还含「语音润色模型配置」:prefs 字段已存在但全链路未实现/未接 UI,
转写直接进 agent 未经语音润色模型整理 —— 故用 Refs 不用 Closes。)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

579 - Partially compliant

Compliant requirements:

  • 实现了 OpenCode CLI 适配器,包括检测、运行、解析、护栏注入
  • provider id 为 opencode-cli,用户可通过设置选择
  • 护栏通过 OPENCODE_CONFIG_CONTENT 环境变量注入,内置高风险 bash 前缀 deny
  • 检测 opencode --version 并返回版本号
  • 运行支持超时(300s)和取消,通过 kill 子进程实现
  • 默认权限 Plan 下不传递 --dangerously-skip-permissions
  • 前端设置页增加了 OpenCode 选项,并显示安装检测状态

Non-compliant requirements:

  • 未将 OpenCode 选项标记为 "experimental"(ticket 要求若缺少稳定输出协议则需标记)
  • 未实现可配置的 executable path(当前 hardcode "opencode")

Requires further human verification:

  • OpenCode CLI 在实际运行中的稳定性和输出协议是否符合预期
  • 护栏配置(bash deny 前缀列表)是否足够覆盖高风险命令
  • 参数注入风险的缓解措施(添加 "--" 分隔符)是否需要应用
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 Security concerns

参数注入风险:OpenCode 适配器将 prompt 作为命令行位置参数,未使用 "--" 分隔选项,可能导致以连字符开头的 prompt 被误解析为 CLI 选项。建议添加 "--" 分隔符。其他方面无明显敏感信息泄露或高风险漏洞。

⚡ Recommended focus areas for review

参数注入风险

在 run_opencode_agent 中,prompt 通过 .arg(&req.prompt) 直接作为位置参数传递给 opencode run。如果 prompt 以连字符开头(例如 "--help"),可能被 OpenCode CLI 解析为命令行选项而非用户指令,导致行为异常或信息泄露。建议在 prompt 之前添加 "--" 参数以终止选项解析,确保 prompt 始终被视为位置参数。

let mut cmd = augmented_command(exe);
cmd.args(&args)
    // prompt 作为最后的位置参数(OpenCode 从 argv 读取 message)。
    .arg(&req.prompt)
缺少 experimental 标记

根据 ticket #579 要求,若 OpenCode CLI 缺少稳定的非交互 stream 输出协议,应先以 experimental provider 暴露,且 UI 中显示 "experimental" 标签。当前代码直接将 OpenCode 作为正式选项(未添加任何实验性标识),可能使用户在未充分了解风险时使用。建议按照 ticket 要求添加实验性标注。

    <option value="opencode-cli">OpenCode</option>
  </select>
</SettingRow>

@H-Chris233

Copy link
Copy Markdown
Collaborator

@appergb 看一下这个参数注入风险

@H-Chris233 H-Chris233 added enhancement New feature or request area:agent Agent area needs-tests Needs tests labels Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:agent Agent area enhancement New feature or request needs-tests Needs tests Review effort 3/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] 语音快捷键触发 Claude Code / OpenCode Agent,并支持语音润色模型配置

2 participants