chainlesschain 0.162.158 → 0.162.160

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 (219) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-BwpTJTN_.js → AIOps-CkRHjW8e.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-BtQOoo3l.js → ActionButton-BP3MeNi2.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-Duf_rXZa.js → Analytics-CjbjQsOr.js} +2 -2
  6. package/src/assets/web-panel/assets/{AppLayout-XQK3wjvB.js → AppLayout-i0adH1M8.js} +5 -5
  7. package/src/assets/web-panel/assets/Artifacts-CCCG2GOU.js +1 -0
  8. package/src/assets/web-panel/assets/{Audit-DNjOGQLh.js → Audit-DrsgvwHC.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-FH9RAfB5.js → BackgroundAgents-DkXNkbZM.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-ZIbrR4-U.js → Backup-DarTWCex.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-DSt3NaND.js → BaseInput-DaDzhcto.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-CY-wvbjv.js → Chat-C4EzGw6V.js} +5 -5
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-lkavIWMS.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-8JVadE_B.js → Checkbox-gddNgaQi.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CN83nPYI.js → Codegen-BKldFWf9.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-xPSvqdN8.js → Col-ChR-brDs.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-D78cnIjG.js → Community-DlHtZX86.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-CSw6Csbp.js → Compact-BAhfcBpC.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-E_HhjcYR.js → Compliance-gIrKkm6m.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DybeQjmm.js → Cowork-BYnzLBO3.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-DS0BJeLY.js → Cron-ByUDIljI.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-BOwVgkbs.js → Crosschain-DOdNbHFp.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-DRUr9YuX.js → DID-BJHaNWD2.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-Cht-yUKW.js → Dashboard-Drm_tClz.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-BLr_EJ9X.js → Dropdown-CA1WLzGN.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-Drexuh5W.js → EmailListRenderer-Di1SKjNO.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D9Q2Kq63.js → FamilyGuardDashboard-44Ve7ZFe.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-C-Z1xI-T.js → Federation-CubPwv_N.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-DLHPMkQF.js → FormItemContext-CsUFe5bU.js} +1 -1
  30. package/src/assets/web-panel/assets/GenericCardRenderer-D7xRjPjZ.js +1 -0
  31. package/src/assets/web-panel/assets/{Git-syATcaBt.js → Git-D-Qx9dhO.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-CYe_r8Zf.js → Governance-_YMYaU4n.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-GWxErB7k.js → Inference-J7Yzh-K_.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-qQvVCZoi.js → KnowledgeGraph-D37iI_Mo.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-BenAU2W8.js → Logs-oMTSYfGY.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-7agE3Or1.js → MarkdownRenderer-BqaPmC0W.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-D4Jn5fpD.js → Marketplace-DKUqYDaF.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DtAwoqiQ.js → McpTools-BEv3Gx61.js} +4 -4
  39. package/src/assets/web-panel/assets/{Memory-BI_O50I8.js → Memory-CtA8XD2y.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-CQWE2lqx.js → MobileBridge-ug7c1mMj.js} +1 -1
  41. package/src/assets/web-panel/assets/MobileProjects-BpX5g9It.js +1 -0
  42. package/src/assets/web-panel/assets/{Mtc-Dnr16qsn.js → Mtc-WJaMs4i2.js} +2 -2
  43. package/src/assets/web-panel/assets/{MtcAudit-CQ5Xe4-a.js → MtcAudit-DXnK_-ru.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-Bjr8O-wy.js → Multisig-C4Ev7726.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-CEe-KirU.js → NLProgramming-BMFb9_zk.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-spKJIKF9.js → Notes-Bl93nvBX.js} +4 -4
  47. package/src/assets/web-panel/assets/{NotificationSettings-DRp83LFG.js → NotificationSettings-BN2W5cl6.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-h1A0XB2a.js → OrderTableRenderer-Dxc9oN4o.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-BlgB0IYg.js → Organization-CAs48637.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-a_35HqZI.js → Overflow-Bhq27Zoc.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-CSNHCNPC.js → P2P-D61-2542.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-C8M1JH-1.js → PdhVaultBrowser-C1j4iUxz.js} +5 -5
  53. package/src/assets/web-panel/assets/{Permissions-DmjJi9Ms.js → Permissions-rJ2-fScM.js} +3 -3
  54. package/src/assets/web-panel/assets/{PersonalDataHub-DfuwRyf4.js → PersonalDataHub-DFIlTok6.js} +2 -2
  55. package/src/assets/web-panel/assets/{Pipeline-B2-S4ekE.js → Pipeline-BjIgEWoZ.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-B1dOiBLC.js → Privacy-BQoH5wcX.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-_pNmc-DX.js → ProjectInit-CYR_-_Ub.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-DmwG2Ii1.js → ProjectSettings-nkatCq-9.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-SS9uZKCl.js +1 -0
  60. package/src/assets/web-panel/assets/{Providers-C6HSjg1d.js → Providers-BnKM9fsF.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-CrDmS7l1.js → QrScannerModal-Cfe82dbz.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-CfKvWt-x.js → QuickAsk-r1RbPufV.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-DMa6Kuzv.js → Recommend-CjCbM2WE.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-DrktUo5x.js → RemoteSession-BuMGPH6V.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-BTPQ1vjL.js → Reputation-Bmj3NY1F.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-B6Q7QkMG.js → Row-BIsNcE_1.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-B1IJhAmX.js → RssFeed-BObmojD2.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-xbea1kGD.js → Search-BRmbkQNS.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-7rs0cO3F.js → Security-_rF3DRpY.js} +2 -2
  70. package/src/assets/web-panel/assets/{Services-0fjixK0S.js → Services-D0If9ZV4.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-B_rVq9Cu.js → Skeleton-o7zIksUg.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-C5uM90b9.js → Skills-oUR7zi9u.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-U4pYCIZH.js → Sla-fqKyGi03.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-Cdz4yTWx.js → SpeechSettings-B1wZ_gxx.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-Me3aetHv.js → SyncSettings-B3qJWYXq.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-Cf6aNkL_.js → Tasks-BH5_dUfr.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-CYatk4tU.js → Templates-CCIVpjRP.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-DIMKLw7o.js → Tenant-2GZtTGnB.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-C_cp-uSJ.js → Terminal-BlMkfs_u.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-cUPVNADp.js → TimelineRenderer-U6hi5OMc.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-CiaEIXVf.js → Tokens-C-_BerGJ.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-DB1WU5lQ.js → Trigger-DaKzBgN3.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-Cfht9Ijy.js → Trust-CLvcsco3.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-K3P4Vt9S.js → UkeySign-BFn_Y1zZ.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-DWjhjXRh.js → VideoEditing-B0V18dLT.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-D9-Q1Kan.js → Wallet-BbmP7Imu.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-BDszy-oV.js → WebAuthn-Dewj_0kt.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-DIhij4rA.js → WorkflowEditor-DOxz3Lwa.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-D7VCiKbh.js → chat-B1OIpZnh.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-BvOd9pVn.js → colors-CSGNVdri.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-DoqXBiAO.js → compact-item-8S0vcg0u.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-CrKRzUtp.js → createContext-vZyc27TN.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-DYEZcY55.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-BWAev4WC.js → hasIn-DWw3-UhA.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DvDOpsJB.js → index-B4pP2ycr.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-oY45XrkG.js → index-BDHyovnK.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CmJ6xKBO.js → index-BKEmQQvR.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BoYrS_L2.js → index-BQssf0EQ.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-ww1QtLnX.js → index-BZnauCqN.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BWWsaimP.js → index-BdyokEau.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CoD31Ndm.js → index-BeBv896m.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DlGu7As6.js → index-BjC-TrOb.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-zH-tuuGZ.js → index-BkWd6YL7.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CEXfKSiI.js → index-BoIPHP4R.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CEPHMSih.js → index-BuB5P0TR.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-PDxGjKRu.js → index-BwKrTVV0.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BkgnGX7v.js → index-C6r27A-o.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DV-Rfmfx.js → index-CB2MOS_8.js} +3 -3
  109. package/src/assets/web-panel/assets/{index-KR1-Em9C.js → index-CFQWZvak.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-8g5TJCZ3.js → index-CI3slBKI.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BCyP071m.js → index-CJgKP2cm.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CSb-tnIo.js → index-CLPr2vC_.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-1poMpMU0.js → index-CbZqw7pg.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BZGk8AbZ.js → index-CfEvacz7.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BXyl8O23.js → index-CwXBblm-.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BHOcVYrx.js → index-D1i_8oop.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Bq_ctRpz.js → index-D8qXVfoV.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-gmxqu5Kk.js → index-DKwl1AkQ.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-D23L1V76.js → index-DLAvUtOH.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CDGLdn12.js → index-DLgkAhEE.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-YGDy7nNq.js → index-DPgY5GLy.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Cx7M7PSF.js → index-Dc40pX3F.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-JzUmJMvn.js → index-DcfLXAW7.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-HwHdTusY.js → index-DeCS12Mh.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-B30NvR21.js → index-Dg0Izipr.js} +1 -1
  126. package/src/assets/web-panel/assets/index-DmVFfcNR.js +1 -0
  127. package/src/assets/web-panel/assets/{index-B0vGEbJT.js → index-DtHWHJnj.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Bf9YlbDn.js → index-Q6oyLnKC.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Bj7EgmOp.js → index-dxocDVD_.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-ISvPZ05r.js → index-t-q3rkVm.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-C5kRmj3P.js → index-tr1x96W9.js} +1 -1
  132. package/src/assets/web-panel/assets/index-x7YyhMUa.js +1 -0
  133. package/src/assets/web-panel/assets/{index-BN9m4Ux2.js → index-xx5M5Rbj.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-XW-OR3mL.js → initDefaultProps-Di5YFId0.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-CeuEHh20.js → motion-BVSN0aeL.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-Dva79Keg.js → move-DHNiaP2l.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-C0B6bD2j.js → mtc-parser-xJPf3gQD.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-AkZvO-a2.js → omit-C7qzUdkN.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-B4r55qGc.js → pickAttrs-pSpjg3Le.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-BLH-z7YE.js → placementArrow-cs_jL_UC.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-ksEGZ2Wf.js → responsiveObserve-DkS_Zs4r.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-Cth4OD1Z.js → slide-B5O1Kpxo.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-9S83ezxw.js → statusUtils-BX02GnZp.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-7yQmQGZ4.js → styleChecker-EmBYRftd.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-DpGnMmPc.js → useFlexGapSupport-ZGzN1cU8.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-C4zDZIXH.js → useFs-CfP7M1m7.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-CoLICdUZ.js → usePersonalDataHub-CbIPHgNh.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-CKyH-35X.js → vnode-7xh0If_O.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-DRDGit17.js → zoom-DmitvTR2.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-manifest.json +17 -3
  152. package/src/commands/agent.js +72 -6
  153. package/src/commands/background-session.js +173 -3
  154. package/src/commands/browse.js +86 -0
  155. package/src/commands/cloud.js +232 -0
  156. package/src/commands/doctor.js +61 -6
  157. package/src/commands/mcp.js +107 -0
  158. package/src/commands/plugin.js +63 -1
  159. package/src/commands/routine.js +367 -0
  160. package/src/commands/session.js +396 -0
  161. package/src/data/changelog.json +12 -2
  162. package/src/harness/jsonl-session-store.js +152 -3
  163. package/src/harness/mcp-client.js +50 -0
  164. package/src/harness/session-index.js +295 -0
  165. package/src/harness/session-mirror.js +142 -0
  166. package/src/harness/transcript-integrity.js +182 -0
  167. package/src/index.js +8 -0
  168. package/src/lib/agent-sandbox.js +68 -0
  169. package/src/lib/agents.js +12 -4
  170. package/src/lib/api-key-helper.js +62 -0
  171. package/src/lib/background-agent-supervisor.js +158 -2
  172. package/src/lib/channels/channel-manager.js +126 -0
  173. package/src/lib/channels/telegram-channel.js +112 -0
  174. package/src/lib/channels/webhook-channel.js +140 -0
  175. package/src/lib/chrome-connector.js +386 -0
  176. package/src/lib/cloud/bundle.js +110 -0
  177. package/src/lib/cloud/cloud-client.js +152 -0
  178. package/src/lib/computer-use/authorization.js +173 -0
  179. package/src/lib/computer-use/computer-use-server.js +276 -0
  180. package/src/lib/computer-use/control-backend.js +150 -0
  181. package/src/lib/dependency-install-policy.js +130 -0
  182. package/src/lib/doctor-checkup.js +652 -0
  183. package/src/lib/exit-codes.cjs +77 -0
  184. package/src/lib/headless-manifest.js +175 -0
  185. package/src/lib/hook-runner.cjs +70 -4
  186. package/src/lib/ide-context-redaction.js +130 -0
  187. package/src/lib/ide-context.js +89 -13
  188. package/src/lib/jsonl-session-store.js +6 -0
  189. package/src/lib/llm-config-defaults.js +24 -8
  190. package/src/lib/lsp/lsp-server-registry.js +48 -21
  191. package/src/lib/plugin-runtime/governance.js +174 -0
  192. package/src/lib/pr-link-ledger.js +221 -0
  193. package/src/lib/project-mcp-trust.js +87 -0
  194. package/src/lib/routine-store.js +352 -0
  195. package/src/lib/sensitive-file-guard.js +51 -0
  196. package/src/lib/session-store-guard.js +40 -0
  197. package/src/lib/session-usage.js +318 -2
  198. package/src/lib/sub-agent-context.js +30 -3
  199. package/src/repl/agent-repl.js +361 -3
  200. package/src/repl/bg-dashboard.js +355 -0
  201. package/src/repl/fast-mode.js +103 -0
  202. package/src/repl/fullscreen-tui.js +197 -0
  203. package/src/repl/slash-command-registry.js +11 -3
  204. package/src/repl/voice-dictation.js +132 -0
  205. package/src/runtime/agent-core.js +344 -7
  206. package/src/runtime/coding-agent-contract-shared.cjs +107 -2
  207. package/src/runtime/coding-agent-policy.cjs +17 -0
  208. package/src/runtime/headless-runner.js +137 -14
  209. package/src/runtime/headless-stream.js +43 -9
  210. package/src/runtime/mcp-config.js +32 -1
  211. package/src/runtime/policies/agent-policy.js +3 -0
  212. package/src/assets/web-panel/assets/Artifacts-DQvJoQ6E.js +0 -1
  213. package/src/assets/web-panel/assets/ChatBubbleRenderer-BAV9V-t-.js +0 -1
  214. package/src/assets/web-panel/assets/GenericCardRenderer-DxxEdn2r.js +0 -1
  215. package/src/assets/web-panel/assets/MobileProjects-CVtTqgtg.js +0 -1
  216. package/src/assets/web-panel/assets/Projects-COATmHxF.js +0 -1
  217. package/src/assets/web-panel/assets/devWarning-nDB--2xr.js +0 -1
  218. package/src/assets/web-panel/assets/index-B1_pRTtB.js +0 -1
  219. package/src/assets/web-panel/assets/index-TMM9FuW-.js +0 -1
@@ -197,6 +197,15 @@ export function registerSessionCommand(program) {
197
197
  process.exit(1);
198
198
  }
199
199
 
200
+ // PR/session linking: surface PRs this session created/touched.
201
+ try {
202
+ const { getPrLinks } = await import("../lib/pr-link-ledger.js");
203
+ const prLinks = getPrLinks(sess.id);
204
+ if (prLinks.length > 0) sess.prLinks = prLinks;
205
+ } catch (_err) {
206
+ // PR decoration is cosmetic
207
+ }
208
+
200
209
  if (options.json) {
201
210
  console.log(JSON.stringify(sess, null, 2));
202
211
  } else {
@@ -206,6 +215,15 @@ export function registerSessionCommand(program) {
206
215
  `ID: ${sess.id} Provider: ${sess.provider} Model: ${sess.model} Messages: ${sess.message_count}`,
207
216
  ),
208
217
  );
218
+ if (sess.prLinks) {
219
+ for (const pr of sess.prLinks) {
220
+ logger.log(
221
+ chalk.magenta(
222
+ `PR: #${pr.number}${pr.state ? ` ${pr.state}` : ""}${pr.url ? ` ${pr.url}` : ""}`,
223
+ ),
224
+ );
225
+ }
226
+ }
209
227
  logger.log("");
210
228
 
211
229
  let messages = sess.messages;
@@ -248,6 +266,10 @@ export function registerSessionCommand(program) {
248
266
  )
249
267
  .option("--model <model>", "Model name")
250
268
  .option("--provider <provider>", "LLM provider")
269
+ .option(
270
+ "--allow-tampered",
271
+ "Resume even when the transcript hash chain is broken (context is untrusted)",
272
+ )
251
273
  .action(async (id, options) => {
252
274
  try {
253
275
  const ctx = await bootstrap({ verbose: program.opts().verbose });
@@ -270,6 +292,29 @@ export function registerSessionCommand(program) {
270
292
 
271
293
  // Try JSONL first
272
294
  if (feature("JSONL_SESSION") && sessionExists(id)) {
295
+ // Tamper gate: a broken hash chain means the transcript was edited
296
+ // outside the store — never silently rebuild it as trusted context.
297
+ const { verifySession } =
298
+ await import("../harness/jsonl-session-store.js");
299
+ const trust = verifySession(id);
300
+ if (trust.status === "tampered") {
301
+ if (!options.allowTampered) {
302
+ logger.error(
303
+ `Session ${id} transcript failed integrity verification: ${trust.reason}` +
304
+ (trust.firstInvalidLine
305
+ ? ` (line ${trust.firstInvalidLine})`
306
+ : ""),
307
+ );
308
+ logger.error(
309
+ "Refusing to resume tampered context. Inspect it read-only with " +
310
+ `'cc session show ${id}' / 'cc session verify ${id}', or pass --allow-tampered to override.`,
311
+ );
312
+ process.exit(1);
313
+ }
314
+ logger.warn(
315
+ `⚠ Resuming a TAMPERED transcript (${trust.reason}) — treat restored context as untrusted.`,
316
+ );
317
+ }
273
318
  const events = readEvents(id);
274
319
  const startEvent = events.find((e) => e.type === "session_start");
275
320
  sess = {
@@ -489,6 +534,76 @@ export function registerSessionCommand(program) {
489
534
  }
490
535
  });
491
536
 
537
+ // session rename — named sessions (gap-analysis 2026-07-11 快速收益 #5).
538
+ // Appends a session_rename event (hash chain stays intact); last one wins.
539
+ session
540
+ .command("rename")
541
+ .description("Rename a headless (JSONL) session — shows up in session list")
542
+ .argument("<id>", "Session ID")
543
+ .argument("<title...>", "New title")
544
+ .option("--json", "Output as JSON")
545
+ .action(async (id, title, options) => {
546
+ try {
547
+ const { renameSession } =
548
+ await import("../harness/jsonl-session-store.js");
549
+ const result = renameSession(
550
+ id,
551
+ Array.isArray(title) ? title.join(" ") : title,
552
+ );
553
+ if (options.json) {
554
+ console.log(JSON.stringify(result, null, 2));
555
+ return;
556
+ }
557
+ logger.success(`Renamed session ${result.id}`);
558
+ logger.info(` ${result.title}`);
559
+ } catch (err) {
560
+ logger.error(`Failed: ${err.message}`);
561
+ process.exitCode = 1;
562
+ }
563
+ });
564
+
565
+ // session prune — retention sweep (gap-analysis 2026-07-11 P1 "保留期限").
566
+ session
567
+ .command("prune")
568
+ .description(
569
+ "Delete JSONL sessions whose last activity is older than a cutoff (newest N always kept)",
570
+ )
571
+ .requiredOption(
572
+ "--older-than <days>",
573
+ "Delete sessions idle for more than this many days",
574
+ )
575
+ .option("--keep <n>", "Always keep the newest N sessions (default 10)")
576
+ .option("--dry-run", "List what would be deleted without deleting")
577
+ .option("--json", "Output as JSON")
578
+ .action(async (options) => {
579
+ try {
580
+ const { pruneJsonlSessions } =
581
+ await import("../harness/jsonl-session-store.js");
582
+ const result = pruneJsonlSessions({
583
+ olderThanDays: Number(options.olderThan),
584
+ keep: options.keep != null ? Number(options.keep) : undefined,
585
+ dryRun: options.dryRun === true,
586
+ });
587
+ if (options.json) {
588
+ console.log(JSON.stringify(result, null, 2));
589
+ return;
590
+ }
591
+ const verb = result.dryRun ? "Would delete" : "Deleted";
592
+ logger.info(
593
+ `${verb} ${result.deleted.length} of ${result.scanned} session(s).`,
594
+ );
595
+ for (const id of result.deleted.slice(0, 50)) {
596
+ logger.info(` - ${id}`);
597
+ }
598
+ if (result.deleted.length > 50) {
599
+ logger.info(` … and ${result.deleted.length - 50} more`);
600
+ }
601
+ } catch (err) {
602
+ logger.error(`Failed: ${err.message}`);
603
+ process.exitCode = 1;
604
+ }
605
+ });
606
+
492
607
  session
493
608
  .command("migrate")
494
609
  .description("Migrate legacy JSON session files to JSONL")
@@ -594,6 +709,143 @@ export function registerSessionCommand(program) {
594
709
  }
595
710
  });
596
711
 
712
+ // session verify — transcript hash-chain integrity (tamper-evidence).
713
+ // Distinct from `validate` (structural: parseable lines + session_start):
714
+ // verify recomputes the per-event hash chain and flags edits / deletions /
715
+ // insertions / reordering of chained records.
716
+ session
717
+ .command("verify")
718
+ .description(
719
+ "Verify transcript hash-chain integrity (tamper-evidence); omit id to verify all",
720
+ )
721
+ .argument("[id]", "Session ID to verify (omit to verify every session)")
722
+ .option("--json", "Output as JSON")
723
+ .action(async (id, options) => {
724
+ try {
725
+ const store = await import("../harness/jsonl-session-store.js");
726
+ const result = id ? store.verifySession(id) : store.verifyAllSessions();
727
+
728
+ if (options.json) {
729
+ console.log(JSON.stringify(result, null, 2));
730
+ } else {
731
+ const results = Array.isArray(result) ? result : [result];
732
+ if (results.length === 0) {
733
+ logger.log("No sessions to verify.");
734
+ }
735
+ for (const item of results) {
736
+ const color =
737
+ item.status === "tampered"
738
+ ? chalk.red
739
+ : item.status === "verified"
740
+ ? chalk.green
741
+ : chalk.yellow;
742
+ const counts =
743
+ item.chainedEvents != null
744
+ ? ` (${item.chainedEvents} chained, ${item.legacyEvents} legacy${item.truncatedTail ? ", truncated tail" : ""})`
745
+ : "";
746
+ logger.log(`${color(item.status)} ${item.sessionId}${counts}`);
747
+ if (item.reason) {
748
+ const where = item.firstInvalidLine
749
+ ? ` at line ${item.firstInvalidLine}`
750
+ : "";
751
+ logger.log(` ${chalk.gray(item.reason + where)}`);
752
+ }
753
+ }
754
+ }
755
+
756
+ const anyTampered = (Array.isArray(result) ? result : [result]).some(
757
+ (item) => item.status === "tampered",
758
+ );
759
+ if (anyTampered) process.exit(1);
760
+ } catch (err) {
761
+ logger.error(`Failed: ${err.message}`);
762
+ process.exit(1);
763
+ }
764
+ });
765
+
766
+ // session index — SQLite index over the JSONL store (gap P2#14). Gives fast
767
+ // list/search without scanning every transcript, and can mirror sessions to a
768
+ // configured off-box target (fs/http). The index is a derived cache: safe to
769
+ // delete and rebuild from the JSONL at any time.
770
+ session
771
+ .command("index")
772
+ .description(
773
+ "Build/refresh the SQLite session index; search it, or mirror sessions off-box",
774
+ )
775
+ .argument("[query]", "Search the index for a term (omit to just sync)")
776
+ .option("--rebuild", "Force a full re-index (ignore mtime cache)")
777
+ .option("--stats", "Show index-wide counters")
778
+ .option("--mirror <ids...>", "Push session id(s) to the configured mirror")
779
+ .option(
780
+ "--limit <n>",
781
+ "Max rows for search/list",
782
+ (v) => parseInt(v, 10),
783
+ 20,
784
+ )
785
+ .option("--json", "Output as JSON")
786
+ .action(async (query, options) => {
787
+ let db;
788
+ try {
789
+ const idx = await import("../harness/session-index.js");
790
+ db = idx.openIndex();
791
+ const sync = idx.syncIndex(db, { force: Boolean(options.rebuild) });
792
+
793
+ if (options.mirror && options.mirror.length) {
794
+ const result = await mirrorSessions(options.mirror);
795
+ if (options.json) return console.log(JSON.stringify(result, null, 2));
796
+ for (const r of result.pushed)
797
+ logger.log(chalk.green(` mirrored ${r.id} (${r.bytes} bytes)`));
798
+ for (const r of result.errors) logger.error(` ${r.id}: ${r.error}`);
799
+ if (!result.target)
800
+ logger.log(
801
+ chalk.yellow(
802
+ "No mirror configured — set session.mirror.kind (fs|http) + target in config.",
803
+ ),
804
+ );
805
+ return;
806
+ }
807
+
808
+ if (options.stats) {
809
+ const st = idx.indexStats(db);
810
+ if (options.json) return console.log(JSON.stringify(st, null, 2));
811
+ logger.log(
812
+ `${st.sessions} sessions, ${st.messages} messages, ${st.events} events indexed`,
813
+ );
814
+ return;
815
+ }
816
+
817
+ if (query) {
818
+ const hits = idx.searchSessions(db, query, { limit: options.limit });
819
+ if (options.json) return console.log(JSON.stringify(hits, null, 2));
820
+ if (hits.length === 0) {
821
+ logger.log(chalk.gray(`No sessions match "${query}".`));
822
+ return;
823
+ }
824
+ for (const h of hits) {
825
+ logger.log(
826
+ `${chalk.cyan(h.id)} ${chalk.gray(h.title)} ${chalk.gray(h.updated_at)}`,
827
+ );
828
+ if (h.snippet) logger.log(` …${h.snippet}…`);
829
+ }
830
+ return;
831
+ }
832
+
833
+ if (options.json) return console.log(JSON.stringify(sync, null, 2));
834
+ logger.success(
835
+ `Index synced: ${sync.updated} updated, ${sync.removed} removed, ${sync.total} total.`,
836
+ );
837
+ } catch (err) {
838
+ logger.error(`Failed: ${err.message}`);
839
+ process.exit(1);
840
+ } finally {
841
+ try {
842
+ db?.close();
843
+ } catch {
844
+ /* best-effort */
845
+ }
846
+ }
847
+ });
848
+
597
849
  // session policy — Managed Agents parity Phase E1 approval policy
598
850
  session
599
851
  .command("policy")
@@ -822,14 +1074,106 @@ export function registerSessionCommand(program) {
822
1074
  // ---------------------------------------------------------------
823
1075
  // session usage [id] — Phase I: aggregate token usage
824
1076
  // ---------------------------------------------------------------
1077
+ const USAGE_BY_DIMENSIONS = [
1078
+ "origin",
1079
+ "skill",
1080
+ "subagent",
1081
+ "tool",
1082
+ "mcp",
1083
+ "model",
1084
+ ];
1085
+
1086
+ // Shared `--by` renderer for the per-session and global modes (both result
1087
+ // shapes carry the same `attribution` section). turnTokens is a turn-level
1088
+ // approximation — a turn's tokens count once per distinct tool, so the
1089
+ // column must not be summed across rows.
1090
+ const printUsageBy = (result, by) => {
1091
+ const a = result.attribution || {};
1092
+ const tokRow = (label, r) =>
1093
+ logger.log(
1094
+ ` ${chalk.gray(String(label).padEnd(28))} in=${r.inputTokens} out=${r.outputTokens} total=${chalk.cyan(r.totalTokens.toLocaleString())} calls=${r.calls}`,
1095
+ );
1096
+ const toolRow = (label, r) =>
1097
+ logger.log(
1098
+ ` ${chalk.gray(String(label).padEnd(28))} calls=${r.calls} errors=${r.errors} turnTokens≈${chalk.cyan((r.turnTokens || 0).toLocaleString())}`,
1099
+ );
1100
+ const empty = (what) => logger.log(chalk.gray(` (no ${what} recorded)`));
1101
+
1102
+ switch (by) {
1103
+ case "model": {
1104
+ const rows = result.byModel || [];
1105
+ if (rows.length === 0) return empty("token_usage events");
1106
+ for (const r of rows)
1107
+ tokRow(`${r.provider || "?"}/${r.model || "?"}`, r);
1108
+ return;
1109
+ }
1110
+ case "origin": {
1111
+ const rows = a.byOrigin || [];
1112
+ if (rows.length === 0) return empty("token_usage events");
1113
+ for (const r of rows) tokRow(r.origin, r);
1114
+ return;
1115
+ }
1116
+ case "skill": {
1117
+ const rows = a.bySkill || [];
1118
+ if (rows.length === 0)
1119
+ return empty("skill-attributed usage (isolated skill runs)");
1120
+ for (const r of rows) tokRow(r.skill, r);
1121
+ return;
1122
+ }
1123
+ case "subagent": {
1124
+ const rows = a.bySubagent || [];
1125
+ if (rows.length === 0) return empty("sub-agent-attributed usage");
1126
+ for (const r of rows)
1127
+ tokRow(`${r.subagentId}${r.role ? ` [${r.role}]` : ""}`, r);
1128
+ return;
1129
+ }
1130
+ case "tool": {
1131
+ const rows = a.tools?.byTool || [];
1132
+ if (rows.length === 0) return empty("tool_call events");
1133
+ for (const r of rows) toolRow(r.tool, r);
1134
+ logger.log(
1135
+ chalk.gray(
1136
+ " turnTokens = tokens of turns that used the tool (approximation; do not sum across rows)",
1137
+ ),
1138
+ );
1139
+ return;
1140
+ }
1141
+ case "mcp": {
1142
+ const rows = a.tools?.byMcpServer || [];
1143
+ if (rows.length === 0)
1144
+ return empty("MCP tool calls (mcp__<server>__*)");
1145
+ for (const r of rows) toolRow(r.server, r);
1146
+ logger.log(
1147
+ chalk.gray(
1148
+ " turnTokens = tokens of turns that used the server (approximation; do not sum across rows)",
1149
+ ),
1150
+ );
1151
+ return;
1152
+ }
1153
+ default:
1154
+ break;
1155
+ }
1156
+ };
1157
+
825
1158
  session
826
1159
  .command("usage")
827
1160
  .description("Aggregate token usage (per-session or global)")
828
1161
  .argument("[id]", "Session ID (omit for global rollup)")
829
1162
  .option("--json", "Output as JSON")
1163
+ .option(
1164
+ "--by <dimension>",
1165
+ `Breakdown dimension: ${USAGE_BY_DIMENSIONS.join("|")}`,
1166
+ )
830
1167
  .option("--limit <n>", "Max sessions for global rollup", "1000")
831
1168
  .action(async (id, options) => {
832
1169
  try {
1170
+ const by = options.by ? String(options.by).toLowerCase() : null;
1171
+ if (by && !USAGE_BY_DIMENSIONS.includes(by)) {
1172
+ logger.error(
1173
+ `Invalid --by "${options.by}" — expected one of: ${USAGE_BY_DIMENSIONS.join(", ")}`,
1174
+ );
1175
+ process.exit(1);
1176
+ }
833
1177
  const { sessionUsage, allSessionsUsage } =
834
1178
  await import("../lib/session-usage.js");
835
1179
  const result = id
@@ -841,6 +1185,22 @@ export function registerSessionCommand(program) {
841
1185
  return;
842
1186
  }
843
1187
 
1188
+ if (by) {
1189
+ const t = result.total;
1190
+ logger.log(
1191
+ chalk.bold(
1192
+ id
1193
+ ? `Session ${chalk.gray(id.slice(0, 16))} — by ${by}`
1194
+ : `Global usage — by ${by}`,
1195
+ ),
1196
+ );
1197
+ logger.log(
1198
+ ` total: ${chalk.cyan(t.totalTokens.toLocaleString())} tokens in=${t.inputTokens} out=${t.outputTokens} calls=${t.calls}`,
1199
+ );
1200
+ printUsageBy(result, by);
1201
+ return;
1202
+ }
1203
+
844
1204
  if (id) {
845
1205
  logger.log(chalk.bold(`Session ${chalk.gray(id.slice(0, 16))}`));
846
1206
  const t = result.total;
@@ -1194,6 +1554,42 @@ export function registerSessionCommand(program) {
1194
1554
  });
1195
1555
  }
1196
1556
 
1557
+ /**
1558
+ * Push the given session ids to the configured off-box mirror (gap P2#14).
1559
+ * Mirror config: session.mirror = { kind: "fs"|"http", dir|baseUrl, token? }.
1560
+ * Returns { target, pushed, errors }; target null when no mirror is configured.
1561
+ */
1562
+ async function mirrorSessions(ids) {
1563
+ const { createMirror } = await import("../harness/session-mirror.js");
1564
+ const store = await import("../harness/jsonl-session-store.js");
1565
+ let cfg = {};
1566
+ try {
1567
+ cfg =
1568
+ (await import("../lib/config-manager.js")).loadConfig()?.session
1569
+ ?.mirror || {};
1570
+ } catch {
1571
+ cfg = {};
1572
+ }
1573
+ const mirror = createMirror(cfg);
1574
+ if (!mirror) return { target: null, pushed: [], errors: [] };
1575
+
1576
+ const pushed = [];
1577
+ const errors = [];
1578
+ const { readFileSync, existsSync } = await import("node:fs");
1579
+ for (const id of ids) {
1580
+ try {
1581
+ if (store.isUnsafeSessionId(id)) throw new Error("unsafe session id");
1582
+ const file = store.sessionPath(id);
1583
+ if (!existsSync(file)) throw new Error("session not found");
1584
+ const bytes = readFileSync(file, "utf-8");
1585
+ pushed.push(await mirror.push(id, bytes));
1586
+ } catch (err) {
1587
+ errors.push({ id, error: err.message });
1588
+ }
1589
+ }
1590
+ return { target: mirror.target, pushed, errors };
1591
+ }
1592
+
1197
1593
  // === Iter21 V2 governance overlay ===
1198
1594
  export function registerSesgovV2Commands(program) {
1199
1595
  const parent = program.commands.find((c) => c.name() === "session");
@@ -3,7 +3,7 @@
3
3
  "releases": [
4
4
  {
5
5
  "productVersion": "Unreleased",
6
- "cliVersion": "0.162.158",
6
+ "cliVersion": "0.162.160",
7
7
  "cliVersions": [
8
8
  "0.4.46",
9
9
  "0.4.51",
@@ -16,11 +16,21 @@
16
16
  "0.162.155",
17
17
  "0.162.156",
18
18
  "0.162.157",
19
- "0.162.158"
19
+ "0.162.158",
20
+ "0.162.159",
21
+ "0.162.160"
20
22
  ],
21
23
  "date": null,
22
24
  "title": "",
23
25
  "sections": [
26
+ {
27
+ "heading": "Added — cc CLI 0.162.160:运行时安全与确定性 8 批(沙箱严格模式 + 依赖/凭据安全 + 确定性 Headless + Subagent 契约 + MCP 生命周期 + Hooks 硬化)(CLI-only npm 发版)",
28
+ "body": "> `chainlesschain` 0.162.159 → **0.162.160** 发 npm `latest`(经 `npm-publish.yml`,`--provenance --access public`)。纯 `packages/cli/src` 增量;未触 `pdh/lib` → 无 Android cc bundle rollover / 无 USR_VERSION 改动。命令面**只加子命令**(`cc session rename/prune`、`cc daemon rm`、`cc mcp trust-project`),**顶层命令数 175 不变**(manifest 无需重生)。对照 `docs/CLAUDE_CODE_CLI_GAP_ANALYSIS.md`(docs 根)审计确认仍缺的 8 项全落地(commit 链 `c4533ad848..4c2e297754`)。\n>\n> ⚠️ **行为变更(升级前须知)**:① `run_code` 的 pip 依赖**默认不再自动安装**(改 opt-in,见下);② agent 脚本默认写 OS 临时目录、不再落项目根(`persist:true` 才进项目);③ headless/agent **退出码细化**——最大轮次→3、预算超限→4、模型错误→5、配置错误→6(成功仍 0、通用失败仍 1、hook block 仍 2、SIGINT/SIGTERM 仍 130/143)。依赖固定 exit≠0 判失败的脚本不受影响;依赖\"失败必为 1\"的脚本需放宽。\n\n- **确定性 Headless(A)**:新增 `--ephemeral`(resume 只读回放、零落盘);init 事件补 `protocol_version`/`session_persistence`/`loaded_sources`/`policy_digest`/`tools_hash`/`isolation_level`(消费者可判定回放确定性与工具/策略指纹);退出码分类(`lib/exit-codes.cjs`);`cc agent --capabilities` 打印机读能力清单(协议版本、agent 工具集、权限模式、退出码、特性开关)后退出。\n- **依赖安装安全(B)**:`run_code` 的 pip 自动安装**默认关闭**——经 settings `runCode.autoInstall` 或 `CC_RUN_CODE_AUTO_INSTALL` 显式开启,且受 `runCode.installAllowlist`(PEP 503 归一化包名)约束,每次安装写审计 JSONL(`~/.chainlesschain/audit/`,`CC_AUDIT_DIR` 可覆盖);agent 脚本默认写 OS 临时目录(`persist:true` 才进项目根)。系统提示词/工具契约文案已同步。\n- **凭据卫生(C)**:config `llm.apiKeyHelper`——从外部命令(→ OS 凭据库)解析 API Key,5 分钟缓存,helper 失败 **fail-closed 并告警一次**(返回 null,绝不静默替换成别的凭据);argv `--api-key` 明文传参现给出告警(进程表/shell 历史泄露面)。\n- **沙箱严格模式(D)**:`failIfUnavailable` 此前只是声明、从不执行——现在启动期探测沙箱引擎(docker `version` / bwrap `--version`),不可用即拒绝启动(退出码 6),headless 与交互双路均生效;`isolation_level`(os-sandbox / container / policy-only)透出到 init 事件与 `sandboxSummary`,不再静默降级为\"只有工具规则\"。\n- **Subagent 契约(E)**:`spawn_sub_agent` 与 `.claude/agents/*.md` frontmatter 双入口新增 `disallowedTools`(从 allowedTools 减去)、`maxTurns`、`isolation:worktree`。**顺手修出一个真 bug**:`SubAgentContext._runCore` 早已算出工具白名单却从未传给 agentLoop——子代理在 LLM 面一直看到全量工具,白名单形同虚设(修=`enabledToolNames` 透传)。\n- **会话运营(F)**:`cc session rename <id> <title>`(append `session_rename` 事件,哈希链不破)+ `cc session prune --older-than <days> [--keep N] [--dry-run]`(按最后活动保留期清理,默认保最新 10)+ `cc daemon rm <id> [--force] [--keep-log]`(终态直接移除、运行中需 `--force`)。\n- **MCP 生命周期(G)**:mcp-client 处理 `notifications/tools/list_changed` + `notifications/resources/list_changed`——合并式重拉服务器工具/资源清单(刻意**不动 in-flight LLM 工具数组**,保 tool-search 缓存稳定性,追加式更新);项目 `.mcp.json` 首用记 sha256 指纹,文件变更后 **fail-closed 拒绝加载**,需 `CC_PROJECT_MCP_TRUST=1` 或 `cc mcp trust-project` 显式重信任(防被换过的项目配置静默拉起新 MCP server)。\n- **Hooks 硬化(H)**:每个 hook 的 stdin payload 打 `schema_version`(=1,additive 变更保号、破坏性 reshape 才 bump);连续失败熔断——同一 hook 命令连续 3 次非阻塞失败(spawn error / 超时 / 非 2 非零退出)开闸,60s 冷却后半开试跑一次,成功或真 block/ask 决策关闸(`CC_HOOK_BREAKER_THRESHOLD=0` 关闭、`CC_HOOK_BREAKER_COOLDOWN_MS` 可调);坏 hook 不再每轮拖慢 agent。"
29
+ },
30
+ {
31
+ "heading": "Added — cc CLI 0.162.159:Claude-Code 平价 gap-analysis 14 项收口(cloud handoff + /tui + /voice + /fast + plugin 治理 + session SQLite 索引)(CLI-only npm 发版)",
32
+ "body": "> `chainlesschain` 0.162.158 → **0.162.159** 发 npm `latest`(经 `npm-publish.yml`,`--provenance --access public`)。纯 `packages/cli/src` 增量;未触 `pdh/lib` → 无 Android cc bundle rollover / 无 USR_VERSION 改动。顶层命令数 **173 → 175**(新增 `cc cloud` + `cc routine`;`cc session index` 是 `session` 子命令、`/tui` `/voice` `/fast` 为 REPL-only 均不计数)。对照 `docs/internal/cli-claude-code-gap-analysis-2026-07-11.md` 14 项全落地。\n\n- **`cc cloud`(P1#7)自托管 cloud handoff**:`cc cloud run/status/attach/list`——`git bundle`(base64)当前分支上传到配置的私有 runner(`cloud.baseUrl`/`cloud.token` 或 `CC_CLOUD_URL`/`CC_CLOUD_TOKEN`),runner 完成后 `cc cloud attach` 轮询到终态并 `git apply --3way` 回流 patch、落 plan/artifacts、显示 PR——无 Anthropic 云依赖。本地 job 台账 `~/.chainlesschain/cloud-jobs/`(穿越守卫),不落地的 patch 存旁不失败。\n- **`/tui`(P1#10)全屏无闪烁视图**:`/tui fullscreen` 进备用屏缓冲(`?1049h`,保滚动历史)+ 尾锚定视口窗口化 + diff 重绘(仅改动行光标寻址重写,无整屏清屏→无闪烁);`/tui default` 退回流式滚动;`CC_NO_FLICKER=1` 启动即全屏;屏幕阅读器模式强制 default。持久化 `cli.tuiMode`。\n- **`/voice`(P2#11)语音听写**:`/voice hold|tap|off`(hold=按住说话,tap=切换);严格 local-first STT 优先级(本地 Whisper > 系统 STT > 云,云需 `voice.allowCloud` 显式开);SSH/无 TTY/CI/`CC_NO_VOICE` 环境清晰降级不假装监听;真实采集是主机绑定 DI seam。\n- **`/fast`(P2#12)延迟档**:`/fast on|off|status`——下一轮最小化推理(thinking off)+ 除非用户 `/model` 钉过模型否则换到 provider 的低延迟模型(anthropic→haiku-4-5、openai→4o-mini、deepseek/dashscope/mistral/gemini),始终显示成本/质量权衡。持久化 `cli.fastMode`。\n- **插件治理(P2#13)三项**:manifest `dependencies`(插件名→semver 区间 + 保留 `host`/`cc` 约束 CLI 版本)经 `cc plugin validate` 校验缺失/越界依赖;LSP 注册表改**每 languageId 有序候选表**——一个插件 LSP server 二进制缺失不再遮蔽 builtin(回退到下一候选/内建,扩展名不再\"变黑\");`isPluginInUse` 把 LSP/MCP/hooks/monitors 被动组件视为 in-use,不再被\"未调用即弃用\"误判。\n- **`cc session index`(P2#14)外部 session 存储**:JSONL 仍是权威源,新增 SQLite 索引(`~/.chainlesschain/session-index.db`)——增量按 mtime+size 同步 + 内容/标题搜索(LIKE 转义通配符)+ stats + `--rebuild`,取代逐文件解析(1072 真 session 验过);可插拔 off-box mirror(`fs`/`http` 驱动,S3/WebDAV/Redis 同接口待配),`session.mirror.{kind,dir|baseUrl,token}` 配置,加密/访问控制由驱动独立负责。\n- **P0/P1 早前提交同随本版发出**:`cc bg view` 交互式 agent 面板(P0#1)+ background supervisor 稳定性加固(P0#2)+ `/doctor` 可修复 checkup + `/checkup` 别名(P0#3)+ transcript hash-chain 防篡改 `cc session verify`(P0#4)+ Inbound Channels(webhook/telegram allowlist,P0#5)+ computer-use MCP 显式开启(P1#6)+ Routines(cron/once/webhook/github,P1#8)+ PR/session linking(P1#9)。"
33
+ },
24
34
  {
25
35
  "heading": "Added — cc CLI 0.162.158:browser_state agent 工具 + REPL /remote-control + web-panel 直连配对/审批卡片 + 交付物预览下载(CLI-only npm 发版)",
26
36
  "body": "> `chainlesschain` 0.162.157 → **0.162.158** 发 npm `latest`(经 `npm-publish.yml`,`--provenance --access public`)。纯 `packages/cli/src` + web-panel dist 随包;未触 `pdh/lib` → 无 Android cc bundle rollover / 无 USR_VERSION 改动。命令数不变(**173**);AGENT_TOOLS **24 → 25**(新增 `browser_state`)。\n\n- **`browser_state` 一等 agent 工具(IDE gap P1 #8 收口)**:Chrome CDP 连接器(`cc browse chrome`)暴露为第 25 个 agent 工具——URL/标题/标签页 + 观察窗口 console/失败网络请求 + DOM 快照(agent 面 40k cap,CLI 命令面 150k)+ 可选截图(**生成的**临时路径,防 agent 自选路径经读工具覆盖任意文件);LOW/READ/plan 模式可用,纯观察不点击不输入不导航,仅 loopback CDP。\n- **REPL `/remote-control`(别名 `/rc`)on|off|status(批 26)**:交互式会话内起自托管 WS + RemoteApprovalBridge 配对(URI + 可选二维码),TAB 补全 + /help;`cc agent --remote-control` 对交互式会话同样生效(启动失败降级 local-only 不中断会话)。**交互式远程审批赛跑语义**(新 `src/repl/remote-approval.js`):两个交互 confirmer(ApprovalGate + settings/hook ask)把终端 prompt 与已配对设备赛跑,先答先赢;与 headless 刻意不同——远端超时/断开**不是决定**(静默的手机永不代键盘用户 auto-deny),设备先答则取消本地 readline 并压掉其搁浅的 idle-timeout。\n- **web-panel 直连(direct-LAN)配对 + `permission.request` 审批卡片(批 28)**:remoteSession store 自动识别双 URI 方案——relay E2EE 路径不变,新增 `chainlesschain://remote-control/pair#` 直连 transport(browser 版 parseDirectPairingUri + 客户端过期检查);直连控制走 remote-session-publish **顶层 commandId+seq**(applyControlIdempotent direct-client 契约);一次性 pairing token → 直连断开**刻意不自动重连**(重 join 必失败,诚实 disconnected 态 + 重配对提示);两种 transport 喂同一 pendingApprovals 卡片(requestId 去重 + permission.resolved 任意裁决方清除 + 首等公民审批卡列表批准/拒绝)。\n- **交付物 Markdown 预览 + 浏览器下载(批 25)**:面板 text/markdown 预览走既有 MarkdownRenderer(marked+DOMPurify+hljs,零新依赖);新 `GET /api/artifacts/<id>/download` 流式下载完整存档副本(WS 预览仍 cap)——Content-Disposition(RFC 5987)+ no-store/nosniff + 配置 wsToken 时同 WS 强度 token 门(sha256+timingSafeEqual,Bearer 头不进 URL)+ basename 防篡改守卫。\n- **安全修复 ×2**:① **直连配对信任边界**——直连(LAN)配对无密码学主机身份(区别 relay E2EE),钓鱼配对 URI 可把面板指向任意公网 ws:// 端点导走 serverToken/prompt/审批决定;两端 parser(web-panel + CLI 孪生)现拒绝指向非私网主机的明文 ws://(仅 loopback/RFC-1918/link-local/ULA,即 pickLanAddress 可能产出的全集;wss:// 不限),IPv6 括号 + IPv4-mapped 点分/hex 双形态识别,RED 反证过。② **chrome-connector launch-arg 加固**——launch URL 曾逐字进 Chrome argv(`--` 开头值被 Chrome 当开关解析=潜在命令执行面),现限 http(s)://、about:(URL parse 校验字节原样透传)+ CDP 端口处处整型边界校验。\n- **健壮性**:web-panel 审批卡发送失败恢复——approve() 乐观清除后若答复未离开本端(直连 publish 拒绝/socket 断/relay 未配对)恢复卡片并浮错,可重新裁决;失败窗口内别设备已 resolved 则抑制恢复(无僵尸卡)。RED 反证 + web-panel 全套 2512 绿。\n- **测试稳健性(仓库面,不进 npm 包)**:checkpoint-store `vi.setConfig 30s` + web-panel doctor 90s 两处负载敏感预算加固(trap #31 形态);ai-doc-creator 集成测试改 chdir tmpDir,不再向仓库根泄漏 12 个产物文件。"