Skip to content

R25 集成批 6 + R26 批 1:连接状态单一真相 + Seatbelt 沙箱 + 先劝再断 + 插件真 PG 门与 golden + MCP 设计稿 - #25

Merged
mycyg merged 32 commits into
mainfrom
r25/integration-4
Sep 5, 2026
Merged

mycyg merged 32 commits into
mainfrom
r25/integration-4

Conversation

@mycyg

@mycyg mycyg commented Sep 5, 2026

Copy link
Copy Markdown
Owner

R25 集成批 6 + R26 批 1:桌面端连接状态单一真相(W-Q)、Seatbelt 沙箱 fail-closed(B8)、重复动作先劝再断(B6)、MCP 客户端设计稿。基线已含 PR #24

一、桌面端连接状态单一真相(r25/q-desktop-connection-state,7 commits)

  • Rust 侧事件 workhub-connection-changed{ state: connected | reconnecting | offline, server_url, since_ms, attempt }。纯函数状态机把 Connecting/Open/Retrying/Closed 收敛成三态;连续失败不满 3 次是「重连中」,之后才「已离线」;只在三态/attempt/server_url 任一变化时广播;等 client token 期间不广播。新命令 get_connection_state 供三窗 boot 拉初值。
  • 三窗接线:工作台头部状态词、主窗聚焦盒顶部连接状态细条、桌宠纯文本提示;撤下桌宠从 sse-status 猜出来的离线卡接线。
  • L-06 根治:桌宠离线提示不再产生 CuuCard(会把窗口撑到 520×720),改走紧凑气泡路径,窗口维持 260×340。
  • 登录广播对称:workhub-logged-insource: main | workbench,两端按 source !== 自己 才 reload,防双刷。
  • 与聚焦盒生长线在 main.rs 只撞一处 use 行,两边保留。真机走查另有 QA 工人在验,结论作为追加 commit 或后续批。

二、B8 Seatbelt 沙箱 fail-closed(r26/s-seatbelt-sandbox,5 commits)+ 旧措辞同步

  • 三档 SandboxMode = read-only | workspace-write | danger-full-access,agent run 默认 workspace-write。
  • macOS:argv 一律包成 sandbox-exec -p <profile> -- <argv>(deny default) + (deny network*) 起手,profile 本机逐条实测收敛(//var 符号链接本体、xcrun 的 _CS_DARWIN_USER_TEMP_DIR 三个反直觉必需项已写进注释);解析 args[0] 实际路径放行其安装前缀,nvm/pyenv/conda 解释器可用。
  • fail-closed:无可用后端平台默认拒绝执行并回 SANDBOX_UNAVAILABLEAGENT_RUN_ALLOW_UNSANDBOXED_COMMANDS 语义改为「允许降级到软沙箱(partial)」。agent-runner 一律注入 createSandboxedCommandRunner({ allowDegraded })
  • 完整度上报 enforcement: full | partial;Seatbelt 拒绝签名翻成固定话 [sandbox: … denied by policy] 这是沙箱策略拒绝,不是命令写错……不要换写法绕过;系统提示词加工作纪律第 7 条。
  • 拒绝路径可复现:把 darwin 分支改回软沙箱注入回归,4 条测试立红,还原复绿。
  • 新增 SAFETY.md(中英五段)。.env.example / .env.pilot.example / DEPLOY.md 的旧措辞同步。
  • 默认行为变更:macOS 上开关为 false 时 run_command 从「不可用」变为「在 Seatbelt 下可用」。
  • CI 影响已核:r1/r2 PG smoke 用自带 options 的内存队列拿不到注入 runner,行为不变;pilot-stack 在 Linux 容器 + 开关 true = 软沙箱,与今天等价。

三、B6 重复动作先劝再断(r26/t-repeat-tool-reminder,3 commits)

  • DoomLoopDetector.push() 返回带档位的信号;阈值默认 [3,5,8]doomLoopWindow + 固定偏移推出;判定口径(SHA-256 指纹、全同窗口、周期 2 交替)一字未改。
  • 传统 loop:tier 1/2 在 tool_result 之后追加一条独立 user 消息继续跑;tier 3 走原 escalated + StructuredHandoff。loop2 经 steering 注入同一段正文;equivalence 用例断言两套引擎完整对话线逐条 deepEqual。
  • 行为变更:原「撞 3 次即 escalated」改为第 3/5 步各劝一次、第 8 步才升级;测试与 Agent Note 均已明说。预算交互:maxSteps 先耗尽仍按预算路径收尾。

四、插件治理真 PG 门 + 插件工具 golden(r26/v-plugin-pg-e2e-golden,3 commits)+ CI 接线

  • 新增 apps/api/src/qa/r1-pg-plugin-governance-smoke.tsqa:r1-pg-plugin-smoke):0072 表形状(升级路径与全新库两条都在真库跑过)→ 空清单 → 非管理员四端点 403 → 三类 422 → 装 echo 夹具 201 + 重复 409 → 工具注册表(PluginHostClient.toolSpecs,即 agent-runner 走的那条:装完出现、停用消失、另一工作区看不到)→ 移除 404 → 四条审计。挂进 verify.yml 的 r1-pg-smoke job(同容器同 env)。
  • 两条真 PG 门共用的基建抽成 apps/api/src/qa/r1-pg-harness.ts(r1 smoke 改为引用);withErrors 补进 PluginServiceErrorseedAdminHeaders 轮换种子管理员令牌,修掉「同一个库上不能复跑」的坑。
  • 插件工具 golden:apps/api/src/golden/plugin-tool.golden.test.ts + 4 份 expected(真起宿主子进程翻译后的 ToolSpec 非函数面 / toModelTools 视图与角色可见性 / 两套引擎请求体里插件那一项 / 恶意文案中和结果)。「能红」演练:改一个字母 3 份同时红。
  • 照出两条事实:插件工具是本仓第一个 external_effect 工具,research/review 角色看不到它(可见性已显式落盘);插件 ctx.systemPrompt.section 未接线,golden 只覆盖工具面并钉住「装了插件系统提示词逐字节不变」。
  • qa:plugin-smoke 挂进 workspace job(不需要 PG / key)。
  • 修 B8 一条环境相关单测:interpreterReadRoot 规范化函数可注入,Ubuntu 合并 /usr 布局下不再把 /bin/sh 前缀算成 /usr 而红。

