Repository navigation
feat: 移植 PilotDeck 上游 2026-09-19 之后的增量 - #610
Merged
Merged
Conversation
同进程内两条配置写路径并存:config.js 走原子写 + 稳定读,而 gateway.js 的
saveYaml 仍是裸 writeFileSync,8 处 read-modify-write 调用点全在共享写锁之外,
进程崩溃或 IM 配置与设置页并发保存会留下截断或互相覆盖的 sati.yaml。
- gateway.js 8 处裸写迁移到 updateSatiConfig:temp + fsync + rename 原子写,
且只改 paths 指到的键——注释、行尾注释与键序保留,不再把
buildDefaultSatiConfig 的默认值物化进用户文件
- 乐观锁 CAS 贯穿到 rename 前:writeSatiConfig 把锁内比对的 revision 下传给
writeConfigAtomically,关闭「锁内校验 → 落盘」之间的 TOCTOU;此前该窗口
内的外部编辑被静默覆盖而客户端收到 200
- 写锁冲突映射为 409 + code:CONFIG_CONFLICT。此前 ConfigConflictError 落到
通用 catch 变成无 code 的裸 500,客户端按 code 走的「刷新后重放」失效
- revision 改取原始 YAML 而非掩码视图:外部只改密钥的编辑此前对乐观锁完全
不可见(掩码后两次 rev 相同)
- watcher 跟随软链目标(多 watcher),与写路径的 resolveConfigWritePath 对称;
此前只 watch 配置目录,读得到软链后的文件而写得到目标
- 缩进探测改取众数而非最小值:doc.toString({indent}) 重排整份文档,取 min 会
让一行浅缩进把全文拉平
- gateway.js 的 WeCom 配置恢复对 botId/secret 的 trim:粘贴常带尾随空格或
换行,原样落盘会让握手报不透明的鉴权错误而 UI 显示「已保存」
- satiConfigWatcher.js 里的裸 NUL 字节改用等价转义写法:Git 此前把该文件判为
二进制,整个模块的改动不可审、不可 blame、不可分块回退
- /test-connection 与 /test-web-search 补速率限制:二者会向外发真实请求,可被
反复触发出网与计费;/test-web-search 的错误回传补 redactSearchError——serpapi
与 custom 的 queryApiKey 把密钥放在 URL 上,传输层错误会原样回显给用户
arch 基线追认(详见 PR 说明):config.js 1224→1234,config.test.js 1029→1138,
satiConfig.js 854→858。
uploads.js 里硬编码 50MB / 20 文件,桌面端与 Web 端无法分别调参,前端也只能 各写一份常量。 新增 uploadLimits.js 作为唯一事实源,并暴露 GET /api/upload/limits 供前端读取。
上游把 web_search 的 provider 从 5 家扩到 9 家,Sati 只有 3 家(glm / tavily /
custom),连上游改动前就有的 serper / brave 都没有。本次补齐到 9 家。
- 新增 webSearchProviders.ts:provider 清单、默认端点与 SerpAPI 引擎的单一
事实源,YAML 校验、工具实现与设置页探测共用一份
- 新增 webSearchAdapter.ts:六家补充 provider(baidu / bocha / exa / serpapi /
serper / brave)的请求构造与响应归一化。放在 pilot/config 是边界要求——
ui/server 只能经 src/pilot/index.js barrel 访问 src,而设置页的连通性探测必须
复用同一套认证方式,否则会出现「设置页测通、实际搜索失败」
- webSearch.ts 拆出 webSearchPerformers.ts(原文件 885 行,超 800 行硬限)
- 每个 provider 都必须有自己的执行分支:serper / brave 此前没有任何分支,穿透
到 execute() 末尾的 GLM 兜底,把 SERPER_API_KEY / BRAVE_API_KEY 当 GLM token
以 Bearer 发到 api.z.ai——搜索全失败且密钥交给了无关第三方;设置页探测同样
落到 GLM 形状,对这两家永远测不通
- 守护测试逐个 provider 断言实际请求落到自己的端点,将来新增 provider 漏接
分支即红(负控制:还原该缺陷后测试精确复现 api.z.ai 这个错误端点)
- serpapi 空结果(search_metadata.status === Success)与 brave 缺 web 块都返回
空列表而非报错,与「无结果」语义一致
inputSchema 未改动(llm-replay 的请求键 digest 只投影 { name, inputSchema },
工具顶层 description 不参与),既有 fixture 无需重录。
ProjectRuntimeRegistry 创建标题生成器时传的是项目默认模型 (config.agent.model),而同一处的 createAgentConfig 用的是 context.modelRoute (M4 会话级模型路由覆盖,团队成员唤醒时传快照 modelRoute)。 后果:走路由的团队成员会话由 routed model 作答,标题却由项目默认模型生成。 - SessionTitleGenerator 的 input 增可选 model,缺省回落 agentModel,向后兼容 - resolveRoutedModel 抽到 agentSessionConfig.ts:provider 与 model 两者都非空 才采用路由模型,避免半截配置覆盖出空模型名 未引入上游的 modelOverride / modelSelection 形态——全仓零命中,照搬等于新建 第二套 per-turn 模型覆盖语义。
- 侧栏拖拽:mousedown 即置 isResizing,按下的轻微抖动也会进入拖拽态;加 3px
位移阈值,超过才判定为拖拽
- Docx 大纲面板默认展开会挤压正文,改为默认折叠
- 电子表格预览失败态此前把服务端下发的原始异常文本当正文(形如
"Error: ... at ..."),用户既读不懂也无法据此行动:
· 服务端只对有意构造的错误(带 statusCode)透出 message,未预期异常一律用
通用文案;错误码仍照常下发
· 前端按 code 映射为可操作提示;无 code 时保留 hook 的原始 message——
"Project is not available." 与客户端侧的 "Interactive workbook data is
incomplete." 都没有 code,却恰恰是唯一可行动的信息
· 失败态补「重试」出口,此前只有下载与去设置两个
- backlog 登记 TD-DESKTOP-N08(macOS 全屏 Space 下关闭-隐藏不区分全屏,P3, 待复现——不能仅凭上游存在修复就断言 Sati 有该缺陷)与 TD-DESKTOP-N09 (arm64 安装包内嵌 win32-x64 原生预编译件且无 ARM64 冒烟,P2) - metrics 与 architecture-baseline 按本次工作树刷新 - code-facts 随 src/pilot/config 新增文件回填 arch 基线本次追认 3 条 file-size 增长:config.js 1224→1234(+10,409 冲突分支 与脱敏)、config.test.js 1029→1138(+109,新增用例)、satiConfig.js 854→858 (+4,CAS 转发)。
This was referenced Oct 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
描述
Closes #609
变更内容
测试
pnpm test通过pnpm lint与pnpm format:check通过pnpm typecheck通过上游增量筛选结论
移植 OpenBMB/PilotDeck 在 Sati 上次同步边界(
cd52c9af/ PR #599,2026-09-18)之后产出的增量(141 条提交 / PR #600–#632)。上游 force-push 无法
git merge,故为手工语义移植——成本在判定不在搬运。
零改动域:
src/model、src/router、src/context、src/memory上游四目录零改动;src/gateway/protocol/version.ts未触及 ⇒ 本次不升协议版本。已覆盖或已分叉、判定不引入:cron day-fields OR(Sati 的 v2 缓存双条件保留 + 闰日
例外,强于上游)、后台任务限流(Sati 另有 TTL 回收)、插件 skill markdown、XLSX
drawings 命名空间、上传重名拒绝(Sati 用
COPYFILE_EXCL,无 TOCTOU,优于上游的lstat预检)、桌面端(已分叉:自有release.sh/build-win.bat/electron-builder.yml)、外观主题体系(白标分支品牌强耦合)。代码审查后的 8 项修复
提交前跑了一轮多角度代码审查。8 项发现全部坐实,无一是误报,均已修复并附回归测试:
serper/brave在web_search里没有执行分支,穿透到末尾的 GLM 兜底——把SERPER_API_KEY/BRAVE_API_KEY当 GLM token 以 Bearer 发到api.z.ai,搜索全失败且密钥交给无关第三方;设置页探测落到同一形状,对这两家永远测不通writeSatiConfig锁内比对了 revision 却没下传给writeConfigAtomically,CAS 只覆盖updateSatiConfig一条路径,TOCTOU 未闭合(注释却声称已闭合)expectedRevision,在 rename 前再确认一次ConfigConflictError落到通用 catch,变成无code的裸 500,客户端按code走的「刷新后重放」失效code: CONFIG_CONFLICT(照memory.js既有形状)satiConfigWatcher.js内含一个裸 NUL 字节,Git 判该文件为二进制——整模块改动不可审、不可 blame、不可分块回退detectYamlIndent取全局最小缩进,一行较浅的行会把整份文档重排code为 null 时(如"Project is not available.")真实信息被通用文案吞掉botId/secret的trim,粘贴带尾随空格的凭据会落盘,握手报不透明鉴权错而 UI 显示「已保存」/test-web-search未脱敏;serpapi 与 custom 的 queryApiKey 把密钥放在 URL 上,传输层错误会原样回显给用户redactSearchError架构基线追认
file-size棘轮本次追认(相对 main 的净变化):ui/server/routes/config.jsui/server/routes/config.test.jsui/server/services/satiConfig.jsui/src/components/app-shell/SidebarV2.tsx验证
pnpm typecheck/pnpm lint(含 11 个领域门禁)/pnpm format:check全绿fail 0;UI 全量 169 文件 / 1162 测试通过打印出错误端点
https://api.z.ai/api/paas/v4/web_search)inputSchema零改动——llm-replay 的请求键 digest 只投影{ name, inputSchema },工具顶层
description不参与,既有 fixture 无需重录非目标
上游的
memory.enabled显式化、服务端权威引用改写(会与 Sati 自有的providerRefs.ts形成双写)、per-turnmodelOverride语义、桌面端与 Linux 打包。