状态:权威开发规则(authoritative) 读取时机:新增或修改涉及 workdir 文件、agent 进程或会话数据的功能,新增/修改 IPC channel 或推送事件,修改 device-link 的重试/超时/断链恢复逻辑,或设计功能入口之前
Cindy 的产品形态不止本地桌面单机。同一个功能可能运行在三种场景里,而这三种缺口都
不报错、typecheck/单测拦不住,只在对应场景的用户实际使用时才暴露成“功能在远程/
手机上不工作”。多端的产品语义见
../product-rules/core-product-principles.md
的「多端连接与任务连续性」;插件在这三种场景的约束见
plugin-security-and-authoring.md。
增量适用原则:约束新增和正在修改的功能;默认期望在同一 PR 内一并适配,适配量大 时才拆 issue 跟踪。
- SSH 远程工作区:workdir、agent 进程、文件都在远程主机上,经
packages/maker-remote-ssh、packages/remote-file-service与 cc-manager 驱动。 - 设备互联远程控制:手机或另一台桌面通过
packages/device-link隧道驱动被控桌面端, IPC channel 走白名单准入。 - 手机版:
apps/mobile独立客户端,作为纯控制端复用 device-link。
| 内容 | 权威来源 |
|---|---|
| SSH 远程工作区 | packages/maker-remote-ssh、packages/remote-file-service、cc-manager |
| 设备互联/手机准入白名单 | packages/device-link/src/allowlist.ts |
| 手机版客户端 | apps/mobile |
- 功能涉及 workdir 文件/agent 进程/会话数据时,在 SSH 远程工作区下能否正常工作?路径
与执行位置在远端,直接
fs读 workdir 会读到本机——必须走 remote-file-service/ cc-manager/exec 等现有远程通道。 - 新增/修改的 IPC channel 与推送事件,手机/远程控制场景需不需要用?需要就按
packages/device-link/src/allowlist.ts顶部注释的准入判据登记 invoke/push 白名单并 同步 topic 路由;不登记,手机/远程控制端就永远调不通。 - 手机版需不需要对应的入口/UI/交互?
设备互联是 1:N 拓扑:被控端与 relay 之间只有一条连接,同账号的全部控制端共用它。
故障域从小到大分四层——单个请求、单个 peer 的 link、整条 relay 连接、relay 聚合
背压(第四层:故障原因不是任何单个请求或 peer,而是本机对 relay 的聚合出站速率;
relay 以 close 1013 inbound backpressure 主动断连,此时任何「立即重连 + 全量重放」
的恢复动作都会立刻复现故障,形成「重连 → 洪峰 → 再被踢」的自放大循环,2026-08-08
线上:两次 1013 间隔 15s,第二条连接只活了 7s,期间控制端全部超时熔断)。修改
packages/device-link 或 Desktop dispatch 层的重试/超时/teardown/重连逻辑前,
先回答三个问题:
- 触发条件是哪一层的故障? 单个请求失败、单个 peer 停止 ACK、整条连接断开, 还是 relay 对聚合速率的背压?对第四层,恢复动作除了同半径(连接级冷却/降速) 外还要问一句:重连成功后的重放会不会立刻重造触发条件?
- 恢复动作作用在哪一层? 默认选择与故障同半径的最小动作。动作半径大于故障半径 (如「单 peer 可靠重试耗尽 → 强拆整条 relay 连接」)就是把一台设备的故障放大成 所有设备同时掉线;确需扩大半径的,必须在 PR 描述里写明理由,且理由要经得起 「一台手机退后台休眠时会发生什么」的追问。
- 多 peer 拓扑下测过吗? 恢复路径改动必须带「≥2 个控制端共享同一被控端,其中 一个 peer 静默/停止 ACK,断言其它 peer 的 link 与在途请求零感知」的用例。单 peer 对连的用例验证不了故障放大——单测全绿只说明实现忠实于设计,设计本身选错 半径时测试不会报警。
判例:#1187(2026-07-31)引入「可靠重试耗尽 → 强拆整条 relay 连接」,wire 向后兼容、 单测全绿、多轮 review 通过,上线后一台休眠手机反复把同账号所有设备(含桌面↔桌面) 一起打掉线,由 #1405 收窄止损半径修复。协议兼容、allowlist、单测三层防线对这类问题 全部免疫,只有 review 时点名问「半径」才拦得住。
Desktop 和 Mobile 的按设备排队、并发上限、退避、取消代次由
packages/device-link/src/peerRecoveryScheduler.ts 统一维护。Desktop 的订阅快照与
在线判断、Mobile 的后台生命周期与页面恢复仍由各端适配器负责。同一设备取消后重新
请求恢复,须等待旧请求结算;旧结果不能取消新请求,也不能恢复已取消的重试。
packages/device-link/src/invokePolicy.ts 集中维护请求策略,三个边界独立判断:
- 通道执行预算:保留 Desktop / Mobile 的超时差异,超时不代表主机操作未执行。
- peer reset 后可重试的读取:显式列举,不按名字推断写操作可以重试。
- 可共享在途结果的 listing:不能从“可重试”推导。
sessions:get与要求fresh的sessions:list必须重新读取,避免复用写入之前开始的快照。
主机数据库后台准入是独立的资源分配策略,不能直接复用上述 listing 集合作为分类依据。 这些策略不改变 wire 格式或通道权限;权限仍以 allowlist 为准。
面向移动端新增独立产品入口时,默认通过 @cindy/device-link 的 Remote Resource
协议接入,不为每个业务模块复制 DTO、store、channel 与 push reason。稳定 wire 入口只有:
maker:remote-resources:manifest:主机声明 collection、placement 与客户端可展示的有限原语;maker:remote-resources:list/maker:remote-resources:get:读取资源投影;maker:remote-resources:invoke:调用主机已注册、已校验的 opaque action;maker:remote-resources:changed:只表达 collection/ref 失效,控制端据此重拉。
Desktop 功能模块通过 RemoteResourceRegistry 注册 provider;主机保留业务权威和安全边界,
provider 只投影用户可见信息。密钥、Token、渠道凭证、系统路径、内部 prompt 与运行时对象不得
进入资源响应。Mobile shell 只理解有限的展示/交互原语,不按 Bot、Schedule 或 Plugin 的内部
enum 编译分支,也不接受任意 HTML、React 或无限 UI DSL。
资源身份必须包含 deviceId + collectionId + kind + id。资源路由打开时应重新调用 resource:get
解析 conversation 等 link,不能把可能 rollover 的 Session id 当成永久资源身份。已有对话继续
复用 canonical Session 的消息、输入、确认与恢复链路,不复制一套模块专属聊天协议。
协议按字段追加演进。未知字段、未知 collection 和未知 action 不得导致整个首页或会话崩溃;
结构化 Session 内容必须携带可读 fallbackMarkdown,旧客户端至少能阅读并继续任务。只有新增
移动端此前无法表达的内容或交互原语时,才要求客户端发版。
功能类 PR 的 Description 必须写明上述每一项的结论,三选一:
- 本 PR 已一并适配;或
- 已开跟踪 issue 并贴链接;或
- 说明为什么不涉及(给出理由,不能只写「不涉及」)。
review 按此检查:功能类 PR 缺这段说明 = P1。
触及 device-link 重试/超时/断链恢复路径的 PR,Description 还必须写明「故障半径 三问」的结论(故障层级、动作层级、多 peer 用例)。缺失同样 = P1。
- 涉及 workdir/agent/会话数据的功能,在 SSH 远程下是否走远程通道而非本机
fs? - 新 IPC/推送是否按 allowlist 判据登记 invoke/push 白名单并同步 topic 路由?
- 手机版入口/UI 是否已适配或明确跟踪?
- PR Description 是否给出了三选一结论,而不是留空或只写「不涉及」?
- 触及重试/超时/断链恢复路径的改动:恢复动作是否与故障同半径?扩大半径是否给出 明确理由?是否有多 peer 拓扑用例证明其它 peer 零感知?