五、真机验收与修复(r26/qa-connection-state-device,2 commits + 2 个追加 commit)

打成 release .app 在本机真跑(PG 容器 + API 8787,昵称登录,无模型 key),七个场景全部截图留证(screencapture CLI,坐标取自 CGWindowListCopyWindowInfo):

  • 三窗共用同一份壳层判定,运行期迁移三窗同一拍同步;杀后端后聚焦盒顶部出现琥珀细条(不遮输入框,窗口 706→741),工作台头部切「离线」,桌宠提示出现;连续失败满 3 次切「已离线」(从流断开算起约 28 秒,与设计的 5+10+20 吻合);恢复后端 15–19 秒内三窗同拍回「已连接」。
  • L-06 根治有效:桌宠窗口 45 个采样恒为 260×340(×75% 缩放),一像素没撑大。
  • 玻璃无回归:聚焦盒与新细条在彩色高对比背景上磨砂完好;工作台外缘透色、内部纸感不变。
  • 聚焦盒生长动画复验:10 个可见样本左上角恒为 (504,133),补间 48→706 步长单调收窄、末帧精确、约 185ms。
  • 登录广播正向通过(主窗登录后主窗、工作台各只 boot 一次,无双刷);工作台自己的凭据门登录只在密码模式出现,QA 受操作边界限制未跑,待人工补一次。

修复:

  • 桌宠连接提示文案被截断(地址被省略号吃掉、「重连中/已离线」整段不可见)→ 只放宽「无卡片仅状态文本」这一种气泡的高度上限(calc(100% - 232px*scale),四个缩放档都不越窗)与行数,窗口尺寸不动。
  • 重复推入同一设备令牌不再递增身份代际 → 每开一扇窗/每登录一次不再把活跃 SSE 判成 Superseded 重连一轮(此前一次登录连打三次「重连中→已连接」,后端真挂着时开窗还会把「已离线」打回「重连中」重等 35 秒)。
  • 工作台头部 boot 竞态(一切正常时长期喊「重连中」)→ 三窗改用同一助手 primeDesktopConnectionState:先订阅、订阅落地后再拉快照、事件永远比快照新。
  • 「重连中(第 0 次)」→ 重连中的 attempt 至少为 1(Rust 侧,单测钉住)。
  • 细条隐藏后窗口约 15 秒才回缩(35px 透明空窗吃点击)→ 细条显隐变化时主动请求尺寸重算。

