chainlesschain 0.162.169 → 0.162.171

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/package.json +2 -2
  2. package/src/assets/web-panel/assets/{AIOps-DCeNRK8z.js → AIOps-Cf8yb18C.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-ZfD0HgVe.js → ActionButton-CsFT6FMt.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-Ccn40f9E.js → Analytics-DQe_hXMA.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-DyXs4nSt.js → AppLayout-Cl9Pp7jk.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-CYjMh5KR.js → Artifacts-CBEzLVjF.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-DZIhvx88.js → Audit-BS2Un14C.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-C1XJsa2d.js → BackgroundAgents-DmcDH8OK.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-CMXC0oUN.js → Backup-DvzjTkkn.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-CRVmIH1t.js → BaseInput-DnbpuyME.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-acBud5Yj.js → Chat-BJRRCm2P.js} +6 -6
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CTjqFhdD.js → ChatBubbleRenderer-Bgpxyqd_.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-CZem825o.js → Checkbox-DFOrWmvm.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-B9UjZsww.js → Codegen-BIQ2kU6Q.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-Dtu6Frtj.js → Col-BI3R-N4I.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-9bV55Huv.js → Community-CPiAwsHT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-g-kQ4c84.js → Compact-Bv4cslSK.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CQjHzBO1.js → Compliance-CSlDG1NZ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-D1Qm_Xhi.js → Cowork-CFcRumXf.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-992qo8zX.js → Cron-DKYKMmOn.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-4TiqEwuc.js → Crosschain-CxPMByp2.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BbsXzXCb.js → DID-DJqiYw1C.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-Dx5aO9d3.js → Dashboard-D2dLMxqy.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-BhzW6i6i.js → Dropdown-BnJc384A.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-qEib4yiq.js → EmailListRenderer-DctFvm-J.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BZ3N-u3h.js → FamilyGuardDashboard-J_xg-Qop.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-CcOqwzHY.js → Federation-BckL07Uw.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-dbi8mohd.js → FormItemContext-B8s5ccSO.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-57dqulUe.js → GenericCardRenderer-hPMBK2No.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-DtSV7C5g.js → Git-CfMpYLo7.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CLnmH-RD.js → Governance-Bt3OcrC7.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-wXiLeDnk.js → Inference-BlKc336N.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-NGdIX2Gx.js → KnowledgeGraph-B6p7og2c.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-Dz0HQn69.js → Logs-uv5Re6YR.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-CId5JWjX.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-DpPDmY3W.js → Marketplace-CfNbntHt.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-UMysoRQm.js → McpTools-YRXt74Yk.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-DsnBpQnY.js → Memory-BfQQs-Jd.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-DweooAfG.js → MobileBridge-BuGC-4S2.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-UdLfZOVA.js → MobileProjects-Czieqw1m.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-oXWhJNgC.js → Mtc-DHWHOVCO.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-CXTD7qrm.js → MtcAudit-DWSEbMW-.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-BKt5mJ9O.js → Multisig-DrhEv1vq.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-C-zcYzje.js → NLProgramming-D8wKCu7E.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CGaZ_jep.js → Notes-AFeqq0I_.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-BwX-tDbq.js → NotificationSettings-D6qenYUo.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-Cc9Se8H-.js → OrderTableRenderer-CF0X4fyB.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-8BXdC7Im.js → Organization-DdfbNqf6.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-zCisFuGU.js → Overflow-BFEvnLfd.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-BehHH9Ro.js → P2P-DtL6wbkz.js} +2 -2
  51. package/src/assets/web-panel/assets/PdhVaultBrowser-CQPriTud.js +7 -0
  52. package/src/assets/web-panel/assets/{Permissions-BVDATm3p.js → Permissions-Bj25ANmR.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-C43nnYoi.js → PersonalDataHub-BNj2Oux0.js} +3 -3
  54. package/src/assets/web-panel/assets/{Pipeline-D9E7aSVD.js → Pipeline-Dyj8UEaG.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-CRbIchdL.js → Privacy-BBUrWRFz.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-CTsy8lhL.js → ProjectInit-BdX7uQn6.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-CPEKWWJ5.js → ProjectSettings-BghtW1zr.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-CNnW0v5N.js → Projects-9VTquZCz.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-BeZnDL9Y.js → Providers-D-0K_Eza.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-CaydwuIU.js → QrScannerModal-DDaN30_D.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-qYWbu7kw.js → QuickAsk-DfHlifOj.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-RV_ogHrM.js → Recommend-CgLIvYE2.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-Mah1i41_.js → RemoteSession-DpOPrwE4.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-xZQhGktc.js → Reputation-wMexlbCE.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-B6cVbPbJ.js → Row-D9aD_nrc.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-C6hKjDPz.js → RssFeed-Drg7l6zu.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-0jti_-Pe.js → Search-BKO1nrwN.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-Cy48feGz.js → Security-G3Kyk-TG.js} +3 -3
  69. package/src/assets/web-panel/assets/{Services-BW3ObFxH.js → Services-xJReTOna.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-CfrZRvAM.js → Skeleton-ldVDVTPG.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-yoccdk3t.js → Skills-emwNYV0j.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-AKBBdri6.js → Sla-CZYwGp1K.js} +1 -1
  73. package/src/assets/web-panel/assets/SpeechSettings-CyaPrzOF.js +1 -0
  74. package/src/assets/web-panel/assets/{SyncSettings-DUuO-ZSw.js → SyncSettings-Dcs5Hhj6.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-ZTUfOzMZ.js → Tasks-CerFAo26.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-shPrQ4NE.js → Templates-BdZUbOg3.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-C_inPunR.js → Tenant-B4c_7Bsw.js} +1 -1
  78. package/src/assets/web-panel/assets/Terminal-VVFLS3Ol.js +3 -0
  79. package/src/assets/web-panel/assets/{TimelineRenderer-Bzp_T3Xc.js → TimelineRenderer-tc6sp0TV.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-kUFRLdtp.js → Tokens-Bw_vkMJq.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-BsTU2blQ.js → Trigger-DkbJnpvz.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DEKkuAsi.js → Trust-CIf9kqN1.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-D1KEEgbK.js → UkeySign-Cjwllnea.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-C2yYkm9W.js → VideoEditing-kgfkY7AN.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-BKKod4qb.js → Wallet-CYWXt4u9.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-XXUgED6d.js → WebAuthn-QgPwyWCO.js} +3 -3
  87. package/src/assets/web-panel/assets/{WorkflowEditor-DnlEyYTi.js → WorkflowEditor-Co0lha18.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-BaVjI6nu.js → chat-CI__Ox-r.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-DQ0yx43H.js → colors-Csv0eYYd.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-DEyWDp0v.js → compact-item-e53UCdHy.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-SuPOfylZ.js → createContext-BMPGRDhV.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-BIulobq7.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-CQz1JNtC.js → hasIn-Dz68qIAq.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BJEIjYhV.js → index--7tHEadk.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BIgWgye3.js → index-53ljQo_x.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-C_fcNCoV.js → index-5iicv5af.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BffmUosH.js → index-B1s1B8Iy.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CgvnudM_.js → index-B4lKPJy1.js} +1 -1
  99. package/src/assets/web-panel/assets/index-BGkiYnET.js +1 -0
  100. package/src/assets/web-panel/assets/{index-COsmMpAO.js → index-BHkDRNA5.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-57GdL9ro.js → index-BOpz2qS1.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DjZ6szGM.js → index-BT3CV4Nh.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DpPv8zeU.js → index-BW2dImbg.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CpR6M7eQ.js → index-C-NbcAbP.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Dfh6OJKz.js → index-C53o-MdJ.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CfVW-cd_.js → index-C5hLtxVz.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-ByGF9ev7.js → index-CNtZMPKV.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-ZKOZ-YFJ.js → index-CXuON9OG.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CGu-b0RE.js → index-CYUxs6J1.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-3xcB2i6I.js → index-CoGSbW5J.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DB8AJ6T-.js → index-CpTzqJ0Z.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-XjQfQBXH.js → index-CrsoSnQF.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CZrARoXd.js → index-CvLBQk_M.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DWIuZ9bI.js → index-CyLUaP2i.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CEfLMvKf.js → index-D0bg74e1.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-ZMgCYtI7.js → index-DCl4Xb4N.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DXczrVij.js → index-DJqQL6so.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-fGgx2f1y.js → index-DVCd2aMX.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BHJhyrFr.js → index-DctYrVMq.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-v8PYYw05.js → index-Dk9Gi8xd.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-fcA6tfBA.js → index-DnXm3Qsh.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-C3Vlo45C.js → index-Dtjm1K1a.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-wBsuJZcz.js → index-DyqcHiL1.js} +1 -1
  124. package/src/assets/web-panel/assets/index-Eyhrr_3u.js +1 -0
  125. package/src/assets/web-panel/assets/{index-CnhJU5To.js → index-HbhQRrM2.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D5SY6Lwu.js → index-K5E_wjut.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DhBeUGCp.js → index-N9DkR1i7.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CI5jlfyn.js → index-_LmPlyeV.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DNYVGZzG.js → index-ajz0E0pK.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-C2k8eHyq.js → index-t6f80uqL.js} +17 -17
  131. package/src/assets/web-panel/assets/{index-C-IKjQD3.js → index-wcCouSNq.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D-gXgEsU.js → index-ygfUVVFv.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-KetDXPXx.js → initDefaultProps-DVCeQJrT.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BfAtwo2m.js → motion-chBCR_8v.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-BnJvzHgx.js → move-BziPlrh6.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-DZxDclhM.js → mtc-parser-Cenk0OD7.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-CmqfX2P_.js → omit-D-AiRsei.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-B2B2ahzC.js → pickAttrs-CHw3Fvqt.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-C1SIgWRj.js → placementArrow-g8I-rCad.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-Dy_adDTl.js → responsiveObserve-BWffAr9k.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-hDO9sBPk.js → slide-BmUQqhkb.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-DlpTjRpg.js → statusUtils-B89gCO-y.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-DAv9eo6h.js → styleChecker-x1bnCF73.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-vfoXaeH8.js → useFlexGapSupport-B-k1_Ggw.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-Dzji-S7v.js → useFs-Bc3PUqQk.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-jKcLN_Gj.js → usePersonalDataHub-D8qFDX1I.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-DAWM5GgY.js → vnode-UaL5-WGg.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-q3XgSOUd.js → zoom-Dv7bsbLs.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agent.js +39 -14
  151. package/src/commands/background-session.js +12 -1
  152. package/src/data/changelog.json +7 -2
  153. package/src/gateways/ws/ws-agent-handler.js +26 -1
  154. package/src/gateways/ws/ws-approval-gate.js +131 -0
  155. package/src/lib/agent-worktree.js +173 -5
  156. package/src/lib/background-agent-supervisor.js +234 -14
  157. package/src/lib/interaction-adapter.js +26 -5
  158. package/src/lib/packer/smoke-runner.js +5 -0
  159. package/src/lib/repl-rewind.js +62 -14
  160. package/src/lib/turn-binding.js +134 -0
  161. package/src/repl/agent-repl.js +75 -3
  162. package/src/repl/repl-turn-binding.js +91 -0
  163. package/src/runtime/headless-runner.js +59 -0
  164. package/src/assets/web-panel/assets/MarkdownRenderer-CSRZ9da3.js +0 -1
  165. package/src/assets/web-panel/assets/PdhVaultBrowser-DVuXYKAR.js +0 -7
  166. package/src/assets/web-panel/assets/SpeechSettings-DH87GbkH.js +0 -1
  167. package/src/assets/web-panel/assets/Terminal-CvVtx6ie.js +0 -3
  168. package/src/assets/web-panel/assets/devWarning-C_RK48j-.js +0 -1
  169. package/src/assets/web-panel/assets/index-CDqhisfz.js +0 -1
  170. package/src/assets/web-panel/assets/index-CKUoX6Yi.js +0 -1
@@ -3,7 +3,7 @@
3
3
  "releases": [
4
4
  {
5
5
  "productVersion": "Unreleased",
6
- "cliVersion": "0.162.169",
6
+ "cliVersion": "0.162.170",
7
7
  "cliVersions": [
8
8
  "0.4.46",
9
9
  "0.4.51",
@@ -27,11 +27,16 @@
27
27
  "0.162.166",
28
28
  "0.162.167",
29
29
  "0.162.168",
30
- "0.162.169"
30
+ "0.162.169",
31
+ "0.162.170"
31
32
  ],
32
33
  "date": null,
33
34
  "title": "",
34
35
  "sections": [
36
+ {
37
+ "heading": "Added — cc CLI 0.162.170:后台 Agent stop 自杀守卫 + 可注入 kill seam + smoke-runner 死 pid 守卫;发版门 flake 治本后首个无 skip_tests 发版(CLI-only npm 发版)",
38
+ "body": "> `chainlesschain` 0.162.169 → **0.162.170** 发 npm `latest`(经 `npm-publish.yml`,`--provenance --access public`)。CLI-only:`packages/cli/src` 仅两文件(+ vitest 4.1.5→4.1.10 devDep 升级与测试);未触 `pdh/lib` → 无 Android bundle / 无 USR_VERSION。**无新增顶层命令,命令数 175 不变**;默认路径字节不变(守卫只在损坏/异常记录上改变行为)。本版同时是 CI 发版门 worker-death flake(卡了 .167/.168/.169 三个发版、cli-ci 30+ 连败)治本(trap #34)后的**验证发版**——test 门应首跑即绿、无需 `skip_tests`。\n\n- **后台 Agent stop 自杀守卫(真实世界加固)**:`stopBackgroundAgent` 现拒绝向 `pid === process.pid` 的记录发信号——合法的 worker 记录不可能指向执行 stop 的进程自身,命中即判损坏记录(标 `lost`/`self-pid-corrupt-record`、不杀任何进程)。此前一份损坏/手改的 state 文件能让 `cc daemon stop` 对用户自己的 shell 树发 SIGTERM(POSIX)或 `taskkill /T /F`(Windows)。agentPid 组杀同样加 `!== process.pid` 守卫;`defaultKillProcessTree` 同款自杀守卫。\n- **可注入 kill seam**:supervisor 全部 POSIX 信号从裸 `process.kill` 收进 `_deps.kill` 单一 seam——测试可注入(正是 flake 根因之一:`_deps.spawnSync` stub 只盖 Windows taskkill 分支、POSIX 裸 kill 拦不住),生产行为不变。\n- **smoke-runner 死 pid 守卫**:子进程已退出(`exitCode`/`signalCode` 非 null)时跳过 Windows `taskkill /T /F`——Windows 秒级复用 pid,对死尸 pid 的树杀会误杀顶替者全树(CI 上即另一 vitest worker / 无辜测试子进程)。\n- **vitest 4.1.5 → 4.1.10**(root workspace 全体 devDep):含 4.1.9 的真 pool 修复(worker 崩溃挂起、per-task error 监听、STOPPED 态、回收 isTerminated 守卫);非 flake 解药但保留为正确卫生。agent-sdk 刻意保持 `^3` 钉版;独立 web-panel 不动。"
39
+ },
35
40
  {
36
41
  "heading": "Added — cc CLI 0.162.169:增量 gap-analysis 收口批——后台 Agent `needs_input`/`uncertain_side_effect` 真实生产者 + IDE stream 路径副作用台账 + post-edit 诊断多根 + plugin 安装入统一 install 审计(CLI-only npm 发版)",
37
42
  "body": "> `chainlesschain` 0.162.168 → **0.162.169** 发 npm `latest`(经 `npm-publish.yml`,`--provenance --access public`)。纯 `packages/cli/src` 增量(4 个 feat/fix commit + 测试 + gap-analysis 文档状态);未触 `pdh/lib` → 无 Android cc bundle rollover / 无 USR_VERSION 改动。**无新增顶层命令,命令数 175 不变**。默认路径全部字节不变——唯一行为变化限**后台** agent 子进程(`ask_user_question` 从 `user_not_reachable` 变为 park 成 `needs_input`,前台/headless 不受影响)。\n\n- **P0 后台 Agent:`needs_input` + `uncertain_side_effect` 真实生产者(状态机相位收口)**:最后两个无生产者相位补齐。`uncertain_side_effect`——后台子进程 `--resume` 时 side-effect ledger reconcile 出 **UNKNOWN 结局**的不可逆操作(inspect 桶),即把 `phase:\"uncertain_side_effect\"` + 计数写进共享 state,Dashboard 归入 \"Needs input\"(unknown 必须人工确认);worker 在下一 turn/idle 边界清注解。`needs_input`(**提问**态,区别于审批)——后台 turn 子进程的 `ask_user_question` 现在有人接:把 `phase:\"needs_input\"` + `pendingQuestion:{question,options}` 写进 state、指示模型结束回合,用户经 attach 的回复作为下一 turn 到达;新纯核 `idlePhaseFor` 保证**未答问题在 idle 边界不降级为 Idle**。session-lifecycle/phase 词表/Dashboard 分组同步扩齐;前台运行不供 interaction → 既有 `user_not_reachable` 路径逐字节不变。\n- **P0-2 IDE stream 路径副作用台账(崩溃恢复补全第三路)**:crash-safe side-effect ledger(prepare→start→commit|fail 记账 + resume 时 verify-before-replay reconcile)此前只在单-prompt runner 与 WS bridge——IDE 双面板真正驱动的 **headless-stream**(`--input-format stream-json --resume`)没有:面板 worker 在 git push/publish/opaque shell 中途被杀,resume 后无恢复通知、可能盲目重放。现 stream 孪生路径同样记账(副作用 settle 前持久化快照),resume 时 reconcile 并把 Recovery notice 以 system 消息 + `raw/side_effect_recovery` 行双路发出(已发的 VS 0.37.16 / JB 0.4.60 面板已渲染该行)。匿名/`--ephemeral` 运行字节不变。\n- **P2 LSP:post-edit 诊断多根**:`_postEditDiagnostics` 的根选择不再恒 cwd——三个编辑工具(write_file/edit_file/edit_file_hashed)按**包含被编辑文件的根**(`pickRootForFile`,镜像 code_intelligence)做 server 探测与共享池 key,编辑 `--add-dir` 根内文件时诊断走它自己项目的语言服务器而非静默降级为无诊断。单根会话字节不变。\n- **P0 沙箱:plugin 安装入统一 install 审计(「下载并运行第三方代码」三入口收口)**:`cc plugin add`/`upgrade` 拉来的插件带可执行组件(bin/hooks/LSP/MCP 以进程运行),本质是 plugin-runtime 版的 `npm install -g`——现与 run_shell 安装、run_code 自动安装共用同一 opt-in 审计 trail(`CC_INSTALL_AUDIT`/settings `installPolicy` → `install-commands.jsonl`):新纯核 `classifyPluginInstall`(manager `cc-plugin`、`name@version`、**仅 user scope 判 global**、source URL + 已声明能力 token 随行),add 成功后与 upgrade **真落盘**新版本后记 `source:\"plugin_install\"`(up-to-date no-op 不记)。默认(策略关)零写;审计 best-effort 绝不破坏安装。"
@@ -26,6 +26,7 @@ import { runnableTaskModel } from "../../lib/runnable-provider.js";
26
26
  import { PlanState } from "../../lib/plan-mode.js";
27
27
  import { CLISlotFiller } from "../../lib/slot-filler.js";
28
28
  import { createAbortError, isAbortError } from "../../lib/abort-utils.js";
29
+ import { createWsApprovalGate } from "./ws-approval-gate.js";
29
30
 
30
31
  export class WSAgentHandler {
31
32
  /**
@@ -34,7 +35,7 @@ export class WSAgentHandler {
34
35
  * @param {import("../../lib/interaction-adapter.js").WebSocketInteractionAdapter} options.interaction
35
36
  * @param {object} [options.db]
36
37
  */
37
- constructor({ session, interaction, db }) {
38
+ constructor({ session, interaction, db, approvalGate }) {
38
39
  this.session = session;
39
40
  this.interaction = interaction;
40
41
  this.db = db || null;
@@ -44,6 +45,27 @@ export class WSAgentHandler {
44
45
  // P0-2: monotonic sequence for crash-safe side-effect ledger op ids. The
45
46
  // session id + a per-op nonce keep ids unique across turns and processes.
46
47
  this._sideEffectSeq = 0;
48
+ // P0 authority: permission-gate-over-WS. An explicitly injected gate wins
49
+ // (tests / embedders); otherwise one is built lazily on the first turn
50
+ // when CC_WS_APPROVAL_GATE=1 — its confirmer raises confirm questions
51
+ // over the interaction adapter WITH an approval binding the client must
52
+ // echo back on approve (mismatch → deny). Default (env unset, nothing
53
+ // injected): approvalGate stays null and loopOptions are byte-identical.
54
+ this._approvalGate = approvalGate || null;
55
+ this._approvalGateInit = Boolean(approvalGate);
56
+ }
57
+
58
+ /** Resolve the session's approval gate (lazy, opt-in; null when disabled). */
59
+ async _ensureApprovalGate() {
60
+ if (this._approvalGateInit) return this._approvalGate;
61
+ this._approvalGateInit = true;
62
+ if (process.env.CC_WS_APPROVAL_GATE === "1") {
63
+ this._approvalGate = await createWsApprovalGate({
64
+ sessionId: this.session.id,
65
+ interaction: this.interaction,
66
+ });
67
+ }
68
+ return this._approvalGate;
47
69
  }
48
70
 
49
71
  /**
@@ -127,6 +149,9 @@ export class WSAgentHandler {
127
149
  slotFiller,
128
150
  interaction: this.interaction,
129
151
  signal: abortController.signal,
152
+ // P0 authority: null unless opted in (byte-identical default — agent
153
+ // core already defaults `options.approvalGate || null`).
154
+ approvalGate: await this._ensureApprovalGate(),
130
155
  };
131
156
 
132
157
  // P0-2: crash-safe side-effect ledger for the IDE/bridge path — mirrors
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Permission-gate-over-WS — the approval-BINDING producer for the WS path.
3
+ *
4
+ * The consumer side has been wired for a while: WebSocketInteractionAdapter
5
+ * stores `options.binding` on a pending request, rides it out on the message,
6
+ * and `_resolvePending` rejects an approve whose echoed binding mismatches
7
+ * (deny, fail closed). What was missing is the PRODUCER: nothing on the WS
8
+ * path ever raised an approval request WITH a binding, because the WS agent
9
+ * handler wired no ApprovalGate at all (CONFIRM-tier decisions fell closed).
10
+ *
11
+ * This module supplies both halves:
12
+ * - `buildWsApprovalConfirmer` — the gate confirmer that computes
13
+ * `approvalBindingDigest({toolCallId, args, policyDigest})` (mirroring the
14
+ * headless-stream `interactiveConfirm` producer) and asks the WS client
15
+ * over the interaction adapter with the binding attached. Timeout /
16
+ * disconnect / any transport error → false (fail closed).
17
+ * - `createWsApprovalGate` — a session-scoped gate whose CONFIRM decisions
18
+ * route to that confirmer. Policy tiers stay authoritative on the shared
19
+ * singleton gate (so `sessions.policy.set` over WS keeps working and no
20
+ * global confirmer is ever mutated — a per-session confirmer on the
21
+ * process-wide singleton would race across concurrent WS sessions).
22
+ *
23
+ * Default byte-identical: the WS handler only builds this when
24
+ * `CC_WS_APPROVAL_GATE=1` (or a gate is injected); otherwise loopOptions
25
+ * carry `approvalGate: null` exactly as before.
26
+ */
27
+
28
+ import { approvalBindingDigest } from "../../lib/agent-authority.js";
29
+
30
+ /**
31
+ * Build the binding-attaching confirmer for one WS session.
32
+ *
33
+ * @param {object} opts
34
+ * @param {{askConfirm: Function}} opts.interaction - the session's WS adapter
35
+ * @param {string} opts.sessionId
36
+ * @returns {(ctx?: object) => Promise<boolean>}
37
+ */
38
+ export function buildWsApprovalConfirmer({ interaction, sessionId }) {
39
+ let approvalSeq = 0;
40
+ return async (ctx = {}) => {
41
+ const command = ctx.command ?? ctx.args?.command ?? null;
42
+ const risk = ctx.riskLevel || ctx.risk || "unknown";
43
+ // Bind this approval to the exact call it authorizes (same recipe as the
44
+ // headless-stream producer): request identity + normalized args + the
45
+ // policy/rule in force. The client must echo it back on approve.
46
+ const binding = approvalBindingDigest({
47
+ toolCallId: `${sessionId}:appr-${++approvalSeq}`,
48
+ args: ctx.args ?? (command != null ? { command } : null),
49
+ policyDigest: ctx.rule || ctx.riskLevel || ctx.risk || null,
50
+ });
51
+ const what = ctx.tool || ctx.toolName || "tool";
52
+ const detail = command != null ? `: ${String(command).slice(0, 200)}` : "";
53
+ try {
54
+ const ok = await interaction.askConfirm(
55
+ `Approve ${what}${detail}? (risk: ${risk})`,
56
+ {
57
+ default: false,
58
+ binding,
59
+ // Structured context so UIs can render a proper approval card
60
+ // instead of parsing the question text.
61
+ approval: {
62
+ tool: what,
63
+ command,
64
+ risk,
65
+ rule: ctx.rule || null,
66
+ },
67
+ },
68
+ );
69
+ return ok === true;
70
+ } catch {
71
+ // timeout / disconnect / interrupt → fail closed, never approve
72
+ return false;
73
+ }
74
+ };
75
+ }
76
+
77
+ /**
78
+ * Create a session-scoped approval gate for the WS agent handler.
79
+ *
80
+ * Policy lookups delegate to the persistent singleton gate (per-session tiers
81
+ * set via `sessions.policy.set` stay live); CONFIRM decisions route to the
82
+ * binding-attaching WS confirmer. Returns null on any load failure so the
83
+ * caller degrades to the legacy no-gate path (fail closed at CONFIRM).
84
+ *
85
+ * @param {object} opts
86
+ * @param {string} opts.sessionId
87
+ * @param {{askConfirm: Function}} opts.interaction
88
+ * @param {object} [opts.deps] - test seams: `loadSingletons`, `loadSessionCore`
89
+ * @returns {Promise<object|null>}
90
+ */
91
+ export async function createWsApprovalGate({
92
+ sessionId,
93
+ interaction,
94
+ deps = {},
95
+ }) {
96
+ try {
97
+ const loadSingletons =
98
+ deps.loadSingletons ||
99
+ (() => import("../../lib/session-core-singletons.js"));
100
+ const loadSessionCore =
101
+ deps.loadSessionCore || (() => import("@chainlesschain/session-core"));
102
+ const [singletons, core] = await Promise.all([
103
+ loadSingletons(),
104
+ loadSessionCore(),
105
+ ]);
106
+ const inner = await singletons.getApprovalGate();
107
+ const confirmer =
108
+ deps.confirmer || buildWsApprovalConfirmer({ interaction, sessionId });
109
+ const gate = new core.ApprovalGate({
110
+ defaultPolicy: core.APPROVAL_POLICY?.STRICT || "strict",
111
+ confirm: confirmer,
112
+ });
113
+ return {
114
+ // Explicit ctx.policy always wins inside decide(); resolve it from the
115
+ // singleton on every call so mid-session `sessions.policy.set` applies.
116
+ decide: (ctx = {}) =>
117
+ gate.decide({
118
+ ...ctx,
119
+ policy:
120
+ ctx.policy || inner.getSessionPolicy(ctx.sessionId || sessionId),
121
+ }),
122
+ hasConfirmer: () => gate.hasConfirmer(),
123
+ setConfirmer: (fn) => gate.setConfirmer(fn),
124
+ getSessionPolicy: (sid) => inner.getSessionPolicy(sid || sessionId),
125
+ setSessionPolicy: (sid, policy) =>
126
+ inner.setSessionPolicy(sid || sessionId, policy),
127
+ };
128
+ } catch {
129
+ return null; // degrade to the legacy no-gate path
130
+ }
131
+ }
@@ -11,11 +11,18 @@
11
11
  * a merge hint. Removal failures fail-open to "kept".
12
12
  */
13
13
 
14
- import { execSync } from "child_process";
14
+ import { execFileSync, execSync } from "child_process";
15
+ import { existsSync, realpathSync } from "node:fs";
16
+ import { resolve } from "node:path";
15
17
  import { createWorktree, removeWorktree } from "./worktree-isolator.js";
16
18
  import { findProjectRoot } from "./project-instructions.js";
17
19
 
18
- export const _deps = { execSync, createWorktree, removeWorktree };
20
+ export const _deps = {
21
+ execFileSync,
22
+ execSync,
23
+ createWorktree,
24
+ removeWorktree,
25
+ };
19
26
 
20
27
  function git(cmd, cwd, deps) {
21
28
  return (deps.execSync || execSync)(`git ${cmd}`, {
@@ -25,6 +32,132 @@ function git(cmd, cwd, deps) {
25
32
  });
26
33
  }
27
34
 
35
+ function canonicalPath(value) {
36
+ const absolute = resolve(String(value || ""));
37
+ const real = realpathSync.native
38
+ ? realpathSync.native(absolute)
39
+ : realpathSync(absolute);
40
+ return process.platform === "win32" ? real.toLowerCase() : real;
41
+ }
42
+
43
+ function parseRegisteredWorktrees(text) {
44
+ const entries = [];
45
+ let current = null;
46
+ for (const line of String(text || "").split(/\r?\n/)) {
47
+ if (line.startsWith("worktree ")) {
48
+ if (current) entries.push(current);
49
+ current = { path: line.slice("worktree ".length) };
50
+ continue;
51
+ }
52
+ if (!current) continue;
53
+ if (line.startsWith("HEAD ")) current.head = line.slice("HEAD ".length);
54
+ if (line.startsWith("branch ")) {
55
+ current.branch = line.slice("branch ".length);
56
+ }
57
+ }
58
+ if (current) entries.push(current);
59
+ return entries;
60
+ }
61
+
62
+ /**
63
+ * Verify that persisted worktree metadata still identifies the same linked
64
+ * worktree. Cleanup callers use this as a fail-closed identity check before
65
+ * invoking any removal primitive: a missing/corrupt path must never turn into
66
+ * a recursive delete of the repository checkout.
67
+ *
68
+ * @returns {{valid:boolean, reason:string}}
69
+ */
70
+ export function validateAgentWorktree(info, { deps = _deps } = {}) {
71
+ const worktreePath = info?.path || info?.worktreePath;
72
+ if (!worktreePath || !info?.repoRoot || !info?.branch || !info?.baseSha) {
73
+ return { valid: false, reason: "incomplete worktree metadata" };
74
+ }
75
+ if (!/^[0-9a-f]{40,64}$/i.test(String(info.baseSha))) {
76
+ return { valid: false, reason: "invalid worktree base SHA" };
77
+ }
78
+ if (!existsSync(worktreePath) || !existsSync(info.repoRoot)) {
79
+ return { valid: false, reason: "worktree or repository path is missing" };
80
+ }
81
+
82
+ try {
83
+ const repo = canonicalPath(info.repoRoot);
84
+ const worktree = canonicalPath(worktreePath);
85
+ if (repo === worktree) {
86
+ return {
87
+ valid: false,
88
+ reason: "refusing to treat the main checkout as an agent worktree",
89
+ };
90
+ }
91
+
92
+ const repoTop = canonicalPath(
93
+ git("rev-parse --show-toplevel", info.repoRoot, deps).trim(),
94
+ );
95
+ const worktreeTop = canonicalPath(
96
+ git("rev-parse --show-toplevel", worktreePath, deps).trim(),
97
+ );
98
+ if (repoTop !== repo || worktreeTop !== worktree) {
99
+ return { valid: false, reason: "worktree path identity mismatch" };
100
+ }
101
+
102
+ const registered = parseRegisteredWorktrees(
103
+ git("worktree list --porcelain", info.repoRoot, deps),
104
+ ).find((entry) => {
105
+ try {
106
+ return canonicalPath(entry.path) === worktree;
107
+ } catch {
108
+ return false;
109
+ }
110
+ });
111
+ if (!registered) {
112
+ return {
113
+ valid: false,
114
+ reason: "path is not a registered worktree of the repository",
115
+ };
116
+ }
117
+ if (registered.branch !== `refs/heads/${info.branch}`) {
118
+ return {
119
+ valid: false,
120
+ reason: "registered worktree branch does not match persisted metadata",
121
+ };
122
+ }
123
+ return { valid: true, reason: "verified" };
124
+ } catch (error) {
125
+ return {
126
+ valid: false,
127
+ reason: `worktree identity could not be verified: ${error.message}`,
128
+ };
129
+ }
130
+ }
131
+
132
+ function removeVerifiedAgentWorktree(info, deps) {
133
+ // Unit seams historically inject removeWorktree directly. Keep that seam,
134
+ // while production uses a non-forced git removal: if the tree changed
135
+ // between inspection and deletion, git refuses instead of discarding it.
136
+ if (deps.removeWorktree && deps.removeWorktree !== removeWorktree) {
137
+ deps.removeWorktree(info.repoRoot, info.path);
138
+ return null;
139
+ }
140
+
141
+ const run = deps.execFileSync || execFileSync;
142
+ run("git", ["worktree", "remove", info.path], {
143
+ cwd: info.repoRoot,
144
+ encoding: "utf8",
145
+ stdio: ["ignore", "pipe", "pipe"],
146
+ });
147
+ try {
148
+ run("git", ["branch", "-D", "--", info.branch], {
149
+ cwd: info.repoRoot,
150
+ encoding: "utf8",
151
+ stdio: ["ignore", "pipe", "pipe"],
152
+ });
153
+ return null;
154
+ } catch (error) {
155
+ // The worktree itself is already gone. Preserve an honest warning rather
156
+ // than claiming the whole cleanup failed and that the path was kept.
157
+ return `branch cleanup failed: ${error.message}`;
158
+ }
159
+ }
160
+
28
161
  /**
29
162
  * @returns {{ path, branch, repoRoot, baseSha }}
30
163
  */
@@ -51,22 +184,57 @@ export function setupAgentWorktree({
51
184
  export function finishAgentWorktree(info, { deps = _deps } = {}) {
52
185
  if (!info) return { removed: false, kept: false, reason: "no worktree" };
53
186
 
187
+ // Production cleanup validates the persisted path against git's own
188
+ // worktree registry. Custom dependency bundles are test seams and retain the
189
+ // historical pure mocked behavior unless they provide validateWorktree.
190
+ const validation =
191
+ typeof deps.validateWorktree === "function"
192
+ ? deps.validateWorktree(info)
193
+ : deps === _deps
194
+ ? validateAgentWorktree(info, { deps })
195
+ : { valid: true, reason: "injected validation seam" };
196
+ if (!validation?.valid) {
197
+ return {
198
+ removed: false,
199
+ kept: true,
200
+ reason: `unverifiable worktree: ${validation?.reason || "unknown reason"}`,
201
+ };
202
+ }
203
+
54
204
  let dirty = true;
55
205
  let headSha = null;
206
+ let readable = false;
56
207
  try {
57
208
  dirty = git("status --porcelain", info.path, deps).trim().length > 0;
58
209
  headSha = git("rev-parse HEAD", info.path, deps).trim();
210
+ readable = true;
59
211
  } catch {
60
212
  /* unreadable → keep, never destroy work we cannot verify */
61
213
  }
62
214
 
215
+ if (!readable) {
216
+ return {
217
+ removed: false,
218
+ kept: true,
219
+ reason: "unverifiable worktree: git state could not be read",
220
+ };
221
+ }
222
+
63
223
  const hasCommits = headSha !== null && headSha !== info.baseSha;
64
224
  if (!dirty && !hasCommits) {
65
225
  try {
66
- (deps.removeWorktree || removeWorktree)(info.repoRoot, info.path);
67
- return { removed: true, kept: false, reason: "no changes" };
226
+ const warning = removeVerifiedAgentWorktree(info, deps);
227
+ return {
228
+ removed: true,
229
+ kept: false,
230
+ reason: warning ? `no changes; ${warning}` : "no changes",
231
+ };
68
232
  } catch (err) {
69
- return { removed: false, kept: true, reason: `cleanup failed: ${err.message}` };
233
+ return {
234
+ removed: false,
235
+ kept: true,
236
+ reason: `cleanup failed: ${err.message}`,
237
+ };
70
238
  }
71
239
  }
72
240
  return {