Skip to content

feat(desktop): 会话已加载窗口显式建模,孤岛接回主段后零成本跳转 - #1

Open
Michael-Yu2978 wants to merge 121 commits into
mainfrom
codex/featb
Open

feat(desktop): 会话已加载窗口显式建模,孤岛接回主段后零成本跳转#1
Michael-Yu2978 wants to merge 121 commits into
mainfrom
codex/featb

Conversation

@Michael-Yu2978

@Michael-Yu2978 Michael-Yu2978 commented Aug 31, 2026

Copy link
Copy Markdown
Owner

这次改了什么

摘要

feat B「会话已加载窗口显式建模」:把 makerChatStore 里「窗口是否掺过孤岛」的布尔
historyWindowHasIsland)升级为显式孤岛区间模型
historyWindowIslands: readonly LoadedWindowIsland[](每座孤岛 {oldestClientId, newestClientId}
按时间升序)。

背景(makecindy#676 review 之后与 MessageStream 锚定窗口双向有界 TODO 同一条后续改动):单个布尔
无法回答「目标落在窗口哪一段」,于是「孤岛 + 已翻到历史起点」的会话每次窗口内搜索都被迫
多发一轮 around + list 探测,窗口其实早已补成连续。显式模型下:孤岛被向上翻页真的跨过
(接回主段)时自动从模型消失;目标落在主连续段(最新尾段,最后一个孤岛最新边界行之后的
全部行)内时,搜索 / 跳转直接 focus、零网络。生产入口(CCAgentSessionView 的 searchJump
effect)与 store 侧补齐快速通道共用同一把结构尺子 isInsideMainContiguousRun,不会再出现
「生产判定与自愈判定漂移」的 makecindy#676 类问题。

变更类型

  • feat 新功能
  • fix 缺陷修复
  • refactor / perf 重构或性能优化
  • docs / test / chore 文档、测试或工程维护
  • 其他:

范围

  • 关联 Issue / 需求:5-PR 提案 feat B;来源线程分析见 searchJumpTargeting.ts 的「孤岛 + 已翻
    到历史起点」注释与 fix(desktop): 跳转历史消息不再留窗口空洞,工作组不跨空洞合并 makecindy/cindy#676 review(bce583965)
  • 本 PR 包含:
    • apps/desktop/src/renderer/lib/searchJumpTargeting.ts:新 API(LoadedWindowIsland
      mainContiguousRunStartIndexisInsideMainContiguousRuncanFocusWithoutJumpLoad
    • apps/desktop/src/renderer/lib/makerChatStore.tsSessionChatState.historyWindowIslands
      (必填)替换旧布尔;SessionChatLightState 保留派生布尔 historyWindowHasIsland;接入
      trim / demote / reload / clear / removeMessagesByClientIds / dropMessagesFromClientId
      (edit-last)/ loadOlderMessages / 初始加载两次 setState / 权威重建(reconcile)/
      around 提交(busy / covered / fallback);新增收口助手
      absorbIslandsCrossedByPagingupdateIslandsAfterAroundMerge
      pruneIslandsForTrimmedWindowconservativeIslandsForconservativeIfIslandRowRemoved
      pruneIslandsAfterNewestTruncation
    • apps/desktop/src/renderer/hooks/useCCAgentChat.ts:sessionless light 快照改用
      EMPTY_LIGHT_STATE
    • 测试:searchJumpTargeting.test.ts(重写)、makerChatStoreJumpBackfill.test.ts(T 三阶段 +
      新增 T2 / T3 / Y 语义更新)、makerChatStoreActiveView / makerQueueState /
      reconcileRemoteMessages(断言迁移)
  • 明确不包含:MessageStream 锚定窗口双向有界 TODO 本身、其它 5-PR 提案项
  • 用户可见变化:窗口内搜索 / 引用跳转在「孤岛已补齐」后不再白打探测,行为更顺;无视觉变化
  • 是否存在 breaking change:无(historyWindowIslands 是 store 内部模型字段,未跨仓消费;
    对外 light 布尔保留)

UI 变化

  • 引用的设计规范:不涉及:无组件 / 布局 / 样式 / 文案 / 动效改动,命中的是 store 状态模型
    与搜索跳转判定逻辑;UI 消费方(CCAgentSessionView / MessageStream)仍读派生布尔
    historyWindowHasIsland,接口不变。

怎么验证的

自动验证

corepack pnpm --filter desktop run typecheck
结果:PASS

corepack pnpm test:unit:related
结果:PASS(test:runner 445 项 0 失败 + apps/desktop related 全绿)

corepack pnpm test:unit(全量 unit tier)
结果:除 8 项 Windows 上既有的 POSIX 平台用例失败外全部 PASS。失败项全部在
src/main/__tests__/codexAuthLink.test.ts(6 项)与
src/main/maker-host/__tests__/codexAuthInvalidation.test.ts(2 项)——模拟
'darwin'/'linux' 平台的 symlink / hardlink 行为,Windows 文件系统返回 link-unsupported。
已对照验证:把本次改动全部 stash 后在干净树重跑同一批文件,失败数与名单完全一致
(8 failed | 68 passed),确认是既有环境问题、与 feat B 无关;其余 workspace(含
apps/desktop renderer 全部单测、maker-core 等)全绿。本地因全局 pnpm 版本与仓库
engines 不匹配,等价执行方式为 corepack 解析的 pnpm 10.33.2 逐段运行
(test:runner + test-workspaces.mjs --tier unit);最终以 CI 为准。

相关单测覆盖(全部通过):

  • searchJumpTargeting:主段内直跳 / 孤岛上交回 store / 多孤岛间洞 / 边界行缺失保守全 false
  • jumpBackfill T:孤岛重跳仍补齐;T2:两孤岛序列只收口被跨过的那座;T3:翻页填洞后同目标
    零分页请求;Y:整座被裁掉的孤岛随洞消失、保留尾段恢复主段
  • reconcile / activeView / queueState:断言迁移到区间模型后全绿

手工验证

未执行实机 GUI / device-link 验证:搜索跳转链路需真实双端联调,本机只覆盖到 store 与纯函数
单测;canFocusWithoutJumpLoad 的判定与 store 快速通道共用同一把尺子,单测已直接覆盖生产入口
(CCAgentSessionView 的 searchJump effect 调用的就是该纯函数)。

未执行的验证

  • 实机 / 真机 GUI 目检(搜索跳转、翻页、device-link 隧道下的窗口收口)
  • absorbIslandsCrossedByPaging 的「row id 游标」分支:测试里 id === clientId,未直接命中
    生产形态的分叉(代码为同一匹配函数,审查级确认)

风险

风险分类

  • 无已知风险
  • SQLite / migration
  • system prompt
  • 协议兼容
  • 权限 / 安全 / 用户数据
  • 存量插件兼容(批准状态 / 指纹 / manifest 校验 / 安装布局 / 包格式)
  • 原生层 / fingerprint / OTA
  • 跨平台差异
  • 其他:孤岛判定的保守性权衡(见下)

影响与回滚

  • 影响范围:desktop renderer 的窗口状态模型与搜索跳转判定;UI 消费接口(light 布尔)不变。
  • 回滚 / 降级方式:revert 本 PR 即可恢复 boolean 模型;无数据 / 兼容 / 协议风险。
    不涉及 SQLite / system prompt / 协议 / 原生层 / 存量插件 / 冷更。

行为变更与边界(如实记录)

  1. 孤岛逐座收口:covered 只证明「尾部 → 本次目标」连续,不证明更早孤岛被跨过;只有翻页
    真的跨过(行落进本批拉取范围)才从模型移除(T2 两孤岛序列守这条)。
  2. around 块连主段判定改在合并前窗口:修复「无孤岛窗口 + 首座失败跳转孤岛」被合并后主段
    起点塌缩吸收、孤岛永远记不上的 bug(T / Y3 回归);判据是「块里至少有一行本来就躺在
    合并前主段里」。
  3. 裁剪精确收口slice(-TRIM_TARGET) 后,整座被裁掉的孤岛随洞消失(Y 用例语义更新),
    仍夹在保留窗口里的孤岛保留(Y3 覆盖)——比旧 boolean「一律保留」更精确且不丢不变量。
  4. busy 路径:around 带回新行且落在主段更老一侧时记孤岛(旧代码会被主段起点塌缩
    跳过);一行都没加进来时不记(Z「零新增」语义不变)。
  5. 保守兜底:孤岛边界行不在窗口里(模型被破坏)时,主段置空、任何目标都走补齐尝试;
    hasMore=false 不再作为短路依据(fix(desktop): 跳转历史消息不再留窗口空洞,工作组不跨空洞合并 makecindy/cindy#676 codex P1 的「孤岛 + 已翻到历史起点」仍可被 around
    捞回缺的邻居)。
  6. 游标匹配absorbIslandsCrossedByPaging 的边界按 clientId || id 匹配——生产分页
    游标是 DB row id,只按 clientId 会让边界永远落空、孤岛不收口。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s,见 DCO)——3 个 commit 均已签名,pnpm check:dcoorigin/main..HEAD 全部通过
  • UI 改动已在「UI 变化」注明引用的设计规范章节(不涉及 UI,已注明理由)
  • 未提交凭证、令牌或授权文件
  • 已补充必要文档(本 PR 说明 + 代码内行为注释)
  • 已确认测试结果或说明未执行原因

DavidShenXD and others added 29 commits September 2, 2026 14:02
Signed-off-by: DavidShen <david@xd.com>
Signed-off-by: DavidShen <david@xd.com>
Signed-off-by: DavidShen <david@xd.com>
perf(mobile): 优化长任务流式渲染与首页订阅
…y#3767)

* fix(maker-core): yield marker 检测收紧为执行器状态头形状(makecindy#3763)

makecindy#3315 的启发式对 exec 输出全文做无锚点扫描,stdout 里被引用/示例的
「Script running with cell ID N」文案(grep 前缀行、源码缩进引号、字面
\n 转义)也会铸造 continuation claim —— 已成功的命令被注入续段等待
不存在的 cell,升级成 yield-continuation-lost-handle 终态失败,一次
读取源码能炸出多个假 cell。

真实 yield 通知是执行器状态头:marker 从物理行首开始、紧跟 Wall time
帧(同行空白分隔或下一行)。正则按此收紧;引用形态均不满足。残余
盲区(纯文本在列 0 完整复刻两行状态头)与真实输出不可区分,接受并
在注释中记录。集成夹具补齐真实帧;agentMessage 引用反例保持裸样。

Signed-off-by: ficowang <fico@xd.com>

* fix(maker-core): yield 状态头识别兼容 CRLF 分行(review P2)

Windows 路径下执行器状态头可能以 CRLF 分行,原换行分支不消费 \r 会漏判
真实 yield(假完成回归)。行首锚点与 Wall time 帧的换行分支均改为 \r?\n;
引用形态(字面 \r\n 转义、缩进引号、grep 前缀)仍不满足锚点,不受影响。

Signed-off-by: ficowang <fico@xd.com>

---------

Signed-off-by: ficowang <fico@xd.com>
…indy#3768)

* fix(maker-core): 兜底模型映射链不改写命名空间自定义 Provider id(makecindy#3764)

目录窗口未知时的 legacyToSdkModelString 用 includes('sonnet') 含糊匹配
追加 [1m]:自定义 Anthropic 兼容 Provider 的 cindy/claude-sonnet-5 被改写
成 …[1m],而 cindy/claude-opus-5 透传 —— 同一 Provider 两个模型的 wire id
形态不对称,上游按白名单逐一 403,且 UI 显示不变、改写不可见(官方
Claude Code CLI 对同上游逐字发送、两个模型均可用)。

含命名空间前缀(provider/…)的 id 是自定义/网关 Provider 的路由键,不属
于兜底链的官方裸 id 知识范围:除既有显式命名空间条目(codex/*、
deepseek/*、z-ai/glm-5.2)外一律逐字透传。裸 id 兜底链与窗口已知路径
(toSdkModelString 按目录窗口决定 [1m])行为不变。

Signed-off-by: ficowang <fico@xd.com>

* ci: 重触发 client-ci

Signed-off-by: ficowang <fico@xd.com>

---------

Signed-off-by: ficowang <fico@xd.com>
Signed-off-by: Stella-xixi <2559689615@qq.com>
* test(search): 锁定被控端按时间筛选

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* test(search): 用 SQLite fixture 验证时间筛选

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Renderer loadLoginState 在 getLoginState 返回 null、抛错或 30 秒未 settle 时落到既有 error 步,避免「正在连接登录服务」一直转圈。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
…ndy#3799)

* fix(maker-core): bwrap 沙箱初始化失败的 exec 结果追加宿主归因(makecindy#3793)

受限 Linux 宿主(容器/禁用非特权 userns/缺 CAP_NET_ADMIN)上 codex 的
bubblewrap 沙箱在配置 namespace/loopback 阶段即死,用户命令从未执行,
但 exec 失败结果只透传裸 bwrap stderr —— 模型对同一命令盲目重试,用户
只看到不可理解的内部错误。

判定形状:failed exec 且输出全部非空行都是 'bwrap: ' 前缀(命令一旦真正
执行,bwrap 即静默,任何命令输出都会破坏该形状,不误伤普通失败)。检出
后在 tool_result 末尾追加 [Cindy] 归因标注:命令未执行、重试必然同败、
引导用户显式切 Full access 或换宿主。恢复入口 UI(重试/切换按钮)属
renderer,另行跟进;沙箱本体修复属 codex 上游。

Signed-off-by: ficowang <fico@xd.com>

* fix(maker-core): 用户自调 bwrap 的失败不归因外层沙箱(review P1)

命令本身调用 bwrap 时,'bwrap: ' 诊断行来自用户命令内层(参数错误/嵌套
沙箱被拒),归因为 Codex 外层沙箱初始化失败并建议切 Full access 是误导。
按命令词法(词界匹配 bwrap 调用,不误伤 'ls bwrap.log' 类数据引用)跳过
标注;外层沙箱真的起不来时任意命令都会中招,下一条非 bwrap 命令仍会得到
标注,漏报代价可忽略。

Signed-off-by: ficowang <fico@xd.com>

---------

Signed-off-by: ficowang <fico@xd.com>
…kecindy#3801)

Signed-off-by: Dash <dashhuang@gmail.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(desktop): 优化登录成功回调页自动关闭

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* docs(desktop): 同步登录回调成功态设计契约

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(desktop): clarify Codex auth restart recovery

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): neutralize remote Codex retirement copy

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): keep Codex retirement copy neutral

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Lizi <jiali@magiclizi.com>
…y#3213)

* fix(remote-ssh): align OpenSSH discovery and isolate Cindy writes

Signed-off-by: Tuccuay <i@tuccuay.com>

* fix(remote-ssh): harden SSH config authentication

Signed-off-by: Tuccuay <i@tuccuay.com>

* fix(remote-ssh): expand HostName tokens

Signed-off-by: Tuccuay <i@tuccuay.com>

* fix(remote-ssh): redact MCP credential paths

Signed-off-by: Tuccuay <i@tuccuay.com>

* fix(remote-ssh): harden config discovery errors

Signed-off-by: Tuccuay <i@tuccuay.com>

* fix(remote-ssh): preserve concurrent config edits

Signed-off-by: Tuccuay <i@tuccuay.com>

* fix(remote-ssh): fail closed for unsupported Match config

Signed-off-by: Tuccuay <i@tuccuay.com>

* fix(remote-ssh): revalidate add ownership

Signed-off-by: Tuccuay <i@tuccuay.com>

* fix(remote-ssh): fail closed on conditional includes

Signed-off-by: Tuccuay <i@tuccuay.com>

* fix(remote-ssh): protect managed config and proxy routes

Signed-off-by: Tuccuay <i@tuccuay.com>

* fix(remote-ssh): harden concurrent auth handling

Signed-off-by: Tuccuay <i@tuccuay.com>

* fix(remote-ssh): fail closed on include and concurrent writes

Signed-off-by: Tuccuay <i@tuccuay.com>

---------

Signed-off-by: Tuccuay <i@tuccuay.com>
* fix(desktop): 投影 XD 网关向量模型能力

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(model-access): accept and gate XD embedding snapshots

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): clear embedding on authoritative empty snapshots

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): preserve embedding denial across refresh failures

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): close embedding fallback at account boundaries

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* docs(library): PR0 会话级只读 extraDirs 契约

把 G2 的会话级只读 extraDirs 注入、blob 相对键、限额与 confirmed=writeCommit ACK
写进权威 library 规则与冻结稿。仓内无 libraryConfirmed.ts,不得发明该文件。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): libraryVault 读路径 O_NOFOLLOW + identity 复核

read() 打开后用 bigint dev/ino 同 fd 复核,换链/ino=0 走 INTERNAL 且不返回字节;
打开器保持私有,失败不回落裸 open。writeCommit ACK 形状不变。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): library 正本 blob 消费口 + open/status 握手

editImage 只认 assets/<2>/<hash>/blob.<ext>,sidecar 禁止当像素;
writeCommit ACK 仍用 sha256 升格 confirmed;open/status 回 authorizedReadonly
与 generation/identity,不回绝对路径。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* refactor(desktop): 复用 library blob 门,去掉重复 sidecar 判断

editImage 解析后的绝对路径走 assertLibraryEditImageSource;
通道门只认 helper,不再平行正则。握手测试直接打 open/status。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): library: 前缀 sidecar 不再绕过源图形状门

hashes 先剥 library: 再判 blob/sidecar,前缀加 preview.webp/meta.json
不得进 resolveOwnedMedia。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* feat(desktop): 抽出 applyDirectoryGrants 并静默注入 library extraDirs

IPC 与 library 槽共用内部入口;writableDirs 仍走 picker。
library 槽不占用户 10 名额,迁根/卸载传 null 即撤,恢复时剥前缀。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* refactor(desktop): 收平 extraDirs 冲突检测的 IIFE

library 槽剥前缀后再走 excludeDirectoryGrantConflicts,写法改为
if/else,行为不变。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(maker-core): Claude extraDirs 代际不一致时 resume+fork 重建

setExtraDirs 只改 closure;下一次 send 复用 rewind 三件套,
不用 fresh:true。拷贝兜底默认关闭。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(maker-core): Codex 低版本 setExtraDirs 忽略以免假授权

启动仍闸 0.144.6;运行中低版本忽略 extraDirs,
读像素继续走 cindy-media 备胎。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* feat(desktop): extraDirs library 槽显示系统项且不占 10 名额

cindy-library: 前缀与用户目录分区,计数豁免 EXTRA_DIRS_MAX。
Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(maker-core): Pi extraDirs prompt 不再写入绝对库根

只描述只读作品库能力并用 basename 标签,不含 /Users/.../libraries/。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): validateExtraDirs 豁免 library 槽 10 名额上限

cindy-library: 前缀不计入 EXTRA_DIRS_MAX;满 10 个用户目录时槽仍通过。
Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): ChatInput 配额不计 library 系统槽

composer 加目录入口与 + 计数改走 countUserExtraDirs,列表把 cindy-library: 显示为系统项且不可移除。顺手把 applyDirectoryGrants 抽出后的源码扫描锚点改到 export function。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* docs(design-previews): library extraDirs 配额系统槽 QA demo

ChatInput 不计 cindy-library: 名额、列表显示「Mivo 作品库(只读)」的可交互验收件。
verify A-D 绿,未采像素基准。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): 把 library extraDirs 接到当前 Mivo 会话

open/status 与迁根、停用、卸载后调用 applyLibraryReadonlyExtraDir。
有焦点只注入当前会话;撤槽扫可见活跃会话。Codex 旧版 setExtraDirs
不再静默忽略,避免库里已授权、运行时读不到。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): 停用/删除成功后再撤 library extraDirs

停用改到 setEnabled 成功之后刷新,避免仍读到 enabled=true 又把槽注回去。
删除改到 trash 成功之后刷新。切走焦点时也撤槽。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): 切会话撤旧 library 槽,空闲会话也能落库

焦点同步对可见会话逐条写:当前焦点注入,其余撤槽。无 live handle
时仍 persist extraDirs。迁根失败也会 refresh 把旧根加回。懒恢复剥掉
cindy-library: 前缀再交给 harness。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): library extraDirs 用 realpath 槽并钉验收测试

注入走 cindy-library:realpath,切焦点把未聚焦会话的槽撤掉。
Claude 中途授权 resume+fork 续聊,不把 session id 当 resumeSessionAt,也不用 fresh:true。
单测钉死注入/撤权/10 名额豁免/代际换根/低版本 Codex 不持久化/握手无绝对路径。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): library extraDirs 同步只认最新一轮

焦点切换时旧一轮 sync 不得覆盖新一轮授权。Orca idle worker 唤醒时把 cindy-library: 逻辑槽剥成真实路径再交给 harness。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* test(desktop): Orca resume extraDirs 断言跟 extraDirsForRuntime

idle worker 唤醒已剥 cindy-library: 前缀,静态扫描测试跟着改。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): library extraDirs 每会话写入后再核代次

旧一轮 sync 在某个 session 的 apply 返回后不得继续写后续会话。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): library extraDirs 失败不再假装授权

握手 authorizedReadonly 只在 focused 本地会话 extraDirs 真正写入后为 true。
Codex 旧版本、远端会话、槽竞态与 writable 重叠都 fail-closed,走 cindy-media 备胎。
SET_EXTRA_DIRS 在 session 锁内合并 library 槽,不再锁外预读。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(maker-core): rewind 重建不再二次关闭旧 Query

commitRewindFiles 已经 close 过旧 q。send 再建时只给 extraDirs 中途授权补 close,避免 abort rewind 测试里 close spy 被调两次。

Signed-off-by: PraiseZhu <zhuzan@xd.com>

---------

Signed-off-by: PraiseZhu <zhuzan@xd.com>
Signed-off-by: Lizi <jiali@magiclizi.com>
Signed-off-by: Lizi <jiali@magiclizi.com>
* chore(desktop): 删除死代码 effortMapping.ts

apps/desktop/src/renderer/lib/effortMapping.ts 全仓零消费方(EFFORT_TO_BUDGET_TOKENS / effortToSdkExtendedThinking 无任何 import),且其 budget_tokens 形态假设与真实 CC effort 链路(effort → effortLevel 字符串)相悖,删除以避免误导后续接入者。无行为变化。

Signed-off-by: Michael-Yu2978 <ymt2978@163.com>

* fix(desktop): 解耦 Windows 实例屏障 helper 启动宽限与互斥等待预算

waitForFirstLine 的时限原为 timeoutMs + 1_000,busy 路径(timeoutMs=50)只给 PowerShell helper 约 1s 冷启动+Add-Type 编译时间,Windows CI runner 负载高时必现 'timed out acquiring Windows packaged-instance barrier'。改为 Math.max(timeoutMs + 1_000, 10s):helper 的互斥等待仍由脚本内 WaitOne(timeoutMs) 约束,仅放宽启动报告时限,fail-closed 语义不变。修复 client-ci 两分片聚合失败。

Signed-off-by: Michael-Yu2978 <ymt2978@163.com>

---------

Signed-off-by: Michael-Yu2978 <ymt2978@163.com>
…#3803)

* fix(desktop): claude-code / codex 保留本地更新版本,不再被 manifest 降级

parseBinaryVersionOutput 只认 pi 的两种输出形态(裸版本号、`pi <version>`)。
claude-code 打印 `2.1.258 (Claude Code)`、codex 打印 `codex-cli 0.145.0`,
两者都落在解析范围外,probe 恒返回 null,所以这两个 vendor 没法开
preserveLocalVersion —— userData/<installSubdir>/ 下已有的更新版本会被
manifest 指定的旧版本盖过去,CDN manifest 落后或回滚时用户被静默降级。

改动:
- parseBinaryVersionOutput 增加「版本号位于行首」与 codex-cli 前缀两种形态;
  未知前缀仍返回 null,不做全行 semver 扫描,保持原有严格度
- CONFIG 给 claude-code 与 codex 打开 preserveLocalVersion

Signed-off-by: luojiajun <luo.jj@trip.com>

* test(desktop): 覆盖 claude-code / codex 的 --version 输出形态

parseBinaryVersionOutput 新支持的两种真实输出之前没有正例断言:
claude-code 打印 `2.1.258 (Claude Code)`(版本号在行首、后缀是产品标签),
codex 打印 `codex-cli 0.145.0`(走 NAMED_VERSION_PREFIXES 白名单)。

补上这两条正例,并把「三 token 的白名单输出」与「版本号不在行首」
两种边界加进拒绝用例,锁住未知前缀不做全行 semver 扫描的严格度。

Signed-off-by: luojiajun <luo.jj@trip.com>

---------

Signed-off-by: luojiajun <luo.jj@trip.com>
Co-authored-by: luojiajun <luo.jj@trip.com>
…nId (makecindy#2882) (makecindy#3818)

会话中途 start_team 后,active-Orca rehydrate 分支直接 close+bootstrap,而
caller/队列的 createOpts 快照常不带 resumeSessionId(或带陈旧值),导致新
原生会话没有旧 transcript(Pi 表现为丢失全部对话历史,模型只看到最新追问)。

修复:rehydrate 在关闭旧 runtime 之前调用既有的 reconcileCreateOptsWithDb
(与 lazy-create 同一条 DB 权威口径),补齐/校正 resumeSessionId 等执行字段;
DB 读失败时 fail-closed 走 REHYDRATE_FAILED,旧 runtime 不被关闭。

新增回归:①对账先于 closeSession 且 DB 权威值胜过 caller 陈旧 resume,
bootstrap 收到旧 sdkSessionId;②对账抛错时不关旧 runtime、不 bootstrap。

Signed-off-by: ficowang <fico@xd.com>
* perf(desktop): 降低 Windows 语音输入启动与提交延迟

- Windows 已授权后跳过重复的临时麦克风探测流;权限运行期被撤销时立即失效缓存
- 系统静音期间缓存开头 PCM,静音完成后补发,不再丢首字
- BYOK ASR 支持 1.5s 错峰竞速与 4.5s 快速失败,输家连接显式回收;托管模式保持串行
- 会话分配加代次号,晚到的托管会话不再覆盖新的 fallback 候选
- FallbackAsrProvider 补发候选胜出前的 connected 事件,asr_connected 打点不再为空
- 新增会话分配 / socket 握手 / flush 等待的分段耗时日志与脱敏的 latency summary
- voice-input:start 里 refiner 链与 ASR 链解析改为并行
- 修复反复语音输入导致的 WebContents destroyed 监听器累积
- 修复每次录音后全局快捷键按对象引用重复同步、反复报 registration failed
- Tip 声明受控意图后固定走受控路径,消除提交时 Radix Tooltip 受控切换警告

Signed-off-by: Dash <dashhuang@gmail.com>

* fix(desktop): 语音输入静音前音频不再补发,撤权时当前尝试直接进入权限恢复

- 回退「系统静音期间缓存开头 PCM 并补发」:这段音频含静音生效前的系统播放
  内容,恢复基线的丢弃门控(inline 与 global overlay 两处对称)。
- captureSession 在 getUserMedia 报 NotAllowedError/SecurityError 时返回
  permissionDenied,useVoiceInput 走 onMicrophonePermissionRequired、overlay
  走 microphone 权限提示,不再让用户先看到原始错误再重试一次。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 去掉 BYOK ASR 启动的 4.5s 外层超时,保留各 provider 自身连接期限

外层计时从 provider.start() 一开始就走,覆盖了凭证读取与代理解析,而各 provider
的 5s 连接超时只从 socket 拨号开始算;单 provider 的 BYOK 配置下,一次前置耗时
1s、拨号 4s 的正常连接会被外层误杀且没有 fallback 可退。删除 startTimeoutMs 与
startWithTimeout,慢候选只由错峰竞速推进。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <dashhuang@gmail.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* feat(design-system): DS-3 最小语义 Token 影子层

在 packages/design-tokens 建立 reference → semantic 两层 DTCG 影子字典。
数据源仅为 DS-2b 冻结快照,零运行时接线;产品代码不改。

Signed-off-by: kirozeng <kirozeng@gmail.com>

* fix-run ds3-r1b wave1 squash

group g1 tip 793c462b68839fc857844d55b4e9d37031153c6b

Signed-off-by: kirozeng <kirozeng@gmail.com>

* fix(design-tokens): HSL 色值改用标准 DTCG 颜色对象,修复 Windows CRLF 假红

review 指出 12 个 HSL triplet reference 节点是自造的 $type: "other"——
它不是标准 DTCG token 类型,且校验器此前显式放行,测试全绿也发现不了。
对治理合同锁定的 Terrazzo 2.7.1 实测:other 类型 token 被 CSS 插件静默
丢弃(不产出任何 CSS 变量),alias 到它同样无输出;裸 triplet 字符串在
$type: color 下被解析成黑色。DS-8 接线时影子层无法直接承担生成切换。

- dtcg.ts 新增 toDtcgColorObject / dtcgColorObjectToString:HSL triplet →
  {colorSpace:"hsl",components:[h,s,l]};hex/rgba/transparent → srgb 分量
  (0-1) + 可选 alpha。$type 收紧为仅 color。
- build-layers.ts 删除 dtcgTypeFor 启发式,reference/semantic 全部节点
  生成标准颜色对象;重生成两层 JSON(12 个 other reference + 14 个
  other semantic alias 全部消除)。
- guards.ts:$type 校验不再放行 other;reference 只许颜色对象字面量,
  semantic 只许 alias;逐值一致守卫改在颜色对象层比较(hex 大小写不构成
  色值差异)。
- .gitattributes 给 packages/design-tokens/src/**/*.json 钉 eol=lf(drizzle
  migration .sql 同款先例):Windows autocrlf 检出把生成物转 CRLF 导致
  「磁盘 = 内存生成」字节一致守卫假红(本轮 CI Windows unit tests 红),
  并补 git check-attr 回归测试钉住该契约。
- value-consistency.test.ts 补往返还原与 other 拒收的回归测试。

验证:pnpm --filter @cindy/design-tokens typecheck / vitest 19 测试全绿;
pnpm test:unit:related 全量绿;生成器两次运行字节一致;最终生成物过
Terrazzo 2.7.1 tz build 零 lint 错误,136 token 全部产出 CSS 变量。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 零接线守卫按文件位置解析相对 import,校验器检查分量范围

Codex 新一轮 review 两条 P1 的跟进:

① 相对路径接线漏检(成立):兄弟 workspace(packages/foo/src/a.ts)写
`../../design-tokens/src/…` 时说明符不含 packages/ 段,旧正则
`packages\/design-tokens\/(?:src|dist|build)\//` 漏检,零运行时接线守卫
会放行真实消费者。修法:新增 relativeSpecifierHitsDesignTokens——提取
from/import()/require()/import.meta.resolve() 语境下的相对说明符,按被
扫描文件的 repo 相对位置 resolve,落进 packages/design-tokens/ 即命中;
任意 ../ 深度、兄弟包与 apps 侧路径全覆盖。findRuntimeImportsOfDesignTokens
双通道检测,并补正反两组自证伪用例(全仓扫描零误报保持)。

② HSL 百分比分量应为 0–1(不成立):DTCG Color 模块 Color Space 表原文
是 hsl hue [0,360) / saturation / lightness [0,100]——s/l 用 0–100 不是
0–1,规范自己的白色示例就是 components: ["none", 0, 100]。Terrazzo 2.7.1
实测:[60, 12.5, 97] 产出 hsl(60 12.5% 97%)(正确),0–1 写法产出
hsl(60 0.125% 0.97%)(几乎全黑)。生成物不改。采纳意见的合理内核:
校验器现在按色彩空间检查分量合法范围(srgb 通道 [0,1]、hsl hue [0,360)
360 非法、s/l [0,100]、alpha [0,1]),把正确范围锁进守卫防止反向犯错,
并补越界反证用例。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;22 个单测全绿
(新增 3 个);生成物未变(本轮零改动 rebase 在 77db609 之上)。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 相对说明符提取补副作用 import 语境

Codex review P2(3913379862)跟进:兄弟 workspace 写
`import '../../design-tokens/src/generate.ts'`(副作用导入,无 from 子句
无括号)时,SPECIFIER_CONTEXT_RE 四种语境都命不中,相对路径解析通道
漏检,零接线守卫会放行。

- 补 `import '…'` 分支:用 `(?=['"])` 前瞻定位引号(分支自身不吞引号,
  引号由共享后继统一消费,`\s*` 覆盖换行形态),实测单行 / 换行 / 双引号
  形态全部命中。
- 顺带修既有误报:import() / require() 分支此前无 `(?<![\w$.])` 守卫,
  `foo.import('…')` / `foo.require('…')` 成员调用会被当 import 语句命中
  (上一轮就存在,本轮一起收口);副作用 import 分支同款守卫。
- 反证用例:副作用 import 两种形态命中;foo.import / foo.require 成员
  调用不误报;全仓扫描仍零命中。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;22 单测全绿
(zero-wiring 新增 4 个断言形态)。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 零接线守卫剥离注释与字符串里的伪导入

Codex review P1(3913459489)跟进:守卫把源码全文直接喂正则,注释或错误
消息里出现完整 import 语句(如 `// import '@cindy/design-tokens';`)会被
当真实接线,required unit workspace 假红,无运行时接线的改动被 CI 阻断。
实测确认属实(此前『非导入语境不误报』用例能过只是因为缺完整 import
语法)。

- 新增 stripCommentsAndDataStrings:轻量状态机剥除行注释 / 块注释(替换
  为等长空白,行列结构不变);字符串字面量按语境二分——开引号前以
  from/import(/require(/import.meta.resolve(/import 语境结尾的是模块
  说明符,保留原文;否则是数据字符串,内容替换为空白(引号保留)。
  语境判定只看输出末尾 64 字符,避免全文件正则回溯。
- containsRuntimeImportOfDesignTokens / relativeSpecifierHitsDesignTokens
  改两段式:先跑廉价正则,零命中(绝大多数文件)不付剥除成本;命中后
  剥除复核,命中若来自注释/文案则判非真实接线。全仓扫描 3.8s,超时余量
  提到 60s(Windows 慢盘)。
- 正则字面量不识别:只在『漏放』方向偏差(正则内容被清空不命中),真实
  import 语句不会写在正则里,可接受——如实注释声明。
- 反证用例:注释(单行/块/JSDoc)/ 数据字符串 / 报错文案里的完整 import
  语句全部不误报(两个通道都验);剥除后真实 import 全形态(含前面有
  注释或数据字符串的)仍然命中;stripper 语义四组直接断言。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;25 单测全绿
(zero-wiring 新增 3 个用例 17 个断言形态);全仓扫描仍零命中。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 零接线守卫剥除顺序前置,修复注释隔断的合法导入漏检

Codex review P2(3913603623)跟进:`import(/* webpackChunkName: "tokens" */
'../../design-tokens/src/snapshot.ts')` 这类说明符前带合法注释的导入,两个
检测通道都漏检。根因是上一轮两段式的顺序缺陷:廉价正则预扫跑在原始文本
上,注释隔断了关键字→引号衔接,预扫零命中提前返回,剥除层(本可剥掉注释
恢复衔接)反而执行不到——防误报的层顺序自己制造了漏放。

- 剥除前置:containsRuntimeImportOfDesignTokens 与
  relativeSpecifierHitsDesignTokens 都改为先剥注释/数据字符串、再跑正则。
  注释的两个方向(伪命中 / 遮蔽真命中)都由剥除层统一解决。
- 性能:stripCommentsAndDataStrings 重写为 chunked 状态机——普通文本按
  区间整段拷贝(不再逐字符拼接),语境判定从后往前拼接 chunk 凑 64 字符
  窗口;7.7MB 真实包源码 59ms(125MB/s,较逐字符版 5.9MB/s 提速 21 倍)。
  另加 import/require/from 关键字 includes 预检,无语法痕迹的文件零成本。
  全仓扫描 1.24s(较上轮 3.8s 基线更快)。
- 反证用例:动态 import / require / 副作用 import 带注释的相对路径走
  rel 通道、包 id 带注释走 id 通道,各自命中;注释/数据字符串误报形态
  回归保持全绿。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;26 单测全绿;
全仓扫描仍零命中零误报。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 导入语境判定去长度窗口,member-require 不再被后顾排除

Codex review 两条 P1 跟进:

① 固定 64 字符窗口判语境(3913687085,成立):>64 字符的块注释剥成
空白后把 import( 挤出 tail 窗口,说明符被当数据字符串清空,两通道漏放。
修法采纳「状态机直接保留导入语境」:维护 lastCodeContext(无空白压缩的
最近输出,注释与空白不推进),判定时 trimEnd 后匹配——任意长度注释
都不再影响语境,无长度上限。实测 100/300 字符注释形态全命中。

② module.require 被负向后顾排除(3913687096,成立):`module.require()`
是 Node 真实模块加载 API,`(?<![\\w$.])` 却因 . 前缀拒绝匹配。修法:
后顾放宽为 `(?<![\\w$])`(只排除 myImport 标识符连写),. 前缀放行;
foo.import( 这类自定义成员调用同被命中——宁误报不漏放,守卫拦截的是
「有人真接线」,member-require 恰是合法接线形态。IMPORT_ENTRY_PATTERNS
与 SPECIFIER_CONTEXT_RE 两处同步。

行为变更如实声明:foo.import('…') / foo.require('…') 从「不误报白名单」
移入「命中集」——上一轮把它们列为不误报用例,本轮按威胁模型反转并在
测试里注明理由;标识符连写(myImport)仍排除。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;28 单测全绿
(新增 2 个 P1 用例);全仓扫描 1.9s 零命中零误报。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 零接线扫描纳入 require.resolve 加载入口

Codex review P2(3913799524)跟进:产品包通过
`readFileSync(require.resolve('../../design-tokens/src/semantic/color.json'), 'utf8')`
消费影子层时是真实运行时消费者,但剥离层把 require.resolve 的参数当普通
数据字符串清空,包 id 与相对路径两个通道都返回 false——漏放。实测确认
(含 module.require.resolve 成员链)。

- SPECIFIER_CONTEXT_RE / SPECIFIER_PREFIX_RE 补 require.resolve 语境
  (`require\s*\.\s*resolve\s*\(` 容忍空格形态);IMPORT_ENTRY_PATTERNS
  补 require.resolve( 模块 id 形态。
- 反证用例:readFileSync(require.resolve(…))(包 id / 相对路径)、裸
  require.resolve、module.require.resolve 成员链全部命中;注释/数据字符串/
  myResolve 标识符回归不误报。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;29 单测全绿;
全仓扫描 1.9s 零命中零误报。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 零接线扫描纳入模板字面量形式的模块说明符

Codex review P1(3913883249)跟进:`import(`../../design-tokens/src/snapshot.ts`)`、
`require(`@cindy/design-tokens/src/snapshot.ts`)` 这类无插值模板字面量是
有效运行时加载,但提取正则只认单双引号——包 id 与相对路径两个通道都漏放。
实测确认。

- SPECIFIER_CONTEXT_RE / IMPORT_ENTRY_PATTERNS 的引号字符类从 ['"] 扩到
  ['"`],全部六种语境(from / import() / require() / require.resolve /
  import.meta.resolve / 裸副作用 import)同步。
- 带 ${…} 插值的模板**同样命中**:路径在运行期拼装恰是零接线阶段不该
  出现的动态消费形态,按宁误报不漏放处理(与 member-require 同一威胁
  模型,如实声明)。
- 数据语境的普通模板不误报:剥离层在非说明符位置把模板内容剥成空白,
  既有机制无需改动。
- 反证用例:import/require/require.resolve/from/副作用 import 的模板形态
  (包 id + 相对路径 + 插值)全部命中;数据模板回归不误报。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;30 单测全绿;
全仓扫描 2.4s 零命中零误报。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 语义豁免色登记 exemption 元数据,fs 裸相对路径直读纳入零接线扫描

Codex review 两条 P2 跟进:

① 豁免色缺 protected 登记(3914411235,成立):destructive / error-* /
warning-* / focus-ring* 已进 semantic 建模,但 DESIGN.md §10 列其为
theme-invariant 语义豁免色、治理合同 §3.2 要求 Tier-3 豁免色作为 protected
角色迁移——分类里却是无元数据的普通 literal,DS-8 生成主题入口时无法区分
可覆写 semantic 与必须保留原值的豁免族。修法:新增 SEMANTIC_EXEMPTION_IDS
与 exemption 元数据(与 PROTECTED_IDS 并列、语义不同——豁免色**照常建模**
只带标记,protected 是「只登记不建模」);ClassificationEntry 加 exemption
字段,11 个已建模豁免色重新生成登记;新守卫 assertSemanticExemptionsRegistered
校验已建模豁免色必须带标记且不与 protected 混用。

② fs 裸相对路径直读漏检(3914411249,成立):
readFileSync('../../design-tokens/src/semantic/color.json') 是最直接的
消费方式,两通道都漏。修法:fs API 路径参数成为第八类说明符语境
(readFileSync/readFile/writeFileSync/existsSync/statSync 等 13 个 API 的
第一参数,含成员链与模板形态),按调用文件位置 resolve。误报核查:第一方
代码既有 fs 读取全部走 resolve(__dirname…) / new URL(…) 包装(54 处实测),
裸相对形态为零——纳入扫描零误报,全仓扫描实证。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;33 单测全绿
(新增豁免色守卫 + fs 检测两组用例);全仓扫描 3.0s 零命中零误报;
生成器两次运行字节一致。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 零接线扫描纳入路径构造器与 Windows 反斜杠说明符

Codex review 两条 P2 跟进:

① 路径构造器包装漏检(3914597287,成立):
`readFileSync(resolve(__dirname, '../../design-tokens/…'))` / `join(…)`
是仓内既有 fs 读取的主流写法(54 处实测),旧实现只认 fs API 第一参数
直接以引号开始——包装形态两通道都漏。修法:resolve/join 调用内的静态
字符串片段(任意参数位置)成为第九类说明符语境,按调用文件位置 resolve
(__dirname ≈ 文件所在目录)。剥离层语境判定同步(resolve(/join( 开括号
后未闭括号内的字符串保留)。误报核查:既有 54 处用法全部不指向
design-tokens,Array.join 分隔符字符串不以 '.' 开头天然过滤——全仓扫描
零误报实证。

② Windows 反斜杠说明符漏检(3914597300,成立):
`require('..\\..\\design-tokens\\src\\x')` 在 Windows CJS 合法,
但说明符直接交给 path.posix、反斜杠不被识别为分隔符——resolve 结果不
落进 design-tokens,漏放。修法:说明符统一反斜杠归一为 / 再 resolve
(源码字符串里 \\ 是转义单反斜杠;仓内无含反斜杠字符的 POSIX 文件名,
替换是安全近似)。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;35 单测全绿
(新增两组 11 个断言形态);全仓扫描 3.1s 零命中零误报。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 路径构造器重建全部静态参数,Tier-1 受保护 slot 照常语义建模

Codex review 两条 P2 跟进:

① 路径构造器只捕获第一个字符串参数(3914703193,成立):
`join(__dirname, '..', '..', 'design-tokens/…')` 分段拼接形态实际读取
design-tokens,但正则只看第一个 '..' 就返回 false——常见分段写法绕过
守卫。修法:重建调用内**全部**静态字符串参数——PATH_CONSTRUCTOR_CALL_RE
捕获完整调用实参区,按序提取字符串字面量拼成候选路径再按文件位置
resolve(非字符串参数天然缺席,等价于从文件目录起算的相对段)。反证:
分段 join / 多段 resolve / 模板段全命中;既有 54 处用法与 Array.join
分隔符回归零误报。

② 受保护 Tier-1 文本角色被移出 semantic(3914703199,成立):
text-secondary / text-secondary-cross 是 DESIGN.md §10 Tier-1 slot,
治理合同 §3.2 明确「Tier-1 semantic slots → semantic,名称与用途延续」;
却被 PROTECTED_IDS 统一 register-only 分支标成只登记、从 semantic JSON
消失——DS-8 接管生成时无法从新真相源生成这两个核心文本变量。U2 保护
限制的是改值须裁决(never darken unilaterally),不等于禁止迁移。
修法:ProtectedRule 加 mode 字段——Tier-1 slot(text-secondary /
text-secondary-cross)semantic-modeled:照常值形态分类 + 强制进
semantic + 保留 protected 元数据;Tier-3 singleton(annotation-accent、
login-brand-*)维持 register-only(治理合同「保留原位,逐项裁决,默认
不动」,本 PR 未裁决它们进 semantic)。守卫 assertProtectedNotSemantic
按 mode 分支校验;SEMANTIC_ROLES 补登记两个 Tier-1 slot。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;35 单测全绿
(oracle 与保护值用例同步 mode 语义;value-consistency 验证
text-secondary 双模式值与冻结快照一致);全仓扫描零命中零误报;
生成器两次运行字节一致。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 零接线扫描纳入 import.meta.glob,语境判定锚定开引号

Codex review 两条 P2 跟进:

① import.meta.glob 漏检(3914869131,成立):Vite 的
`import.meta.glob('../../design-tokens/src/**/*.json')` 在构建期把影子层
内容打进产品(仓内 packages/lizi-mcps/src/browser/recipe-loader.ts 有
实际用例),但说明符语境没有 import.meta.glob 分支——两通道漏放。修法:
SPECIFIER_CONTEXT_RE / SPECIFIER_PREFIX_RE 补 import.meta.glob( 分支,
相对 glob 模式按文件位置 resolve(glob 元字符不影响前缀段判定)。

② 语境残留误报(3914869140,成立):`import x from './x'; const hint =
'packages/design-tokens/src/x';` 被误报——SPECIFIER_PREFIX_RE 的
\\bfrom / \\bimport 分支在 2320ce02 轮重写时丢失了 \\$ 锚定,匹配的是
「语境里任何位置有 import 字样」而非「语境以 import 结尾」,数据字符串被
剥除层错误保留、裸路径模式随后误报接线。修法:所有分支共享末尾 \\$ 锚定
((?:…)$ 整组包裹),保证匹配的是紧邻当前开引号的调用语境。

反证用例:glob(含模板形态与既有 recipes 用法不误报)、残留语境三种形态
不误报;此前全部九类语境 + 注释/数据字符串回归(23 形态)全绿。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;37 单测全绿;
全仓扫描零命中零误报。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

* fix(design-tokens): 零接线扫描纳入 fs/promises.open 与 .mts/.cts 源码

Codex review 两条 P2 跟进:

① fs/promises.open 漏检(3915016839,成立):
`const file = await open('../../design-tokens/src/semantic/color.json');
await file.readFile()` 真实消费影子层,但 open 不在 fs API 列表(openSync
在、异步 open 不在)——路径字符串被剥除,两通道漏放。修法:open 加入
FS_API_PATH_CONTEXT_RE 与 SPECIFIER_PREFIX_RE,预检补 open 字样(裸
open('…') 不含 File/Sync,此前连剥除层都进不去)。误报核查:window.open
的 URL 参数不以 '.' 开头天然过滤;仓内以 '.' 开头字符串参数的 open( 调用
实测为零。

② .mts/.cts 扫描缺口(3915016844,成立):扫描器扩展名正则只认
ts/tsx/js/mjs/cjs,.mts/.cts(有效 Node/TS 模块扩展,仓内 scripts/ 与
tools/ 已使用)的文件在读取内容前被跳过——相对路径消费且不留
package.json 依赖,两通道都看不见。修法:扩展名正则补 mts|cts,并用
临时 fixture(.mts 与 .cjs 消费者各一)直接验证扫描器行为。

验证:pnpm --filter @cindy/design-tokens typecheck 通过;38 单测全绿;
全仓扫描零命中零误报。

Signed-off-by: Kirozeng <kirozeng@gmail.com>

---------

Signed-off-by: kirozeng <kirozeng@gmail.com>
Signed-off-by: Kirozeng <kirozeng@gmail.com>
* feat(desktop): 支持 Creator Micro 2 与 Codex 同板任务键和宽键

Codex 与 Creator 共用同一 PCB:任意 1U 可设为任务键或动作键,相邻键可合成宽键/长键。
额外任务键走 ACT HID 切换任务,灯光仍只绑前 6 颗 AG。宽键先按下的开关认按下和松开。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 收口 Work Louder 断连手势、Creator keymap 与自定义任务键

断连或报错时释放按住的语音和宽键状态。只改写 Creator Micro 2 的 keymap,停用时从备份还原。
空闲板保留任务目录。第 7 颗及之后的任务键不再假装可自定义。SDK 空闲错误恢复限流。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 按占用刷新 Creator keymap 快照并改写当前 profile

每次改写前保存会话快照,停用时还原该快照而不是过期出厂备份。按 firmware 的 profile/layer 改写。
Codex 状态探测失败不再当成已连接。带新 schema 的全空 Creator 布局不再被迁回默认。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 固定占用前 keymap 快照并保留自定义空槽

旧版 2U 动作从 ACT10_ACT11 迁到 origin。同一次占用内重绑不再覆盖恢复快照。
等待中的 keymap 重绑会套用最新请求。自定义模式前 6 槽的 null 仍是新建任务。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 用完整 Cindy keymap 识别占用快照并在 stop 时中断等待

恢复快照只跳过整层都是 KV_OAI_* 的 Cindy 占用映射,厂商布局即使含 AG00 仍会捕获。
Creator 空闲探测按同型号枚举确认仍在。keymap 重载等待可被 stop 打断,以免 1 秒断开超时杀宿主。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 身份探测失败后重试,并显示额外任务键的实际绑定

键盘尚未接入时启用配件会持续有界重试发现。自定义模式下第 7 颗及之后的任务键悬浮提示改用实际槽位标题。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 会话快照只认即将改写的活动图层

占用身份以当前 profile/layer 为准。其它层上残留的 Cindy 映射不再阻止捕获用户正在编辑的厂商布局。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 失败的 status 信封与 HID 争用不得算作成功

探测和改写都把 {ok:false} 当成 round-trip 失败,走同一条断开/重试路径。keymap 争用排期后循环内检查退避,不再立刻连打。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): Creator 空闲超时不探测,布局预览只挡住正在编辑的板

hid_read_timeout 是 Creator 静默,不再触发 getDeviceStatus。打开另一块板的设置页时,占用中的键盘快捷键继续可用。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 异常断连恢复 keymap,Codex 按出厂 HID 解角色

占用写入后任何 teardown 都先尝试写回会话快照。Codex 固件不改写键位,任务键按物理 AG/ACT 名称解析,不再套 Creator 的重映射。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 换板继续发现,Codex 灯跟物理 AG,初次连接拒绝失败信封

已识别的板消失后继续发现另一块启用的键盘。Codex 任务灯按物理 AG00–AG05 对齐;初次连接的 getDeviceStatus 失败信封与探测同一条断开路径。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 占用变化时重算布局预览抑制

设置页打开时记住正在编辑的型号。这块板之后才占用 HID、或占用转到另一块时,预览租约跟着重算,避免按键误触发动作或挡住别的板。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 退出超时恢复 keymap,争用报占用,闲置投影额外任务键

stop 不再无限等灯光 RPC,1s 杀进程前把 Creator keymap 写回。HID 争用走 device-in-use,退避中的 probe/listen 不再报 connected。未占用的 custom 额外任务键从任务目录投影标题。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 预览租约随窗口销毁,合并保留动作,同步设计台账

设置页 renderer 崩溃时清掉预览会话,避免按键被永久抑制。合成宽键后保存不再把原动作写成空。rebase 后重跑 design:inventory。

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* feat(skillhub): connect clients to Cindy SkillHub

Signed-off-by: xd-bobo <caojianbo@xd.com>

* fix(skillhub): remove legacy XD visibility copy

Signed-off-by: xd-bobo <caojianbo@xd.com>

* feat(skillhub): add scoped catalog tabs

Signed-off-by: xd-bobo <caojianbo@xd.com>

* fix(skillhub): remove available market filter

Signed-off-by: xd-bobo <caojianbo@xd.com>

* refactor(skillhub): simplify skill catalog header

Signed-off-by: xd-bobo <caojianbo@xd.com>

* feat(skillhub): 优化目录导航、匿名浏览和扫描反馈

Signed-off-by: xd-bobo <caojianbo@xd.com>

* fix(desktop): keep local endpoints across realm switches

Signed-off-by: xd-bobo <caojianbo@xd.com>

* fix(skillhub): align catalog presentation and review feedback

Signed-off-by: xd-bobo <caojianbo@xd.com>

* fix(skillhub): guard endpoint and preserve metadata

Signed-off-by: xd-bobo <caojianbo@xd.com>

* fix(skillhub): 对齐组织归属与 XD 只读策略

Signed-off-by: xd-bobo <caojianbo@xd.com>

* fix(skillhub): 透传技能目录来源

Signed-off-by: xd-bobo <caojianbo@xd.com>

* fix(skillhub): 由服务端决定组织目录路由

Signed-off-by: xd-bobo <caojianbo@xd.com>

* feat(skillhub): 支持当前版本公开审核

Signed-off-by: xd-bobo <caojianbo@xd.com>

* feat(skillhub): 支持首页分页加载更多

Signed-off-by: xd-bobo <caojianbo@xd.com>

* fix(skillhub): align catalog scope and publishing contracts

Signed-off-by: xd-bobo <caojianbo@xd.com>

---------

Signed-off-by: xd-bobo <caojianbo@xd.com>
…makecindy#3822)

* feat(desktop): support native image generation for custom Codex providers

Signed-off-by: fmfsaisai <fmfsaisai@gmail.com>

* refactor(desktop): generalize custom provider Codex capabilities

Signed-off-by: fmfsaisai <fmfsaisai@gmail.com>

* fix(desktop): reload custom Codex providers atomically

Signed-off-by: fmfsaisai <fmfsaisai@gmail.com>

* fix(desktop): hard-retire Codex host before provider changes

Signed-off-by: fmfsaisai <fmfsaisai@gmail.com>

---------

Signed-off-by: fmfsaisai <fmfsaisai@gmail.com>
* fix(skillhub): preserve catalog scope boundaries

Signed-off-by: xd-bobo <caojianbo@xd.com>

* fix(skillhub): avoid duplicate auto-sync install slots

Signed-off-by: xd-bobo <caojianbo@xd.com>

---------

Signed-off-by: xd-bobo <caojianbo@xd.com>
dashhuang and others added 27 commits September 5, 2026 07:33
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(codex): honor custom provider context windows

Signed-off-by: xujun <xujun@xd.com>

* fix(codex): preserve custom context startup compatibility

Signed-off-by: xujun <xujun@xd.com>

* fix(codex): rebuild context hosts across model switches

Signed-off-by: xujun <xujun@xd.com>

---------

Signed-off-by: xujun <xujun@xd.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: fmfsaisai <fmfsaisai@gmail.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(desktop): allow verified model window switches

Signed-off-by: david <david@xd.com>

* fix(desktop): explain model switch protection failures

Signed-off-by: david <david@xd.com>

* fix(desktop): 修复跨供应商切模型与上下文保护

Signed-off-by: david <david@xd.com>

* fix(desktop): 保留设置输入框深链能力

Signed-off-by: david <david@xd.com>

* fix(desktop): isolate Release auth snapshots during dev forks

Signed-off-by: david <david@xd.com>

* test(desktop): clean up rebase assertion

Signed-off-by: david <david@xd.com>

* chore(desktop): refresh design inventory after main rebase

Signed-off-by: david <david@xd.com>

---------

Signed-off-by: david <david@xd.com>
* fix(device-link): keep subscription recovery alive before presence arrives

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(device-link): stop unknown subscription replay on explicit offline

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* test(device-link): clarify existing known-online replay policy

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(mobile): 同步键盘避让与输入卡片动画

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): 避免 Web 调用原生键盘快照接口

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(pi): preserve explicit catalog reasoning levels

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(pi): validate explicit catalog reasoning capabilities

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

---------

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>
Co-authored-by: Chris <4436110+zqchris@users.noreply.github.com>
Signed-off-by: Lizi <jiali@magiclizi.com>
Signed-off-by: Lizi <jiali@magiclizi.com>
* feat(models): 支持 GPT-6 Astra 并修正推理与缓存计量

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(usage): 接通 Fast 参考估值与 Codex 缓存写入计量

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(usage): 保留标准价格覆盖后的独立 Fast 参考价

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(pi): 同步 OpenAI 公共目录并保留上游 Astra 元数据

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* test(pi): 修复 Windows 同步夹具的 ESM 加载路径

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(context): 校正历史任务窗口并明确模型目录维护归属

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* perf(mobile): isolate composer updates and move resize gestures to UI thread

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): scope command refs and immediately roll back canceled resize

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): keep editor height limits on the UI resize path

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(desktop): 仅在确认订阅额度重置时播放礼花

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(desktop): 稀疏额度快照同步更新百分比基线

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* feat(computer): negotiate driver contracts and verify action outcomes

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(computer): cancel startup waits and disambiguate repeated tokens

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(device-link): 区分内容恢复状态并加快前台切网恢复

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(device-link): preserve weak-network tolerance and invalidate peer recovery

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(usage): 修正平均 token 速度取样与主子计时隔离

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(usage): 仅随新增输出推进配对耗时并保留提示换行

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
…indy#3958)

* fix(model-selector): unify default A and synchronize favorite configuration

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(model-selector): preserve Fast across engine reselect and verify provider routes

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(model-switch): recover native history through durable Cindy handoffs

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(model-switch): recover outgoing auth and align pending picker state

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(model-selector): keep explicit configuration open during interaction

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

* fix(model-selector): toggle configuration with the same button

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

---------

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>
Co-authored-by: Chris <4436110+zqchris@users.noreply.github.com>
…ecindy#3950)

* perf(desktop): 优化任务搜索与 SkillHub 数据库访问

Signed-off-by: david <david@xd.com>

* fix(desktop): fence SkillHub refresh across account switches

Signed-off-by: david <david@xd.com>

* fix(desktop): align SkillHub migration snapshot indexes

Signed-off-by: david <david@xd.com>

---------

Signed-off-by: david <david@xd.com>
* fix(models): 对齐 Server 模型目录与客户端兜底

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(models): 保留目录同步后的网关与消费端兼容

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(mobile): 移除设置中的被控权限说明

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* fix(mobile): 清理被控权限遗留翻译文案

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* fix(mobile): 新建任务沿用上次项目或对话选择

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

* test(mobile): 覆盖新建工作区偏好恢复时序

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>

---------

Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
Signed-off-by: Dash <125997726+dashhuang@users.noreply.github.com>
* feat(desktop): add library clipboardWrite PNG bitmap op

Plugins already send library-request clipboardWrite; the host whitelist
rejected it as PATH_INVALID. Add the op as a PNG bitmap write via
nativeImage + clipboard.writeImage, not saveAs/reveal or the
media:copy-to-clipboard file-reference path.

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): reject invalid base64 and truncated PNG in clipboardWrite

v1 review: Buffer.from accepted bad padding, and isPngBuffer only
checked the 8-byte magic, so truncated PNG still reached
writeClipboardPng. Decode strictly and require a full IHDR header.

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): require a complete PNG, not a 24-byte header

v1 follow-up: a 24-byte signature+IHDR prefix is still truncated.
Accept only a full IHDR chunk plus IEND, and use a real 1x1 PNG fixture.

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): walk PNG chunks instead of searching for IEND

includes('IEND') accepted truncated images with the ASCII marker
embedded mid-file. Parse length+type+data+crc, CRC-check IHDR, and
require IEND as the final chunk with no trailing bytes.

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): map missing shell window to UNSUPPORTED

FORGE_GUIDE promises UNSUPPORTED when no main shell window can host
clipboardWrite. Catch the production '没有可挂靠的宿主窗口' error instead
of collapsing it into INTERNAL.

Signed-off-by: PraiseZhu <zhuzan@xd.com>

* fix(desktop): disclose library clipboardWrite in plugin details

Plugin details for library:true only mentioned Finder/Explorer and Save As.
Host already writes PNG bitmaps to the system clipboard without a confirm
dialog, so the five-locale copy and ghost.ts comment now say so.

Signed-off-by: PraiseZhu <zhuzan@xd.com>

---------

Signed-off-by: PraiseZhu <zhuzan@xd.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.