六、MCP 客户端设计稿

reports/r25-侦察-2026-09-05/M-MCP客户端设计.md:新包 packages/mcp-client 与插件宿主平级、mcp_servers 表(迁移 0073)、11 个工包切分。带出一条硬事实:插件工具按 external_effect 映成 riskCategory,人工保留守卫只要它非空就开升级 409,等于每次插件调用都转人(plugin-smoke 靠假守卫跑通)——插件阶段 2 的第一优先项定为逐工具风险分级。

expected 基线 diff 摘要(pnpm gen:expected 幂等复核后 git status 为空)

  • B8:5 个文件 +7/-3——系统提示词工作纪律第 7 条(agent-run-system-prompt.full/.workeragent-run-request.loop/.loop2 共 4 处)+ run_command description 增一句(agent-run-request.loop/.loop2agent-run-tool-schemas 共 3 处)。
  • W-V:只新增 4 份 apps/api/expected/plugin-tool-*.expected.json,既有 47 份零改动。
  • B6:只新增 7 份 packages/agent/expected/doom-loop-reminder.*(档位表 + 三档话术 + 截断尾注 + 第三档 handoff 句),既有 expected 零改动。
  • W-Q / 设计稿:无模型可见文本变化。

验证

  • cargo fmt --check / cargo test 198 / cargo clippy -D warnings 三门绿;desktop-webview 1701 全过;真机 release .app codesign --verify 通过。
  • pnpm -r typecheck 全 Done;tools 52 / agent 286 / api 2111 / plugin-host 43 / db 499 全过;qa:plugin-smoke 通过;qa:r1-pg-plugin-smoke 本机真容器三跑全过。
  • CI(fddf05c6)8/8 绿:workspace / r1-pg-smoke(含插件门)/ r2-pg-redis-smoke / pilot-stack-smoke / web-live-route-smoke / rust-system-i18n / migration-audit / security-advisory。
  • audit:agent-notes(36 份)/ copy-terms / third-party-notices / portable-config 通过。

🤖 Generated with Claude Code

mycyg and others added 24 commits September 5, 2026 22:16
Rust SSE worker 在三态(connected/reconnecting/offline)迁移时广播
workhub-connection-changed(payload: state/server_url/since_ms/attempt),
新增 get_connection_state 命令供窗口 boot 拉初值。三态由 sse.rs 的纯函数
shell_connection_state_for + next_shell_connection_payload 从内部 SSE
per-subscription 状态 + 连续失败次数推导,超过 3 次连续失败后从
reconnecting 翻成 offline;ShellConnectionStatus(sse_worker.rs)持有
运行时单一真相并做迁移去重。挂起等 client token 期间不写,避免把"还没
登录"误判成"连不上"。

Rust 单测覆盖状态机推导边界、迁移去重、payload 序列化形状、
ShellConnectionStatus 的落盘语义;cargo fmt/test/clippy 全绿。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
新增 DesktopShellConnectionState/DesktopShellConnectionChangedPayload 类型
与 parseDesktopShellConnectionChangedPayload(shell-events.ts,字段形状对齐
Rust 侧 sse.rs 的 ShellConnectionChangedPayload 序列化),三窗后续都从这一份
解析取值,不各写一遍。desktop-window-controls.ts 新增 readDesktopConnectionState
(照 resolveDesktopDeviceName 同款 invoke 包装)供窗口 boot 拉初值。
DesktopShellEventName 补 "workhub-connection-changed",并在
"workhub-logged-in" 顶注记录即将补的 source 字段用途。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
store.connectionState(新字段)由 boot.ts 挂载 shell 后拉一次
get_connection_state 补初值 + 订阅 workhub-connection-changed 写入;
rail.ts 顶部"已连接"不再只要 vm 加载成功就硬编码,改读这个字段渲
已连接/重连中/离线(undefined 时沿用"已连接"兜底,vm 能加载成功本身
就是连通性证据)。

