sim
4519f19c92
style(app): 统一前端与平台源码格式
2026-09-08 14:35:28 +08:00
sim
a6ed77c0d0
feat(ui): 完善设置子页与自定义风格图标
2026-09-08 14:16:24 +08:00
Chris233
fc38edd1a3
refactor: consolidate Core 2.0 and restore cross-platform behavior ( #1019 )
...
* refactor: split Linux egui host from shared backend
* fix: keep CI contracts portable across runners
* fix(ci): preserve CRLF while removing qwen dependency
* fix(ci): regenerate target lockfile and install ripgrep
* fix(ci): gate desktop-only selection voice module
* ci: limit native build parallelism on cross-platform runners
* fix(host): expose blocking task seam to coordinator
* fix(settings): avoid macOS default hotkey collision
* docs: record cross-platform CI evidence
* ci: expose Linux validation package artifact
* ci: grant reusable Linux package permissions
* refactor: remove legacy coordinator dictation facade
* ci: skip unsupported MSI prerelease builds
* ci: install minisign only for signed Linux releases
* fix: keep test-only coordinator session helper
* refactor: move style preview diagnostics into core
* fix: import shared preferences type from core
* refactor: centralize enabled vocabulary projection
* ci: install ripgrep for Linux package gates
* test: make source contract line-ending agnostic
* fix: keep Linux desktop entry standards compliant
* docs: expose core style diagnostics contract
* ci: resolve Linux package version in app directory
* refactor: centralize ASR vocabulary priority
* fix: use shared vocabulary note constant
* refactor: keep vocabulary priority inside core
* test: retain starting-stop queue coverage
* ci: scope Linux updater URL to the current repository
* docs: close implementation and artifact evidence
* docs: record final CI artifact evidence
* fix(android): align updater plugin versions
* fix(android): pin updater crate exactly
* refactor(core): remove unused type anchor
* docs: record final artifact runs
* fix(remote-input): preserve beta status fields
* fix(tauri): finish beta host merge
* fix(asr): pass scoped operation to native qwen adapter
* fix(macos): bundle mlx metallib as a resource
* test(remote-input): update status wire contract
* feat: share Less Computer voice flow with Linux
* refactor(core): centralize host-independent runtime policy
* refactor(core): complete 2.0 backend consolidation
* fix(ci): prepare Sherpa after cache restore
* fix(macos): route native model paths through Core store
* fix(macos): use ModelStore catalog API
* fix(ci): serialize macOS native test build
* test(ci): preserve macOS cargo diagnostics
* fix(macos): release input source lock before await
* test(core): replace legacy hotkey injection gate
* test(core): move Codex sandbox contract into Core
* fix(core): close final runtime policy gaps
* fix(windows): terminate coding agent process trees
* fix(core): complete local model migration
* fix(credentials): align Core LLM model account
* fix(core): restore 1.x parity across shared backend
* fix(android): complete shared backend stubs
* fix: close cross-platform migration review gaps
* fix: complete migration lifecycles and platform compatibility
* docs: define desktop 2.0 acceptance and Linux handoff
* fix: close desktop parity and session lifecycle gaps
* fix: finish voice lifecycle and desktop feedback parity
* fix: close desktop cancellation and feedback race gaps
* fix: honor proxy settings and reject stale session effects
* fix: dispatch native cleanup on host runtimes and order events
* fix: keep optional recording effects best effort
* fix: close desktop agent model and QA lifecycle gaps
* fix: preserve desktop raw voice and QA turn ownership
* fix: finalize desktop dictation and QA feedback
2026-09-06 18:39:46 +08:00
b7ac91f0e6
feat(coding-agent): Less Computer 接入 dsh(DeepSeek Harness),自带事件插件补齐流式与工具展示 ( #975 )
...
* fix(coding-agent): 版本号解析认预发布号
dsh 的真实输出是 `0.1.0-rc.6`,而解析器要求严格的三段全数字:它被切成
["0","1","0-rc","6"] 四段,判定失败。用这个判「装没装」的地方就会说它没装。
改成认 `-rc.6` / `+build.7` 这类预发布与构建后缀,并原样保留(用户看到的版本号才是
真的)。同时把排版噪声挡在外面:`(1.2.3)` 取到的是 1.2.3,不是 `1.2.3)`。
单测里的样例串是手抄的,抄错了测试照样绿。所以另加一条 #[ignore] 的 live 测试,直接问
本机四个 CLI 要 --version 输出再喂给解析器——那才是「设置页会不会误报没装」的可信证据。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* fix(coding-agent): 「装没装」改看命令能否跑通,不再依赖版本号解析
上一条修的是解析器,那只是补了一个洞。根子在于判据选错了:
installed: version.is_some() // 解析不出版本号 = 判定没装
版本字符串是这些 CLI 里最容易变的东西——改排版、加后缀、换前缀,对它们都不算 breaking
change,但对「拿版本号当判据」的我们就是。
改成:命令跑通(进程起得来且 exit 0)= 装了;版本号只用来显示,解析不出来最多让界面少
显示一个号码,不会把能用的后端说成没装。三个后端共用一个 probe_cli,顺手删掉了三份
几乎一样的 detect_* 。
单测拿 `echo` 当替身——`echo --version` 会原样打印并 exit 0,正是「跑得通但读不出版本号」
这个形状,且不依赖本机装了哪些 agent CLI。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* feat(dsh): 无头 dsh 后端,自带事件 tap 插件
dsh(DeepSeek Harness)的 headless 只在结束时往 stdout 打一行最终文本——没有 JSON、
没有逐字流、没有工具事件。但这不是它缺数据:它内部的会话事件总线上 assistant/chunk、
tool/call、turn/end 一应俱全,只是 headless 汇总完就丢了。
dsh 是插件栈架构,`--patch` 是官方留给外部叠自己那层的入口。这里挂上一个订阅
session/event 的小插件,把需要的事件规范化成 NDJSON 打到 stderr,stdout 的「只打最终
文本」契约原样不动。挂载不侵入用户的 profile,插件随运行落到临时目录、跑完删掉。
上游耦合被刻意压缩在那个 30 行的 JS 里:它把 dsh 的内部事件翻译成我们自己定义的稳定
形状,上游改字段只需要改 JS,Rust 的解析和单测都不用动。tap 是纯增益——挂不上也只是
少了逐字流和工具展示,最终结果照样从 stdout 拿得到。
prompt 不进 argv:dsh 的启动器不认 `--` 作为选项终止符(实测 `dsh --profile headless
"--version"` 会真的去执行 --version),所以以 `-` 开头的转写会被劫持成 flag。既然本来
就要写 patch 文件,索性用它直接覆盖 runner 的 task 配置,prompt 连进程列表都不会出现
——这点比另外三家都严。
护栏走 DSH_PERMISSION_MODE 三档沙箱,沙箱根 = 子进程工作目录。和 Codex 一样没有逐命令
deny 清单,所以审批卡对它同样不生效,撞墙时如实报错。
模型解析永远返回 None:headless profile 没有 --model,模型由 dsh 自己的 profile 决定,
返回 Some 只会让调用方以为选得动。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* feat(settings): Less Computer 后端下拉加入 dsh
第四项。它的能力面和另外三家不一样,设置页如实反映,不给假开关:
- 模型那一行**整行不显示**,换成一句说明:dsh 的 headless 没有模型开关,模型由它自己的
profile 决定,在这里改不了。给个选不动的下拉比不给更糟。
- 复用 Codex 那条沙箱护栏说明(两家都只有粗粒度档位、都不弹审批卡)。
- 装没装当场检测;可执行文件 placeholder 跟着后端走。
文案 5 语言齐全。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* refactor(dsh): tap 插件抽成独立项目 dsh-jsonl,OpenLess 改消费它的公开 schema
原来这个插件是内嵌在 dsh.rs 里的一段 Rust 字符串,只发我们自己要的 4 类事件、用我们
自己的私有形状。它要开源出去(dsh 缺结构化输出,而上游不收 PR,第三方插件是这个能力
唯一可能存在的形式),于是有了「两份会各自跑偏的代码」这个问题。
解法是保证世界上只有一份这个 JS:
- 源头在 github.com/bigsongeth/dsh-jsonl,发完整的、有文档、有版本的事件词表。
- OpenLess 用 include_str! 内嵌一份逐字副本(vendor/dsh-jsonl.js),带版本标记。
内嵌而不是依赖 npm 包:用户装机即用,不需要网络、不需要 npm i,也不碰用户自己的
dsh profile 配置。
- Rust 侧改成解析那个公开 schema(text.delta / tool.call / turn.end),不再有私有形状。
用不上的事件按 schema 的兼容性约定忽略——上游加新事件不会把我们打挂。
**跨仓库没有任何自动同步**,这点没被消除,只是变便宜了:
- scripts/vendor-dsh-jsonl.sh 把同步变成一条命令
- 单测盯着 vendor 文件的版本标记与 VENDORED_DSH_JSONL_VERSION 一致,防止有人直接手改副本
- 插件报的 schema 大版本与我们支持的不一致时留警告日志(不中止:最终结果始终从 stdout
拿得到,流式展示只是锦上添花)
- 真的对不上时,live::tap_plugin_delivers_streaming_and_tool_events 会红
顺带把「禁止任何 import」的断言放宽成「只准 node: 内置」——公开插件要写文件,用了
node:fs,而 node: 内置不需要 node_modules,不影响从临时目录绝对路径加载。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* test(dsh): 沙箱验收的靶子选错了,它一直是靠运气过的
那条「沙箱挡得住工作目录之外的写入」把靶子放在 $TMPDIR。但 dsh 的 workspace-write 把
可写根写死成 [工作目录, "/tmp", $TMPDIR](见 @deepseek-ai/dsh-sandbox 的 writableRoots)
——往 $TMPDIR 写**本来就是允许的**。
它之前能过,只是因为模型碰巧挑了受限更严的 `write` 工具;这次挑了 `bash`,文件就真的
落下去了。也就是说这条测试的成败取决于模型当次的选择,不取决于沙箱是否失效。
改成打真正的边界(家目录),实测会被拒:
bash: /Users/…: Operation not permitted [sandbox: file access denied under workspace-write mode]
另加一条把 dsh 的既有行为钉成契约:$TMPDIR 可写,且**没有配置项能收掉**——Codex 那边
有 -c sandbox_workspace_write.exclude_* 可以收紧,dsh 没有对应物。哪天 dsh 收紧了这条
会红,那是好消息,届时删掉它并更新文档。
所以对用户的措辞只能是「撞到限制会如实报错」,不能说成「写入限制在工作目录内」。
模块文档里把这个边界写清楚了。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* fix(dsh): 收紧运行协议与权限边界
* fix(dsh): 修复权限与连续对话审查项
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-22 00:14:17 +08:00
5ba0fa725c
feat(coding-agent): Less Computer 接入 Codex,语音使唤电脑不再只能用 Claude ( #974 )
...
* fix(coding-agent): 从用户 shell 取真实 PATH,打包后也找得到 CLI
macOS 从 Finder 启动的 GUI 进程拿到的是一份极简 PATH,不经过用户的 shell 配置。
后果是:凡是版本管理器装的 agent CLI 在打包版里一个都找不到——`opencode` 官方脚本
装在 `~/.opencode/bin`,全局 npm 前缀和 nvm 的路径更是带版本号、根本没法硬编码。
用户只能手动去高级设置里填绝对路径。
改成先问用户的 shell 要一份真实 PATH。三个细节是踩出来的,都写进注释了:
- 用 `-lic` 而不是 `-lc`。nvm 这类版本管理器通常在 .zshrc 里初始化,而 .zshrc 只有
交互式 shell 才读;`-lc` 拿到的 PATH 里 nvm 排在 /usr/local/bin 之后,
`#!/usr/bin/env node` 会命中错版本的 node,子进程直接崩。
- 拼接必须保序。逐项前插等于把整个 PATH 倒过来,同样会选错解释器。
- 分隔符交给 std::env::split_paths / join_paths,不能手写 `:`。Windows 用 `;`,
而且盘符自带冒号,手写会把 `C:\Windows` 切成两半、整条 PATH 报废。
shell 卡住有 5 秒硬上限,超时回落到静态目录列表。整段探测限定 unix:Windows 没有
「登录 shell 的 PATH」这个概念,装了 Git Bash 的机器 SHELL 有值,真去跑只会莫名其妙
拉起一个 bash。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* feat(codex): 无头 Codex 适配器
Less Computer 现在只认 Claude Code 和 OpenCode,因为每个后端都得手写一套适配器。
这个是 Codex 的那套,形状和前两家一致,共用同一套请求/事件/错误类型。
- 运行:`codex exec --json`,逐行解析 JSONL。
- prompt 走 stdin(末尾的 `-`),不进 argv,也不会出现在进程列表里。
- 续接用 `resume --last`。参数顺序有坑:`-s` / `--json` 这些必须排在 `resume` 之前,
写反了 CLI 直接报 unexpected argument,已对 0.146.0 核实并写进注释。
- 工具展示会剥掉 `/bin/zsh -lc '…'` 这层 shell 包装,胶囊里显示的是真正执行的命令名。
- 护栏:Codex 给不了逐命令 deny 清单(`.rules` 只从 $CODEX_HOME / 项目目录读,实测
`-c rules=[…]` 会被静默忽略)。它的护栏是自带的 seatbelt 沙箱,走 `-s <mode>`。
额外关掉 workspace-write 对 /tmp 与 $TMPDIR 的默认放行——不关的话「写入限制在工作
目录内」就是句假话,实测任务真的能在 $TMPDIR 里落文件。
有一处刻意不做:Codex 会把「模型元数据没找到」这类警告也塞成 error 事件,本机每次运行
都会发一条。把它当终局失败会让所有运行都被误判,所以只认 turn.failed。
联机验收默认 #[ignore],其中一条不调模型不花钱:它比对加固前后实际生效的可写根,
盯住护栏别被上游无声改宽(codex 对未知 -c 键是静默接受的,改名了不会报错)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* feat(coding-agent): Less Computer 可选 Codex 后端,审批卡按能力启用
把 Codex 接进语音链路,后端从两家变三家。
护栏这块两类后端不一样,代码里如实分开:Claude / OpenCode 有逐命令 deny 清单,撞了能
弹审批卡、批准后放行那一条重跑;Codex 只有粗粒度沙箱档位,唯一的「放行」办法是整体
降档,那等于把护栏关掉,不是放行这一条。
所以给 provider 加了一个能力位,只有支持逐命令放行的后端才弹审批卡。否则用户点了批准、
重跑还是同样被拦,那是个假承诺。撞墙时直接把失败如实报出去。
模型解析也按后端分开:Codex 收裸模型名(gpt-5 / o3 这种),留空交给它自己的
~/.codex/config.toml,不替用户瞎猜一个默认。
另加一个通用的安装检测命令:选了后端却没装 CLI 时,在设置页当场看到,而不是等说完
一句话才发现跑不起来。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* feat(settings): Less Computer 后端下拉加入 Codex
设置页的后端下拉从两项变三项,并且如实反映能力差异,不给假开关:
- 模型:Claude 用别名下拉,OpenCode 拉账号可用列表,Codex 改成自由文本——它的模型名
是裸名(gpt-5 / 自建网关的任意名字),枚举不过来;留空 = 用 Codex 自己的默认。
- 护栏:选了 Codex 会挂一条说明,讲清楚它只有粗粒度沙箱、撞到限制会直接报错而不是弹
「批准这条命令」的卡片。免得用户按 Claude 的心智去用。
- 装没装:当场检测并提示,没装时按警示色显示。
- 可执行文件的 placeholder 跟着后端走。
文案 5 语言齐全。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* fix(coding-agent): harden Codex execution
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-21 22:07:10 +08:00
cd48a037a4
fix(less-computer): restore OpenCode model discovery and execution ( #840 )
...
* fix(less-computer): restore OpenCode model execution
* fix(capsule): gate set_ignore_cursor_events behind #[cfg(not(mobile))]
#887 introduced desktop-only capsule cursor-passthrough logic into the
unconditionally-compiled capsule_focus module, breaking Android cargo check
(E0599: no method set_ignore_cursor_events on tauri::WebviewWindow). Gate the
block to desktop; variables it consumes (classic_style, selection_polish,
visible) are still used on mobile outside the block.
---------
Co-authored-by: sim <sim@local>
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-03 16:03:41 +08:00