Repository navigation
feat(desktop): 任务级上下文窗口档位(内核) - #4598
Conversation
[Medium impact] 本轮修改看起来可以合并,未发现新增的阻塞问题。
|
|
本 PR 命中维护者确认门(产品 / UI、规则文档、架构),已开讨论 issue,请维护者在本 PR 上 Approve 放行;需要修改请 Request Changes。 讨论 issue:#4601 |
|
@greptileai review — 请基于当前 head 上一轮 3 条意见的处置(逐条已在对应 thread 回复):
本轮其它变化: CI 首轮红的根因是 4 个手写 |
|
@codex review |
|
关于本轮 证据(该 job 日志):
本机全量 desktop 单测上还观察到两个同类上游 flake(两轮各挂 1 / 2 例,且两轮互不相同),均为上游 main 的既有测试、
这两处不属于本 PR 的改动范围,本 PR 未改动上游测试。 |
Upstream-PR: makecindy#4598 Upstream-PR-Head: 6e5c18d Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
本地构建曾把 makecindy#4598 的「任务级上下文窗口档位」迁移落成 0108_loose_puppet_master.sql; 之后该分支合入上游时,主干已在 0108 放了 bot 私聊迁移(0108_lowly_scarlet_witch.sql), 同一 schema 意图被重编号到 0110_green_unus.sql。于是「用旧包升级过、又换新包」的库会在 prepareMigrationRuntimeManifest 处以 `applied migration runtime identity changed at seq 108 (0108_loose_puppet_master.sql)` 失败关闭——app 起不来,而且重装官方版也救不回:判定依据是 userData 里的数据库与 migration-runtime manifest,不属于安装包。 修法(只认登记在册、内容指纹完全一致的重编号,其余仍失败关闭): - migrationRunner: 新增 KNOWN_RENUMBERED_APPLIED_MIGRATIONS 登记表与 resolveCanonicalAppliedIdentity;已落库旧身份能解析到 canonical 时重写 manifest, 并把它腾出的旧 seq 一并销账;「canonical 未执行」的缺条目检查只查到已落库前缀为止。 - 0108 重放守卫:重编号血统的 bot_direct_messages 缺 sender_name/recipient_name 时, 在该迁移事务内把表收敛回 0108 执行后的等价结构(列序/索引对齐,先自查无外部外键引用)。 - migrate: up-to-date 与 replay 两条启动路径都调用 reconcileRenumberedAppliedSchema, 保证「旧 checkout 血统」的 schema 收敛不会被 pending 为空短路掉。 测试:migrationCompatibility / migrationReplay 新增重编号修复与重放守卫用例; 四个引用 migrationRunner/migrate 的测试文件 39 通过,tsc 通过。 Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn> (cherry picked from commit 194299a) (cherry picked from commit d697139)
Upstream-PR: makecindy#4598 Upstream-PR-Head: 6e5c18d Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
本地构建曾把 makecindy#4598 的「任务级上下文窗口档位」迁移落成 0108_loose_puppet_master.sql; 之后该分支合入上游时,主干已在 0108 放了 bot 私聊迁移(0108_lowly_scarlet_witch.sql), 同一 schema 意图被重编号到 0110_green_unus.sql。于是「用旧包升级过、又换新包」的库会在 prepareMigrationRuntimeManifest 处以 `applied migration runtime identity changed at seq 108 (0108_loose_puppet_master.sql)` 失败关闭——app 起不来,而且重装官方版也救不回:判定依据是 userData 里的数据库与 migration-runtime manifest,不属于安装包。 修法(只认登记在册、内容指纹完全一致的重编号,其余仍失败关闭): - migrationRunner: 新增 KNOWN_RENUMBERED_APPLIED_MIGRATIONS 登记表与 resolveCanonicalAppliedIdentity;已落库旧身份能解析到 canonical 时重写 manifest, 并把它腾出的旧 seq 一并销账;「canonical 未执行」的缺条目检查只查到已落库前缀为止。 - 0108 重放守卫:重编号血统的 bot_direct_messages 缺 sender_name/recipient_name 时, 在该迁移事务内把表收敛回 0108 执行后的等价结构(列序/索引对齐,先自查无外部外键引用)。 - migrate: up-to-date 与 replay 两条启动路径都调用 reconcileRenumberedAppliedSchema, 保证「旧 checkout 血统」的 schema 收敛不会被 pending 为空短路掉。 测试:migrationCompatibility / migrationReplay 新增重编号修复与重放守卫用例; 四个引用 migrationRunner/migrate 的测试文件 39 通过,tsc 通过。 Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn> (cherry picked from commit 194299a) (cherry picked from commit d697139)
|
补充说明这次同步主干带来的迁移调整(不改功能语义,只改迁移身份与幂等形态): 1. 迁移号
|
Upstream-PR: makecindy#4598 Upstream-PR-Head: f80c550 Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
追加说明:任务窗口预算不再进数据库(
|
Upstream-PR: makecindy#4598 Upstream-PR-Head: 26dd1d3 Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
|
@greptileai review 存储位置刚从「新增会话列 + 迁移 0114」改成「main 侧偏好文件( |
Greptile P1(2026-10-24):已有待处理的模型/来源切换时,改预算走 pendingTarget 分支只补 forceSessionRebuild 就返回,没有登记重放意图。那条 pending 随后被 「重选当前模型」清掉(SET_MODEL 成功路径 agentSwitchPending.clear),预算就 再也没有应用点,任务继续用旧窗口。 - pendingTarget 合并分支补 pendingSessionContextWindowBudgetReconciles.add; - 「有效窗口没变」「已应用」两个出口清掉残留意图(否则每条消息都白跑一次对账); - 切片扩到包含 applier,新增回归用例「已有切换 → 改预算 → 重选模型 → 发送」 断言:合并不重建(不记 dedup)但意图留下(反向验证:去掉 add 立即变红)。 285 例 + tsc + hygiene 全过。 Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zqchris
left a comment
There was a problem hiding this comment.
这次补的是合并进待处理切换那条路:改预算并到已有的模型/来源切换里时,也登记重放意图,这样那条切换被「重选当前模型」清掉后,发送边界还能把预算补上。另外「已应用」「有效窗口没变」两个出口顺手清掉残留意图,避免每条消息白跑一次对账。回归用例也覆盖了。批准,等 CI 全绿。
|
@greptileai review |
Greptile P1(2026-10-24):previous 只是「上一个保存值」,不是活实例当前的 窗口。实例 300K、模型级上限 500K 时,回合中先存 500K 再存 600K——第二次两个 保存值都收敛到 500K,判定「没变」,但实例仍是 300K。此前在这个出口清掉重放 意图,随后重选模型取消 pending,预算就永久不生效。 意图改为只在三种终态清除:真正 applied、记账值等于偏好值、实例已关。 新增用例复现该两次保存序列(反向验证:加回 delete 立即变红)。 Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zqchris
left a comment
There was a problem hiding this comment.
这次去掉了「有效窗口判为没变」出口清意图的那一行:previous 只是上一个保存值、不代表活实例当前窗口,两次保存收敛到同一上限时确实会误判,清掉就会让预算永久不生效。现在意图只在真正 applied、记账值等于偏好值、实例已关这三种终态清除,留着最多是发送边界多做一次对账,代价很小。复现两次保存序列的用例也补上了。批准,等 CI 全绿。
GLM-5.3 独立对抗审查发现两条: P1:预算重放只挂在直发(IM/Goal/定时任务)边界。回合中改预算登记意图后, 用户在模型选择器里点选会清掉那条 pending 凭证切换,随后从桌面 UI 发消息走 createMakerSendTransaction → applyPendingAgentSwitch(只调 applyPendingAgentSwitchIfIdle,不重放),预算永久不生效——最普通的操作路径 反而失守。抽出 replayPendingSessionContextWindowBudgetUnderLock(锁内,调用 方持 send 锁),直发 holder 与 UI 发送事务两处共用。 P3:偏好文件不可读时 store 拒绝写,两处写入的失败语义互相矛盾—— persistSessionFields 把预算写放在行更新之前(失败带走整包字段); updateSessionInDb 放在行更新+广播之后且无兜底(IPC 报错但改名已落库)。 现在都降级为「预算未保存」的 warn:persistSessionFields 把写挪到行更新之后 并用 budgetWritten 门控 apply;updateSessionInDb 收进 try/catch。 用例:重放门控改成执行式(原先是源码字符串断言,GLM 指出挡不住回归)+ 「UI 发送事务确实重放」;新增「store 拒绝写时行更新照旧」(反向验证两条都 变红)。331 例 + tsc + hygiene 全过。 Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
|
另做了一轮独立对抗审查(不改代码逻辑,专门找并发/时序/生命周期/失败路径/假绿),报出 1 条 P1 + 2 条 P3,全部核实并处置( P1 — 预算重放只挂在直发边界,桌面 UI 发送路径没有 修法:抽出 P3 — 偏好文件不可读时的失败语义互相矛盾 P3 — 测试假绿 未采纳的一条:worker 唤醒注入仍由顺序断言覆盖。该注入函数本身有 353 行行为用例( CI 重新跑中。 |
|
@greptileai review |
zqchris
left a comment
There was a problem hiding this comment.
这次两处都成立:把发送边界的预算重放抽成 replayPendingSessionContextWindowBudgetUnderLock,桌面 UI 发送事务的 applyPendingAgentSwitch 也走一遍,补上了「回合中改预算、选择器点选清掉 pending、从界面发消息」这条最常见路径;偏好文件写失败改成在行更新之后、降级为 warn 并用 budgetWritten 门控 apply,不再连累同一个 patch 里的改名等字段。重放门控的用例改成真正执行函数,也比断言源码文本靠谱。批准。现在标签还挂着 ci-failed,请确认这轮 CI 跑完全绿再等合入。
Greptile P1(2026-10-24)指出我上一条降级改过头了:maker:set-context-window-budget 只提交预算字段,偏好文件损坏/超限时 store 拒绝写,新加的 catch 把错误吞掉 —— 预算没保存、新窗口也没应用,请求却正常返回,用户想缩小窗口省钱时仍按旧窗口跑。 按 patch 形态分流: - 纯预算 patch(除 contextWindowBudget 外无其它字段)→ 抛出,让 IPC 明确失败; - 混合 patch → 其它字段已落库,保留降级,但把未写入的 contextWindowBudget 从 broadcast 里剔除,避免 UI 显示一个没存下去的值。 persistSessionFields 与 updateSessionInDb 两条入口都按这个口径处理。 用例:新增「纯预算请求在 store 拒绝写时 rejects」,混合那条补断言广播值不被当成 已保存。216 例 + tsc + hygiene 全过。 Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zqchris
left a comment
There was a problem hiding this comment.
这次收得对:纯预算请求(maker:set-context-window-budget 就是这种)只有这一个意图,store 拒绝写时就该让 IPC 明确失败,不能「看着成功、实际还是旧窗口」;混合 patch 保留降级,persistSessionFields 里也把没存下去的 contextWindowBudget 从广播剔掉了。新用例覆盖了纯预算 rejects。
小提醒(不卡):updateSessionInDb 的混合 patch 分支广播在写偏好之前就发了,提交说明说两条入口都剔除了广播值,实际这条没剔,失败时 UI 可能短暂显示没存下的值。后面顺手对齐就好。批准,CI 跑完全绿再等合入。
该用例直接切片 register.ts 的 applyPendingAgentSwitch 适配器执行,新增的 replayPendingSessionContextWindowBudgetUnderLock 需要显式注入(no-op, 用例只关心切换守卫)。 Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
zqchris
left a comment
There was a problem hiding this comment.
这次只动了 makerSendTransaction.test.ts:切片执行 applyPendingAgentSwitch 适配器时补上 replayPendingSessionContextWindowBudgetUnderLock 的 no-op 注入,跟上一条把预算重放挂到桌面发送事务的改动对齐,用例仍只验切换守卫,没问题。请保持 CI 跑到全绿再等合入。
|
@greptileai 的 P1(纯预算请求失败被吞)已处置( 之后又补了一处: CI 现在 18/18 全绿(Linux 1-4/4、Windows runner/unit 1-4/4、database、Git integration、hygiene 等),DCO 8 commits passed。 |
|
发现 1 条:
另外,GitHub 当前 PR 元数据显示 |
Greptile P1(2026-10-24):persistSessionFields 的预算写与 apply 都在 await db.update 之后,偏好文件按「当下」账号的 userData 解析——保存期间切账号 时,这条远程镜像更新会把旧账号任务的预算写进新账号的文件,甚至对新 owner 的 活会话执行应用。updateSessionInDb 早先已加 isOwnerScopeCurrent 守卫,镜像路径 漏了。 现在写偏好与 apply 都要求 scope 仍有效(同一函数内各查一次,O(1))。 新增用例「镜像落库在 owner 变化后不写偏好」(反向验证:去掉守卫立即变红)。 Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn> # Conflicts: # apps/desktop/src/main/bootstrap-electron.ts
|
判定:成立,已修( 确认了: 修法:写偏好与 apply 都要求 scope 仍有效(同一函数内各查一次,O(1), 关于 |
|
@greptileai review |
Greptile P2(2026-10-24):未登录时 ownerScopedUserDataPath 退到
app.getPath('temp'),而 mock 只重定向了 userData → 预算偏好落到固定的
/tmp/cindy-fork-test/temp,不在 afterEach 清理范围内,同进程用例会共享同一份
文件。现在 temp 也指向每例临时目录;顺便把 mkdtemp 改成 await(原先 .then()
不等待,用例可能在目录建好之前写偏好)。fork 89 例 + tsc + hygiene 全过。
Signed-off-by: Zhang Yunjin <zhangyunjin@zju.edu.cn>
|
@greptileai review |
PR #4598 独立代码审查报告(heavy 档)
Findings无 P0/P1。 P2 备忘(不阻断、不派 thread):
已核验的既有 bot 意见落实(不重复开帖):
Rule coverage
固定问题逐条:① 单一目的、描述一致 ✔;② 共享符号( Verification均在 PR head worktree 实跑(命令退出码如实记录):
Intent alignment
结论:一致。核心目标(任务级窗口档位内核、目录上限不被越过、三引擎生效、fork/worker/远程一致口径)在最终 diff 中全部可核验;无删除/削弱/反转;讨论 issue 三项行动(语义统一、维护者审阅后放行、范围聚焦)均落实。存疑项无。 Overallpass —— 没有 P0/P1。 heavy 档审查(含恶意代码与数据外发专项、security-scan)通过;23 条历史 thread 全部已解决且修复落实;维护门四类触发(product/security/rules/arch)由维护者 zqchris 对当前 head 的 GitHub Approve 放行;CI 双源全绿、无冲突、未落后 base、未命中受保护路径。建议按仓库惯例以 merge 方式合并。 警示(非阻断):机器人静默期超时放行 —— |
feat(desktop): 任务级上下文窗口档位(内核)
---HEAD: feat/session-context-window
这次改了什么
摘要
给单个任务加「工作上下文窗口档位」的内核:任务可以选择本任务最多用多大的上下文窗口(tokens),与设置页的自动压缩阈值百分比正交——档位决定分母/容量,百分比决定触发点。
动机:分档计费模型的窗口档位直接决定价带(如 Qwen3.7 Plus ≤256K / >256K 单价差 3 倍);不分档模型把任务压到更小运行窗口可降低平均每轮成本。
关联 Issue:#3464(维护者结论:产品缺口是任务级策略与入口,
contextWindow必须继续来自目录,不能由 session 数据库改写)。本 PR 落任务级入口,且从不超过目录contextWindowMax。范围
session-context-budget-prefs.json),读写集中在session-context-budget-store.ts;不新增数据库迁移、不改 schema、不写会话行(DB 模式停在官方0113)。applied migration runtime identity changed at seq ...拒绝启动。任务级窗口是纯客户端偏好,不该换来不可逆的库状态。min(任务预算 ?? 模型级上限, 模型级上限, 目录 contextWindowMax);resolveSessionContextWindowBounds/resolveRouteContextWindowBounds;contextWindowMax原始字段不进 renderer 投影,main 解析出的有效上限随 bounds 下发(renderer 永不自行推导窗口)。compaction.reserveTokens(热切实时改 + 切后重载 fail-closed);宿主不做强制重建(引擎自己的requiresModelSwitchRebuild标出重建需求);switchModel使用同一份目标路由收敛预算(外层预演不再用引擎持有的旧预算,修「Pi 首次切模发送失败」)。applySessionContextWindowBudgetToCreateOpts。maker:set/get-context-window-budget(allowlist 单向新增);控制端经隧道让被控端写自己的偏好并应用;老被控端回CHANNEL_NOT_ALLOWED。远程「只能收紧」仅发生在拿不到被控端权威边界时(老被控端 / 拉取失败)。UI 变化
sessionService.ts的类型字段与vite-env.d.ts的 IPC 类型声明,无任何组件、样式、布局、文案改动。变更类型
feat新功能怎么验证的
自动验证
tsc --noEmit通过;内核测试:desktop 10 文件 481 例(store / bounds / budget / fork / runtimeSetModel / sessionsUpdate / wiring / ordering)+ maker-core pi 54 例 + device-link allowlist 54 例全过;check:test-hygienePASS。手工验证
reserveTokens生效、切模收敛、fork 继承、worker 唤醒预算透传、远程收紧口径(第 8 轮用户实测报障的交互问题已在 UI PR 修复)。未执行的验证
风险
风险分类
CHANNEL_NOT_ALLOWED降级)credentials-and-local-storage规则)影响与回滚
偏好文件是新增文件,回滚 = 删文件 + 回退代码;不触碰 DB、迁移链与已有 channel 语义。无预算(
null)任务行为与现状完全一致。提交前检查
docs/dev-rules/database-and-migrations.md(无迁移)、docs/dev-rules/protocol-compatibility.md(allowlist 单向新增)、docs/dev-rules/maker-core-and-agent-behavior.md(本 PR 一并更新)、docs/dev-rules/pi-harness.md(Pi 原生能力非退化)。pnpm check:dco通过。