工作台自己的凭据门(密码/hybrid 模式)登录成功后此前只 reload 自己这扇
窗口——新增 reloadAfterWorkbenchLogin 复用 desktop-login.ts 的
completeDesktopLoginSuccess,广播 workhub-logged-in 时带
source:"workbench"。bindWorkbenchLoggedInListener 签名改为透传 source,
调用方据 source!=="workbench" 才 reload,避免和自己的直接 reload() 打
一次空转的双重刷新(主窗那一侧的订阅在下一批补)。

boot.test.ts 补 bindWorkbenchConnectionChangedListener 的降级/订阅/丢弃
畸形 payload 三个用例 + bindWorkbenchLoggedInListener 的 source 透传用例。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
新增聚焦盒顶部细条(与「AI 服务未配置」横幅同样式:wh-spot-connection-banner,
warn 语义色/内边距/边框),只在盒子展开态显示,文案见新增
connection-banner-copy.ts 的 desktopConnectionBannerText(connected 不渲,
reconnecting/offline 各一句,随 locale)。spotlight.setConnectionState 由
browser.ts 的 bootSpotlight 驱动:挂载后先拉一次 get_connection_state 补
初值,再订阅 workhub-connection-changed。

主窗此前是登录广播的唯一发起方,从不自我订阅 workhub-logged-in(自己已经
在 reloadAfterDesktopLogin 里直接 reload);现在工作台也能发起这条广播
(source:"workbench"),主窗新增订阅并据 source!=="main" 才 reload,
payload 也带上 source:"main" 供其它窗口做同样的防自循环判断。

controller.test.ts 补横幅槽位的静态渲染断言;新增
connection-banner-copy.test.ts 覆盖三态文案。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
bindDesktopShellCuuRuntime 此前把 sse-status(per-subscription 协议粒度的
原始信号)翻成一张 state="offline" 的 CuuCard 塞进 controller
(bridge.handleSseStatusPayload + retryingDelayMs 防抖 + dismissCardIfPresent
复原)。这张卡的 CuuState 非 idle,走 windowModeForState 的"card"分支会把
桌宠 260×340 撑到 520×720、原生窗口跟着挪位置——这正是 r24-S5-reverify.md
记录的 L-06「桌宠离线卡放大搬家」的根因。

sse-status 订阅本身保留(INF-08 断线重连全量对账仍然需要它),但不再产出
任何卡片;离线/重连提示改由 workhub-connection-changed 驱动(下一批接线,
走既有的"无卡片、只有 status_text"紧凑气泡,不触发窗口尺寸变化)。移除
bindDesktopShellCuuRuntime 的 retryingDelayMs 选项与 pet-surface.ts 里只为
它存在的 desktopPetRuntimeRetryingDelayMs 常量(真正通用的
handleDesktopPetRuntimeNotice/Decision 予以保留——id 前缀判定仍然安全无害)。

desktop-cuu-runtime.test.ts:sse-status 相关三个用例改为断言"不再产卡",
"live locale getter"改用同样走 bridge/emitCard 的 dispatch_ask 推送通知
验证(原机制已随离线卡一起撤下)。

shell-events.ts 的 desktopCuuCardFromShellSseStatus / parseDesktopShellSseStatusPayload
等纯函数与各自的既有单测保留不动(不再被生产代码调用,但仍是独立正确、
有自己覆盖率的工具函数,非本批范围内的死代码大扫除)。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
新增 desktopPetConnectionStatusText:workhub-connection-changed 的 payload
渲成一行诚实文案「连不上服务器 <地址> · 重连中(第 N 次)/已离线」,随
locale。这行文字不产生 CuuCard,走既有的"无卡片、只有 status_text"紧凑
气泡路径(renderDesktopPetSurface 的 compactStatusOnly,body_only 尺寸
260×340 不变——该保证已由既有测试锁死),彻底避开旧 sse-status 离线卡
"state=offline 非 idle → windowModeForState 判成 card → 520×720 撑大+
原生窗口挪位置"这条 L-06 根因路径。

bootDesktopPetSurface 挂载后拉一次 get_connection_state 补初值 + 订阅
workhub-connection-changed,写入独立的 connectionStatus 变量(不复用会被
右键菜单清空的瞬态 statusText)。render() 里两者合并:statusText 优先
(动作反馈),否则在没有真实卡片占用气泡时才显示连接提示;state="connected"
时提示随下一次 render 自然消失,桌宠回到正常待命态。

pet-surface.test.ts 补 desktopPetConnectionStatusText 三态文案单测 + 一条
端到端用例(收到广播渲提示且不触发 resize、恢复后提示清除、dispose 解绑);
清理了因移除 retryingDelayMs 而失去意义的一行断言。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
记录本轮事件契约(workhub-connection-changed/get_connection_state)、
三态状态机推导与去重规则、三窗接线取舍、L-06 根治的具体机制(不复用
CuuCard 管线)、workhub-logged-in 补 source 字段防自循环的设计,以及
考虑过又放弃的几个方案(offline 态整体特批 body_only 会连带破坏登出/
首启卡、横幅按 offline/reconnecting 分色阶、attempt 在 offline 期间
继续累加、连带清空 shell-events.ts 里已撤线但仍自洽的纯函数)。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- types 增加 sandboxMode 三档(read-only / workspace-write / danger-full-access)
  与执行完整度上报(full / partial);
- 新增 seatbelt.ts:(deny default) 起手的 SBPL profile 生成、argv 包裹、
  平台×开关×模式的 fail-closed 决策矩阵、拒绝签名识别与固定话术;
- nodeCommandRunner 在 spawn 前把 argv 包成 sandbox-exec -p '<profile>' --;
  没有可用后端且未显式允许降级时拒绝执行(SANDBOX_UNAVAILABLE)。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts:
#	client-tauri/src-tauri/src/main.rs
- AGENT_RUN_ALLOW_UNSANDBOXED_COMMANDS 语义改为「允许降级到软沙箱(partial)」,
  注释写清三平台矩阵与 fail-closed 默认;
- agent-runner 一律注入 createSandboxedCommandRunner,由沙箱层自己 fail-closed;
- 工作纪律新增第 7 条:沙箱拒绝是策略不是命令写错,不要换写法绕过;
- gen:expected 重生成基线(4 处提示词新增一行 + 3 处 run_command description)。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
阶段 1 的 Agent Note 明写着「未做真 PG 验证」——治理层此前只被内存仓储的单测覆盖,
迁移 0072 在真库上的落地形状、唯一索引这条 409、以及「宿主按 DB 清单热重载之后
工具注册表真的增减」这三件事从没在真 PG 上跑过。

新增 qa:r1-pg-plugin-smoke,跑在 r1-pg-smoke 的同一个容器、同一份 env 上(verify.yml
的同一个 job 里追加一步,不新开 job)。八段断言:0072 表形状(列/索引/三条 CHECK/外键)、
空清单与 bootstrap_path_count=0、非管理员四端点 403、体检拒装三类各自的错误码、
装 echo 夹具 201 + 重复安装 409、toolSpecs 可观测点(装完有/停用没/再启用又有,
且别的工作区看不到)、移除后 404、四个写动作各一条审计。

起库/种子/鉴权/错误信封抽到 r1-pg-harness.ts 由两条门共用(不复制一份);
seedAdminHeaders 顺手轮换种子管理员令牌,让两条门在同一个库上可以重复跑
——种子令牌是每进程随机的,而 ensureDefaultSeed 是 onConflictDoNothing。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
DoomLoopDetector 命中不再一撞上就升级叫醒人:同一重复链路连续 3 步注入一条温和
提醒、5 步注入带工具名/连续步数/规范化参数预览的详细提醒、8 步才走原来的
escalated + StructuredHandoff 路径。判定口径(SHA-256 指纹、全同窗口、周期 2
交替)一字未改,交替形态走同一套档位。

- 新增 packages/agent/src/loop/doom-loop-reminder.ts:档位阈值、动作摘要与三档
  话术(纯函数,便于 golden 逐字节钉住);头部注明借鉴来源与 MIT 许可。
- loop.ts:tier 1/2 在 tool_result 之后追加一条 user 消息后继续跑,tier 3 升级。
- loop2/config-builder.ts:同一份档位实现,tier 1/2 经 getSteeringMessages 在下一轮
  之前注入同一条消息,tier 3 升级;两侧共用 DOOM_LOOP_ESCALATION_REASON。

这是行为变更不是修 bug:原「撞 3 次即 escalated」的用例已按新行为重写。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- 新增 packages/agent/src/golden/loop-reminder.golden.test.ts:用真的
  DoomLoopDetector 跑固定步序列,把「档位触发时机」与「三档正文」一起钉住
  (逐步档位表 JSON + 全同/交替各两档正文 + 参数预览截断尾注 + 第三档升级语)。
  pnpm gen:expected 只新增 7 份基线,既有 expected 一份未改。
- loop2 equivalence:新增重复动作场景,断言两套引擎的完整对话线(含提醒文本、
  角色与位置)逐条相同;另加一条 maxSteps 先耗尽仍按步数预算收尾的用例。
  harness 增加 requestMessages 逐次深拷贝(传统 loop 复用同一个 messages 数组,
  原 requests 抓不到每轮快照)。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- 单测覆盖 profile 生成(deny default、写白名单、SBPL 转义防注入)、
  平台×开关×模式的 12 行决策矩阵、拒绝签名识别与话术、包裹器故障分类;
- 真 macOS 集成用例(非 darwin 或无 sandbox-exec 时 skip):workdir 内写通过、
  workdir 外写被拒、网络被拒、read-only 下 workdir 内写也被拒、子进程继承策略;
- 已验证「拒绝路径可复现」:把后端退回软沙箱后 4 条守卫立即变红,还原后全绿。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
仓库是公开的、跑模型生成的代码、沙箱有明确边界,此前只有 SECURITY.md
(纯漏洞披露流程),没有任何免责与能力口径。照 deepseek-harness 的五段结构
写中英双语,逐条写清 full/partial 两档各自约束什么、不约束什么。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
新建 packages/mcp-client 而不复用插件宿主子进程(MCP 服务器本就是独立进程),
治理形状照抄插件那一套并另起 mcp_servers 表(0073)。含逐字段 ToolSpec 映射、
密钥引用式存储、启动前静态体检、桌面端 UI 与错误文案、假 MCP 服务器夹具与
qa:mcp-smoke/golden 门、11 个可并行工包与依赖图。

顺带记下两条既有事实:external_effect 会让每次工具调用都升级给人
(agent-runner.ts:1716 + human-reserved-guard.ts:246),插件工具的模型可见文本
至今没有 golden 覆盖。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
R25 批 B1 的提示词 golden Note 把「插件工具的翻译形状」列为合并后第一优先补丁——
当时分支基线上还没有插件面。现在补上,四层从「宿主报上来什么」钉到「模型收到什么」:

- plugin-tool-spec:真起一个宿主子进程加载 echo 夹具,翻成 ToolSpec 之后的非函数面
  (id/description/json_schema/side_effect/min_scope/prompt_snippet/prompt_guidelines)。
  不手写描述符常量——dsh defineTool 归一化、translate、to-tool-spec 三段翻译都要在门内。
- plugin-tool-model-view:toModelTools 里的那一项,外加按任务计划角色的可见性。
  插件工具是本仓第一个 external_effect 工具,于是 research/review 看不到它——
  agent-run-prompt.golden.test.ts 里「三种角色可见集一致」那条事实第一次被打破,钉住。
- plugin-tool-engine-request:两套引擎真正发给 provider 的请求体里插件那一项。
  复核 KNOWN_ENGINE_DELTA:side_effect 只在传统 loop 上 wire,值是 external_effect。
- plugin-tool-sanitized:插件自报文案的中和结果(控制字符去掉、换行制表保留、
  围栏标签原样留着——中和不做语义改写)。4000 截断用精确断言而不是落一份 4000 字符的
  expected(一整行的 diff 不可读,同 B1 批对超长转录的处置)。

另加一条断言钉住阶段 0 硬约束:装了插件之后系统提示词逐字节不变。插件的
ctx.systemPrompt.section() 只被宿主收集成 promptSectionCount 计数,正文不过线协议;
且插件工具没有 promptSnippet,所以也不进「可用工具」清单。将来真接线时这条会先红。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
记下两条取舍:可观测点直接用产线的 toolSpecs(不为测试新开只读端点);
插件 golden 放 apps/api 而不是 plugin-host(避免叶子包反向依赖 @workhub/agent)。
以及两条遗留:ctx.systemPrompt.section 未接线所以 golden 只覆盖工具面、
qa:plugin-smoke 至今不在 CI 里(是否挂进去留给指挥者拍板)。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@mycyg mycyg changed the title R25 集成批 6:桌面端连接状态单一真相(三窗接线 + 桌宠离线卡不再撑大) R25 集成批 6 + R26 批 1:连接状态单一真相 + Seatbelt 沙箱 fail-closed + 先劝再断 + MCP 设计稿 Sep 5, 2026
@mycyg mycyg changed the title R25 集成批 6 + R26 批 1:连接状态单一真相 + Seatbelt 沙箱 fail-closed + 先劝再断 + MCP 设计稿 R25 集成批 6 + R26 批 1:连接状态单一真相 + Seatbelt 沙箱 + 先劝再断 + 插件真 PG 门与 golden + MCP 设计稿 Sep 5, 2026
真机复验「连接状态单一真相」这批时发现:桌宠的连接提示走的是「无卡片、只有状态文本」
的紧凑气泡,而这条路径此前与「带卡片的紧凑气泡」共用 86px*scale 上限 + 2 行钳制。
75% 缩放下实测只放得下「连不上服务器 http://127.0.0.1:…」——服务器地址被省略号截掉,
「· 重连中(第 N 次)/ 已离线」整段看不见,重连中与已离线两态在屏幕上一模一样。

只放宽这一种气泡(`:not([data-pet-bubble-kind])`,有卡片时那个属性必然存在):
上限改成「气泡底边到窗口顶边之间的可用高度」,跟着 --wh-pet-scale 与窗口高度自适应,
四个缩放档(75/100/125/150)下都不越窗;行数钳制同步放到 4 行。窗口尺寸仍由
body_only(260×340)钉死,一个像素都没碰——L-06 的根治点在那里,不在这里。

真机验证(release .app,75% 缩放):窗口恒为 195×255(= 260×340 × 0.75),气泡完整渲出
「连不上服务器 http://127.0.0.1:8787 · 重连中(第 0/1/2 次)」与「· 已离线」两种文案。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mycyg and others added 4 commits September 5, 2026 23:31
三扇窗各自 boot 都会把同一个设备令牌推给壳层(browser.ts / workbench/boot.ts 的
pushClientTokenToShell)。ShellClientToken::set 此前无条件递增身份代际并唤醒等待者,
于是每一次窗口 boot 都把活跃的 SSE 判成 Superseded、中止、重连。真机实测一次登录连打
三次(shell.log 里 generation 1→2→3),后果有三:

1. 每开一扇窗、每登录一次,三窗的连接提示都要闪一轮「重连中 → 已连接」;
2. 那一闪的 attempt 是 0,桌宠照字面渲成「重连中(第 0 次)」;
3. 后端真的挂着时,开一扇窗会把 consecutive_failures 复位,已经诚实显示的「已离线」
   被打回「重连中」,重新等满约 35s 才敢再说离线。

代际是**身份**代际,不是"写入次数"计数器:令牌值没变就没有"旧身份连接需要中止"这回事。
清空→再写回同一个令牌仍然是两次真变化,各递增一次(换服务器清令牌那条路径不受影响,
它随后还会递增端点代际并唤醒)。set_client_token 的日志措辞同步改为不再断言"以新身份重连"。

真机验证(release .app):同一会话两次推同一令牌,shell.log 两行都是 generation 1,
SSE 不再重连;杀后端 → 桌宠依次显示第 0/1/2 次 → 约 35s 后「已离线」,不再被开窗打回。
cargo test 197 passed / cargo clippy --all-targets -D warnings 无警告 / cargo fmt --check 干净。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- shell-events 新增 primeDesktopConnectionState:订阅落地后再拉 get_connection_state,快照到手时已收到事件就丢弃
- 工作台/主窗/桌宠三处 boot 改用同一助手;bindWorkbenchConnectionChangedListener 返回「订阅已落地」promise
- 聚焦盒 setConnectionState 在细条显隐变化时 requestResize,消掉隐藏后约 15 秒的 35px 透明空窗

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@mycyg
mycyg merged commit 703511a into main Sep 5, 2026
8 checks passed
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.

1 participant