chainlesschain 0.162.163 → 0.162.164

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 (213) hide show
  1. package/package.json +3 -1
  2. package/src/assets/web-panel/assets/{AIOps-BQ0kgfA1.js → AIOps-BqRkaHGb.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-Dr1MDANe.js → ActionButton-DWO7OZLC.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-CZ5vsaXW.js → Analytics-C5oaVezM.js} +2 -2
  5. package/src/assets/web-panel/assets/{AppLayout-BIBga881.js → AppLayout-CsUESDoZ.js} +3 -3
  6. package/src/assets/web-panel/assets/{Artifacts-vpXHRU3u.js → Artifacts-Dragqu5k.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-B8exuoub.js → Audit-CQsaHctY.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-923evRRN.js → BackgroundAgents-DdG1jNSM.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-sr_sswlQ.js → Backup-DN6sjK4z.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-CnW-4XKT.js → BaseInput-Cxm_ZTCl.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-FZCsTZIL.js → Chat-DVXGmA56.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DDPd9qT0.js → ChatBubbleRenderer-BdaCL_iE.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-DFrxB59H.js → Checkbox-DEc4vD_A.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-_b6WEel8.js → Codegen-CKiTcv36.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-CkbnnZ-K.js → Col-Br9UUNN0.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-BxM_82zW.js → Community-NeUH_70T.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-DYinP45R.js → Compact-BqR-z2Df.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-DyXp3sSP.js → Compliance-DcWB_SgZ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-BEr2DhW7.js → Cowork-BfhgcudF.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-B-Vwo1hN.js → Cron-LaU29LgD.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-DbLxKddd.js → Crosschain-B2ZB9uwW.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BsQ_LFw9.js → DID-BuHl38A2.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-lVlXQHu5.js → Dashboard-m627yqvZ.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-Beo_foO7.js → Dropdown-C7bxF8F5.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-DszHIXED.js → EmailListRenderer-DPLPQzLp.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DcAwoh81.js → FamilyGuardDashboard-cOiH6ike.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-CuJLa2eb.js → Federation-Bvkzg88z.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-Cxd1lxhc.js → FormItemContext-BTsiA7h_.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BBvq5Jsw.js → GenericCardRenderer-BGnVE5qu.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-WqPKetx6.js → Git-DS_j6r25.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CWYtY3ke.js → Governance-CKRG3Rsl.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-BnX1iiNV.js → Inference-DW8INoNQ.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-BJuM8GN5.js → KnowledgeGraph-BhEYeFhC.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-DqLMNntm.js → Logs-wqi0uxuR.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BqYwEYou.js → MarkdownRenderer-DS8dE8ty.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-_vGfmhNl.js → Marketplace-CMlTuPLT.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-BeJDOkDj.js → McpTools-CSx4m5JJ.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-Da0EEZDO.js → Memory-vnQCUy-_.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-DSuzcCCy.js → MobileBridge-BV2NA742.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-Bw0k8e1B.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-BE9YgdA0.js → Mtc-CzK_FPB1.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-fC2BpO5S.js → MtcAudit-Cb7G9oL3.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-D24Xxqw3.js → Multisig-qJxlUyzr.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-Bs3oGvS8.js → NLProgramming-DqaBJ7Te.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-BFjmAA-3.js → Notes-CwhTJkRi.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-BX_9Bhv0.js → NotificationSettings-YDs4Fal4.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-wjkZwJXI.js → OrderTableRenderer-CvEALpq7.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-DBcdsVEP.js → Organization-CEmQm9n8.js} +2 -2
  49. package/src/assets/web-panel/assets/{Overflow-DDyaF9vC.js → Overflow-zby_P_LA.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-BB78L4he.js → P2P-DsmDNFNT.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-DqTiZFum.js → PdhVaultBrowser-FWwFlZGF.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-INbnWv4j.js → Permissions-DuzQ6me5.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-BST-VmTq.js → PersonalDataHub-DZbeN613.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-CE7_tS2k.js → Pipeline-CoNSVAUS.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-hK5L8dOB.js → Privacy-OYJEAaMC.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-BvkkUoDn.js → ProjectInit-CWIapV--.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-u7_EUtkm.js → ProjectSettings-CX8xMxZn.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-5Fmt_8Nw.js → Projects-BLCN8YyY.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-B9eanarR.js → Providers-BV_sxp75.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-C0dX5N2f.js → QrScannerModal-BTnRrHRp.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-CKTpG-PZ.js → QuickAsk-D1KZg018.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-Px_w_XQM.js → Recommend-COm5cuNg.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-Di0fnYkM.js → RemoteSession-0saWEkYI.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-vesnWjXt.js → Reputation-Dr7iKLS2.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-exbY6LkT.js → Row-C1vpPBcr.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-B3O3x70t.js → RssFeed-CxlWrtNj.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-uRA0DhNz.js → Search-BT2V8dPN.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-CViqdH6K.js → Security-bm9V3c5k.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-CkXS97WN.js → Services-CocH1yyX.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-DxW5WYnE.js → Skeleton-DHItlMdd.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-CLxmGFse.js → Skills-C59sLc4w.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-Cr5fziXq.js → Sla-Crj4BMc8.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-H0JSb3cp.js → SpeechSettings-DX4KFceY.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-DMYWMae7.js → SyncSettings-BD_iwQLr.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-CbYyDdF4.js → Tasks-BmCTzYsH.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-Cpc8JjiK.js → Templates-BGUaxZYl.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-CRh77Jda.js → Tenant-BS0tJx2O.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-4D8tNBO5.js → Terminal--tHSAsld.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-D41jzzbG.js → TimelineRenderer-D9-MFxS4.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-C_jWEy6b.js → Tokens-CiCB485f.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-Dk1ZUhsy.js → Trigger-SmCFuW_l.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-CLVy4l7m.js → Trust-DkK3Rqe0.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-C-FeqFDj.js → UkeySign-CDM4LGdB.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-CqVZzyMm.js → VideoEditing-IDB7DJXt.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-DX9BW9pu.js → Wallet-Bikj5cmt.js} +2 -2
  86. package/src/assets/web-panel/assets/{WebAuthn-7hQDpWw_.js → WebAuthn-C9abWlAw.js} +2 -2
  87. package/src/assets/web-panel/assets/{WorkflowEditor-6XqUAO0j.js → WorkflowEditor-DrbRKVky.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-5R_vkaJG.js → chat-CccCSDQf.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-huUPZdC-.js → colors-CXObXLhb.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-CfJlgisE.js → compact-item-B5kfYAxC.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-jO8heofr.js → createContext-DmiPBfip.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-CvKrADNa.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DY8Vxqx7.js → hasIn-Q6lYteAc.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BtEXSoPL.js → index-25Wlqps4.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BsBtRWbA.js → index-33U29UNV.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BQS-uPOK.js → index-4oXBDczR.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DXBcvUnf.js → index-BGYXvMc5.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-C2iVSFpy.js → index-BJCCa0s_.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-A48PtXw9.js → index-BJJ5DhKI.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Db1cLWir.js → index-BLOzViJ6.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-vXs3B8GJ.js → index-BRrbUMoG.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BHE8OIls.js → index-BUFpT17Q.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BmqGerpC.js → index-BkRDiqGc.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CzKmteNS.js → index-BsfvW6my.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BAON7TzU.js → index-By-kfPED.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DoI2sZm7.js → index-BzFTcQsc.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-j3O2EEJe.js → index-C2FXdboq.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CRElRhyS.js → index-C5XOlTHN.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-KRHroryI.js → index-CLhBDS2w.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Bxax9vQ6.js → index-CMq7UeoT.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BfCx4pFT.js → index-CVMTWNKl.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CA6sO5VD.js → index-Caghi-yL.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CA90TZxG.js → index-CdB6FuSH.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Cbe9G0V4.js → index-CoK8-kWQ.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-D0pet5Uy.js → index-CsO3GUAY.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-ChvHubX3.js → index-CwZBa20Z.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BRXOP09X.js → index-D-kASucw.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-B74OXFxC.js → index-D8eBw7vW.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-D1VLuww0.js → index-D9wgifk4.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-p85YaaKw.js → index-DAYCTc07.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BliaT02i.js → index-DF6Q8l2h.js} +1 -1
  122. package/src/assets/web-panel/assets/index-DTdFUWc_.js +1 -0
  123. package/src/assets/web-panel/assets/{index-Cjxbu68P.js → index-DgTLApS9.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Cis_Ip7H.js → index-DgxVJ8pg.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-v3pTaEQm.js → index-DlJvIqoR.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BYLBPpt8.js → index-Dw4eHfsG.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-B300RPnT.js → index-Rh-ytwow.js} +3 -3
  128. package/src/assets/web-panel/assets/{index-CLhBh-qE.js → index-StAEt2pq.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-d1xkbtNz.js → index-bzfw951k.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BCqxyvqp.js → index-fSUMbMvv.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-dKx57io7.js → index-tFHy5Wol.js} +1 -1
  132. package/src/assets/web-panel/assets/index-zQ0M_kz-.js +1 -0
  133. package/src/assets/web-panel/assets/{initDefaultProps-MCB7-raZ.js → initDefaultProps-BNq7X72N.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-UWDF9swV.js → motion-DOf9viTc.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-CsTmgXZU.js → move-CRHTcW4x.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-fQy6KGLg.js → mtc-parser-Cyoozgm2.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-woPnEjd1.js → omit-6-TjFNru.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-Bs2dZ1gf.js → pickAttrs-DlUmO1zi.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-C7EVgPkg.js → placementArrow-CFOL8mIR.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-BH2kYCTN.js → responsiveObserve-BtaFK5zd.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-BFBP2V_e.js → slide-BIbc2bur.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-xdDxDYse.js → statusUtils-BXX9d6-w.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-aAWgPPQf.js → styleChecker-BGv10Bpn.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-DkGSAWoO.js → useFlexGapSupport-bKnKT8tw.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-BrOih40N.js → useFs-FY4IL4jn.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-eIYrgMqO.js → usePersonalDataHub-Bwl9TEgu.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-B8lutNSE.js → vnode-BM3OHxAT.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-CffNre7V.js → zoom-D1fbFiHB.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agenda.js +71 -14
  151. package/src/commands/doctor.js +85 -1
  152. package/src/commands/plugin.js +226 -9
  153. package/src/commands/review.js +17 -1
  154. package/src/commands/session.js +191 -0
  155. package/src/commands/team.js +19 -1
  156. package/src/data/changelog.json +5 -4
  157. package/src/gateways/ws/session-protocol.js +50 -0
  158. package/src/gateways/ws/ws-agent-handler.js +50 -0
  159. package/src/harness/jsonl-session-store.js +60 -0
  160. package/src/harness/worktree-isolator.js +140 -5
  161. package/src/lib/agent-schedule-store.js +30 -1
  162. package/src/lib/agent-team/team-worktree.js +37 -2
  163. package/src/lib/agent-tool-admission.js +125 -0
  164. package/src/lib/app-preview.js +360 -0
  165. package/src/lib/async-hook-supervisor.cjs +54 -0
  166. package/src/lib/capability-manifest.js +319 -0
  167. package/src/lib/capability-negotiation.js +23 -12
  168. package/src/lib/diagnostic-bundle.js +230 -0
  169. package/src/lib/doctor-checkup.js +223 -4
  170. package/src/lib/execution-location.js +286 -0
  171. package/src/lib/headless-manifest.js +13 -40
  172. package/src/lib/hook-stats-store.cjs +197 -0
  173. package/src/lib/idempotency.js +109 -0
  174. package/src/lib/jsonl-session-store.js +1 -0
  175. package/src/lib/lsp/__tests__/lsp-manager.test.js +58 -0
  176. package/src/lib/lsp/lsp-manager.js +56 -0
  177. package/src/lib/monitor-event.js +290 -0
  178. package/src/lib/operation-fingerprint.js +217 -0
  179. package/src/lib/plugin-runtime/bin.js +37 -0
  180. package/src/lib/plugin-runtime/capabilities.js +19 -0
  181. package/src/lib/plugin-runtime/capability-consent.js +28 -0
  182. package/src/lib/plugin-runtime/hooks.js +37 -0
  183. package/src/lib/plugin-runtime/lsp.js +38 -0
  184. package/src/lib/plugin-runtime/mcp.js +40 -0
  185. package/src/lib/plugin-runtime/monitors.js +37 -0
  186. package/src/lib/plugin-runtime/plugin-options.js +151 -0
  187. package/src/lib/pr-automation-policy.js +224 -0
  188. package/src/lib/remote-approval-bridge.js +66 -13
  189. package/src/lib/repl-rewind.js +101 -0
  190. package/src/lib/review-comment-anchor.js +241 -0
  191. package/src/lib/review-pipeline.js +46 -7
  192. package/src/lib/runtime-checkup.js +221 -0
  193. package/src/lib/secret-scan.js +409 -0
  194. package/src/lib/session-branch.js +0 -0
  195. package/src/lib/side-effect-ledger.js +105 -0
  196. package/src/lib/skill-loader.js +32 -0
  197. package/src/lib/skill-path-scope.js +63 -0
  198. package/src/lib/sub-agent-context.js +36 -4
  199. package/src/lib/sub-agent-registry.js +19 -0
  200. package/src/lib/telemetry/span-recorder.js +17 -4
  201. package/src/lib/terminal-context.js +195 -0
  202. package/src/lib/unattended-action-policy.js +182 -0
  203. package/src/lib/worktree-cleanup-safety.js +138 -0
  204. package/src/lib/worktree-isolator.js +8 -0
  205. package/src/lib/worktree-sparse.js +131 -0
  206. package/src/repl/agent-repl.js +152 -38
  207. package/src/runtime/agent-core.js +191 -13
  208. package/src/runtime/diagnostics.js +60 -0
  209. package/src/runtime/headless-runner.js +126 -1
  210. package/src/assets/web-panel/assets/MobileProjects-D0RAk4e8.js +0 -1
  211. package/src/assets/web-panel/assets/devWarning-DeIrCfH-.js +0 -1
  212. package/src/assets/web-panel/assets/index-BxUKMRpP.js +0 -1
  213. package/src/assets/web-panel/assets/index-DscU9iPI.js +0 -1
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Canonical capability manifest — the single source the CLI capability output,
3
+ * the VS Code / JetBrains negotiation, the protocol docs, the compatibility
4
+ * fixture, the behavior matrix and the release-notes capability diff all project
5
+ * from (P1-9 "Capability Manifest 与脱敏诊断包" of
6
+ * CLAUDE_CODE_IDE_INCREMENTAL_GAP_ANALYSIS_2026-07-13.md).
7
+ *
8
+ * Today the wire-protocol capability surface is hand-authored in THREE places
9
+ * that can silently drift:
10
+ * - [[capability-negotiation.js]]: `PROTOCOL_FEATURES`, `FEATURE_MIN_VERSION`,
11
+ * and the private `FEATURE_TO_FIELD` map.
12
+ * - [[headless-manifest.js]]: `buildAgentCapabilities().features` (the
13
+ * `cc agent --capabilities` output).
14
+ * - the Java twin + the shared negotiation fixture.
15
+ * A new v2 field added to one list but not the others parses wrong on the peer
16
+ * that missed it. This module is the ONE definition; every other shape is a pure
17
+ * projection of it, and a drift-guard test asserts the live hand-authored
18
+ * constants still equal the projection (see capability-manifest.test.js).
19
+ *
20
+ * Pure: no fs / process / clock. Projections are deterministic so a caller can
21
+ * regenerate docs/fixtures and diff them against the checked-in copies in CI.
22
+ */
23
+
24
+ import { createHash } from "node:crypto";
25
+
26
+ /**
27
+ * THE canonical manifest. `wireFeatures` is the negotiable additive per-line
28
+ * field set (order mirrors capability-negotiation.js `PROTOCOL_FEATURES`);
29
+ * `runtimeFeatures` are the boolean capability flags that change what the CLI
30
+ * can DO (not the shape of a line the client must parse) and are NOT negotiated.
31
+ */
32
+ export const CAPABILITY_MANIFEST = Object.freeze({
33
+ name: "chainlesschain-cli",
34
+ protocolVersion: 1,
35
+ minProtocolVersion: 1,
36
+
37
+ wireFeatures: Object.freeze([
38
+ Object.freeze({
39
+ key: "event_seq",
40
+ field: "seq",
41
+ minVersion: 1,
42
+ description:
43
+ "every stream-json stdout line carries a monotonic per-run `seq`",
44
+ }),
45
+ Object.freeze({
46
+ key: "tool_use_id",
47
+ field: "tool_use_id",
48
+ minVersion: 1,
49
+ description:
50
+ 'tool_use / tool_result lines carry a pairing `id` ("tu-<n>")',
51
+ }),
52
+ Object.freeze({
53
+ key: "trace_id",
54
+ field: "trace_id",
55
+ minVersion: 1,
56
+ description:
57
+ "each line carries a run-scoped, caller-injectable `trace_id` for IDE ↔ CLI correlation",
58
+ }),
59
+ ]),
60
+
61
+ // Boolean capability flags (buildAgentCapabilities().features truthy keys).
62
+ runtimeFeatures: Object.freeze([
63
+ "bare",
64
+ "safe_mode",
65
+ "ephemeral",
66
+ "background",
67
+ "worktree",
68
+ "checkpoint",
69
+ "json_schema_output",
70
+ "partial_messages",
71
+ "interactive_approvals",
72
+ "permission_prompt_tool",
73
+ "remote_control",
74
+ ]),
75
+
76
+ mcpFeatures: Object.freeze([
77
+ "config_file",
78
+ "registry",
79
+ "tool_search",
80
+ "oauth",
81
+ "managed_allowlist",
82
+ ]),
83
+ sandboxEngines: Object.freeze(["docker", "bubblewrap"]),
84
+
85
+ permissionModes: Object.freeze([
86
+ "default",
87
+ "manual",
88
+ "auto",
89
+ "dontAsk",
90
+ "plan",
91
+ "acceptEdits",
92
+ "bypassPermissions",
93
+ ]),
94
+ outputFormats: Object.freeze(["text", "json", "stream-json"]),
95
+ inputFormats: Object.freeze(["text", "stream-json"]),
96
+ });
97
+
98
+ const manifestOr = (m) =>
99
+ m && typeof m === "object" ? m : CAPABILITY_MANIFEST;
100
+ const wireFeaturesOf = (m) =>
101
+ Array.isArray(manifestOr(m).wireFeatures) ? manifestOr(m).wireFeatures : [];
102
+
103
+ /**
104
+ * The negotiable wire feature keys, in canonical declared order — the value
105
+ * capability-negotiation.js `PROTOCOL_FEATURES` must equal.
106
+ * @returns {string[]}
107
+ */
108
+ export function toProtocolFeatures(m = CAPABILITY_MANIFEST) {
109
+ return wireFeaturesOf(m).map((f) => f.key);
110
+ }
111
+
112
+ /**
113
+ * Feature → minimum protocol version, ONLY for features gated above the base
114
+ * version (a v1-additive field is absent, matching an empty
115
+ * `FEATURE_MIN_VERSION` today). Adding a v2 feature here flows the gate to the
116
+ * negotiator automatically.
117
+ * @returns {Record<string, number>}
118
+ */
119
+ export function toFeatureMinVersion(m = CAPABILITY_MANIFEST) {
120
+ const out = {};
121
+ for (const f of wireFeaturesOf(m)) {
122
+ if (Number(f.minVersion) > 1) out[f.key] = Number(f.minVersion);
123
+ }
124
+ return out;
125
+ }
126
+
127
+ /**
128
+ * Feature → the stream line field it gates (the value of the private
129
+ * `FEATURE_TO_FIELD` map in capability-negotiation.js).
130
+ * @returns {Record<string, string>}
131
+ */
132
+ export function toFieldGate(m = CAPABILITY_MANIFEST) {
133
+ const out = {};
134
+ for (const f of wireFeaturesOf(m)) out[f.key] = f.field;
135
+ return out;
136
+ }
137
+
138
+ /**
139
+ * The server negotiation offer shape consumed by
140
+ * capability-negotiation.js `negotiateProtocol(server, …)`.
141
+ * @returns {{protocolVersion:number, minProtocolVersion:number, features:string[]}}
142
+ */
143
+ export function toServerOffer(m = CAPABILITY_MANIFEST) {
144
+ const mm = manifestOr(m);
145
+ return {
146
+ protocolVersion: Number(mm.protocolVersion) || 1,
147
+ minProtocolVersion: Number(mm.minProtocolVersion) || 1,
148
+ features: toProtocolFeatures(mm),
149
+ };
150
+ }
151
+
152
+ /**
153
+ * The `features` object of `cc agent --capabilities`
154
+ * (buildAgentCapabilities().features): every runtime flag true, every wire
155
+ * feature true, plus the nested `mcp` object and `sandbox_engines` list. A test
156
+ * asserts the live buildAgentCapabilities().features deep-equals this.
157
+ * @returns {object}
158
+ */
159
+ export function toAgentFeatureFlags(m = CAPABILITY_MANIFEST) {
160
+ const mm = manifestOr(m);
161
+ const flags = {};
162
+ for (const key of mm.runtimeFeatures || []) flags[key] = true;
163
+ for (const f of wireFeaturesOf(mm)) flags[f.key] = true;
164
+ flags.mcp = {};
165
+ for (const key of mm.mcpFeatures || []) flags.mcp[key] = true;
166
+ flags.sandbox_engines = [...(mm.sandboxEngines || [])];
167
+ return flags;
168
+ }
169
+
170
+ /**
171
+ * A deterministic capability fixture the VS Code / JetBrains twins can pin and
172
+ * assert against (the "Compatibility Fixture" the gap doc asks for). Order- and
173
+ * clock-independent; a `digest` lets a peer detect any change in one compare.
174
+ * @returns {object}
175
+ */
176
+ export function buildCompatFixture(m = CAPABILITY_MANIFEST) {
177
+ const mm = manifestOr(m);
178
+ const body = {
179
+ protocolVersion: Number(mm.protocolVersion) || 1,
180
+ minProtocolVersion: Number(mm.minProtocolVersion) || 1,
181
+ features: toProtocolFeatures(mm),
182
+ featureFields: toFieldGate(mm),
183
+ featureMinVersion: toFeatureMinVersion(mm),
184
+ permissionModes: [...(mm.permissionModes || [])],
185
+ outputFormats: [...(mm.outputFormats || [])],
186
+ inputFormats: [...(mm.inputFormats || [])],
187
+ };
188
+ return { ...body, digest: capabilityDigest(mm) };
189
+ }
190
+
191
+ /**
192
+ * A stable digest of the whole capability surface (no RNG/clock) — one string
193
+ * that changes iff any projected capability changed. Handy as a CI drift check.
194
+ * @returns {string} 16-hex-char sha256 prefix
195
+ */
196
+ export function capabilityDigest(m = CAPABILITY_MANIFEST) {
197
+ const mm = manifestOr(m);
198
+ const canonical = {
199
+ protocolVersion: Number(mm.protocolVersion) || 1,
200
+ minProtocolVersion: Number(mm.minProtocolVersion) || 1,
201
+ wire: wireFeaturesOf(mm)
202
+ .map((f) => [f.key, f.field, Number(f.minVersion) || 1])
203
+ .sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)),
204
+ runtime: [...(mm.runtimeFeatures || [])].sort(),
205
+ mcp: [...(mm.mcpFeatures || [])].sort(),
206
+ sandbox: [...(mm.sandboxEngines || [])].sort(),
207
+ permissionModes: [...(mm.permissionModes || [])].sort(),
208
+ outputFormats: [...(mm.outputFormats || [])].sort(),
209
+ inputFormats: [...(mm.inputFormats || [])].sort(),
210
+ };
211
+ return createHash("sha256")
212
+ .update(JSON.stringify(canonical))
213
+ .digest("hex")
214
+ .slice(0, 16);
215
+ }
216
+
217
+ /**
218
+ * A behavior matrix row per wire feature — feature × min version × gated field ×
219
+ * negotiable × description. Consumed by renderProtocolDoc and by a test that a
220
+ * doc table matches the code.
221
+ * @returns {Array<{feature:string, minVersion:number, field:string, negotiable:boolean, description:string}>}
222
+ */
223
+ export function renderBehaviorMatrix(m = CAPABILITY_MANIFEST) {
224
+ return wireFeaturesOf(m).map((f) => ({
225
+ feature: f.key,
226
+ minVersion: Number(f.minVersion) || 1,
227
+ field: f.field,
228
+ negotiable: true,
229
+ description: String(f.description || ""),
230
+ }));
231
+ }
232
+
233
+ /**
234
+ * Render the protocol capability section as Markdown (a generated doc a CI job
235
+ * diffs against the checked-in copy). Deterministic; ends with a trailing
236
+ * newline so a byte compare is stable.
237
+ * @returns {string}
238
+ */
239
+ export function renderProtocolDoc(m = CAPABILITY_MANIFEST) {
240
+ const mm = manifestOr(m);
241
+ const lines = [];
242
+ lines.push("# Agent Protocol Capability Manifest (generated)");
243
+ lines.push("");
244
+ lines.push(
245
+ `Protocol version **${mm.protocolVersion}** (negotiates down to **${mm.minProtocolVersion}**).`,
246
+ );
247
+ lines.push("");
248
+ lines.push("## Negotiable wire features");
249
+ lines.push("");
250
+ lines.push("| Feature | Min version | Line field | Description |");
251
+ lines.push("| ------- | ----------- | ---------- | ----------- |");
252
+ for (const row of renderBehaviorMatrix(mm)) {
253
+ lines.push(
254
+ `| \`${row.feature}\` | ${row.minVersion} | \`${row.field}\` | ${row.description} |`,
255
+ );
256
+ }
257
+ lines.push("");
258
+ lines.push("## Permission modes");
259
+ lines.push("");
260
+ lines.push((mm.permissionModes || []).map((x) => `\`${x}\``).join(", "));
261
+ lines.push("");
262
+ return lines.join("\n") + "\n";
263
+ }
264
+
265
+ /**
266
+ * Capability diff for release notes: what changed between a previous and a next
267
+ * manifest (or a manifest and the current canonical one). Additive vs removed vs
268
+ * min-version/field changes, plus any protocol-version bump.
269
+ *
270
+ * @param {object} prev a manifest (or its compat fixture)
271
+ * @param {object} [next=CAPABILITY_MANIFEST]
272
+ * @returns {{protocolChange:{from:number,to:number}|null,
273
+ * addedWireFeatures:string[], removedWireFeatures:string[],
274
+ * changedWireFeatures:Array<{feature:string, from:object, to:object}>,
275
+ * addedRuntimeFeatures:string[], removedRuntimeFeatures:string[]}}
276
+ */
277
+ export function diffCapabilities(prev, next = CAPABILITY_MANIFEST) {
278
+ const wireMap = (m) => {
279
+ const out = new Map();
280
+ for (const f of wireFeaturesOf(m)) {
281
+ out.set(f.key, {
282
+ field: f.field,
283
+ minVersion: Number(f.minVersion) || 1,
284
+ });
285
+ }
286
+ return out;
287
+ };
288
+ const runtimeSet = (m) => new Set(manifestOr(m).runtimeFeatures || []);
289
+
290
+ const p = wireMap(prev);
291
+ const n = wireMap(next);
292
+ const addedWireFeatures = [...n.keys()].filter((k) => !p.has(k)).sort();
293
+ const removedWireFeatures = [...p.keys()].filter((k) => !n.has(k)).sort();
294
+ const changedWireFeatures = [];
295
+ for (const [key, nv] of n) {
296
+ const pv = p.get(key);
297
+ if (pv && (pv.field !== nv.field || pv.minVersion !== nv.minVersion)) {
298
+ changedWireFeatures.push({ feature: key, from: pv, to: nv });
299
+ }
300
+ }
301
+
302
+ const pr = runtimeSet(prev);
303
+ const nr = runtimeSet(next);
304
+ const addedRuntimeFeatures = [...nr].filter((k) => !pr.has(k)).sort();
305
+ const removedRuntimeFeatures = [...pr].filter((k) => !nr.has(k)).sort();
306
+
307
+ const pVer = Number(manifestOr(prev).protocolVersion) || 1;
308
+ const nVer = Number(manifestOr(next).protocolVersion) || 1;
309
+ const protocolChange = pVer === nVer ? null : { from: pVer, to: nVer };
310
+
311
+ return {
312
+ protocolChange,
313
+ addedWireFeatures,
314
+ removedWireFeatures,
315
+ changedWireFeatures,
316
+ addedRuntimeFeatures,
317
+ removedRuntimeFeatures,
318
+ };
319
+ }
@@ -36,31 +36,42 @@
36
36
  * fixture __tests__/fixtures/capability-negotiation-cases.json.
37
37
  */
38
38
 
39
+ import {
40
+ CAPABILITY_MANIFEST,
41
+ toProtocolFeatures,
42
+ toFeatureMinVersion,
43
+ toFieldGate,
44
+ } from "./capability-manifest.js";
45
+
39
46
  /** The current protocol version the CLI speaks (mirror of PROTOCOL_VERSION). */
40
- export const PROTOCOL_VERSION = 1;
47
+ export const PROTOCOL_VERSION = CAPABILITY_MANIFEST.protocolVersion;
41
48
 
42
49
  /**
43
50
  * The oldest protocol version the CLI can still speak end-to-end (the N-1 in
44
51
  * "N / N-1"). At v1 there is no older line shape, so min === current; once v2
45
52
  * ships this drops to 1 so a v1-only client negotiates a v1 session.
46
53
  */
47
- export const PROTOCOL_MIN_VERSION = 1;
54
+ export const PROTOCOL_MIN_VERSION = CAPABILITY_MANIFEST.minProtocolVersion;
48
55
 
49
56
  /**
50
57
  * The wire-protocol features subject to negotiation — the additive per-line
51
58
  * fields a client may or may not understand. Runtime capabilities (bare,
52
59
  * worktree, mcp, …) are NOT negotiated: they change what the CLI can do, not
53
60
  * the shape of a line the client must parse.
61
+ *
62
+ * P1-9: projected from the single canonical [[capability-manifest.js]] so the
63
+ * negotiator, `cc agent --capabilities`, the protocol docs and the Java twin can
64
+ * no longer drift — add a v2 field to the manifest ONCE and it flows here.
54
65
  */
55
- export const PROTOCOL_FEATURES = ["event_seq", "tool_use_id", "trace_id"];
66
+ export const PROTOCOL_FEATURES = toProtocolFeatures();
56
67
 
57
68
  /**
58
69
  * Minimum protocol version a feature requires. Empty today (every current
59
- * feature is a v1 additive field). When a v2 field lands, add it here so an
60
- * N-1 (v1) session drops it automatically.
70
+ * feature is a v1 additive field). When a v2 field lands, bump its manifest
71
+ * `minVersion` and an N-1 (v1) session drops it automatically.
61
72
  * @type {Record<string, number>}
62
73
  */
63
- export const FEATURE_MIN_VERSION = {};
74
+ export const FEATURE_MIN_VERSION = toFeatureMinVersion();
64
75
 
65
76
  function intOr(value, fallback) {
66
77
  const n = Number(value);
@@ -185,12 +196,12 @@ export function negotiateProtocol(server = {}, clientOffer = null, opts = {}) {
185
196
  };
186
197
  }
187
198
 
188
- /** Feature key → the stream line field it gates. */
189
- const FEATURE_TO_FIELD = {
190
- event_seq: "seq",
191
- trace_id: "trace_id",
192
- tool_use_id: "tool_use_id",
193
- };
199
+ /**
200
+ * Feature key → the stream line field it gates. P1-9: projected from the
201
+ * canonical [[capability-manifest.js]] so this map can't drift from the
202
+ * negotiated feature list.
203
+ */
204
+ const FEATURE_TO_FIELD = toFieldGate();
194
205
 
195
206
  /**
196
207
  * Fold a negotiation result into a live field-gate the emitter reads per line:
@@ -0,0 +1,230 @@
1
+ /**
2
+ * De-identified diagnostic bundle assembler + mandatory secret-scan export gate
3
+ * (IDE gap P1-9 "脱敏诊断包" of CLAUDE_CODE_IDE_INCREMENTAL_GAP_ANALYSIS_2026-
4
+ * 07-13.md).
5
+ *
6
+ * When an IDE session misbehaves, a one-click diagnostic export is the fastest
7
+ * path to a fix — but a naive dump of "everything about the session" leaks
8
+ * source, keys and terminal output. The gap doc pins the contract exactly: a
9
+ * bundle carries version, platform, capability, connection state, redaction
10
+ * events, trace, reconnect history, and process/port/lockfile/worktree
11
+ * summaries; it does NOT carry source code, API keys, cookies, full environment
12
+ * variables, or unpermitted terminal output; and "导出前必须运行 Secret Scan".
13
+ *
14
+ * This module assembles that bundle from caller-supplied signals and runs the
15
+ * §8.1 recall-first scanner ([[secret-scan.js]]) as a fail-closed export gate.
16
+ * It reuses the canonical capability projection ([[capability-manifest.js]]) so
17
+ * the bundle never invents a second capability truth.
18
+ *
19
+ * Pure: no fs / process / clock / RNG — the caller collects the raw signals
20
+ * (which processes, which ports, the env object) and this module shapes, de-
21
+ * identifies and gates them. Timestamps, if any, are passed in.
22
+ */
23
+
24
+ import { redactSecrets, scanSecrets, SURFACES } from "./secret-scan.js";
25
+ import { buildCompatFixture } from "./capability-manifest.js";
26
+ import { isSecretEnvName } from "./credential-guard.js";
27
+
28
+ /** The categories deliberately EXCLUDED from every bundle (never assembled). */
29
+ export const EXCLUDED_BY_DEFAULT = Object.freeze([
30
+ "source code body",
31
+ "API keys / tokens",
32
+ "cookies",
33
+ "full environment variable values",
34
+ "unpermitted terminal output",
35
+ ]);
36
+
37
+ /**
38
+ * Summarize an environment WITHOUT any values: variable NAMES only, with the
39
+ * secret-named ones flagged (via isSecretEnvName). Honors the contract "默认不
40
+ * 包含完整环境变量" — a name is metadata, a value may be a credential.
41
+ * @param {object} env
42
+ * @returns {{count:number, names:string[], secretNames:string[]}}
43
+ */
44
+ export function summarizeEnv(env) {
45
+ if (!env || typeof env !== "object") {
46
+ return { count: 0, names: [], secretNames: [] };
47
+ }
48
+ const names = Object.keys(env).sort();
49
+ const secretNames = names.filter((n) => isSecretEnvName(n));
50
+ return { count: names.length, names, secretNames };
51
+ }
52
+
53
+ /** Recursively redact every string in a value (arrays/objects walked). */
54
+ function deepRedact(value, depth = 0) {
55
+ if (depth > 64) return value;
56
+ if (typeof value === "string") return redactSecrets(value);
57
+ if (Array.isArray(value)) return value.map((v) => deepRedact(v, depth + 1));
58
+ if (value && typeof value === "object") {
59
+ const out = {};
60
+ for (const k of Object.keys(value))
61
+ out[k] = deepRedact(value[k], depth + 1);
62
+ return out;
63
+ }
64
+ return value;
65
+ }
66
+
67
+ /**
68
+ * Recursively scan a value for any residual secret, returning findings with a
69
+ * dotted path so the gate can name WHERE a leak survived. Read-only.
70
+ * @returns {Array<{path:string, category:string}>}
71
+ */
72
+ export function scanBundleForSecrets(value, path = "", depth = 0) {
73
+ if (depth > 64) return [];
74
+ const findings = [];
75
+ if (typeof value === "string") {
76
+ for (const f of scanSecrets(value)) {
77
+ findings.push({ path: path || "(root)", category: f.category });
78
+ }
79
+ } else if (Array.isArray(value)) {
80
+ value.forEach((v, i) =>
81
+ findings.push(...scanBundleForSecrets(v, `${path}[${i}]`, depth + 1)),
82
+ );
83
+ } else if (value && typeof value === "object") {
84
+ for (const k of Object.keys(value)) {
85
+ findings.push(
86
+ ...scanBundleForSecrets(value[k], path ? `${path}.${k}` : k, depth + 1),
87
+ );
88
+ }
89
+ }
90
+ return findings;
91
+ }
92
+
93
+ const asArray = (v) => (Array.isArray(v) ? v : []);
94
+ const asString = (v) =>
95
+ typeof v === "string" ? v : v == null ? "" : String(v);
96
+
97
+ /**
98
+ * Assemble the diagnostic bundle from caller-supplied signals, de-identifying
99
+ * as it goes. Free-text fields are secret-redacted; env is reduced to names;
100
+ * terminal output is included ONLY with explicit permission (and still
101
+ * redacted). Excluded categories are never assembled in the first place.
102
+ *
103
+ * @param {object} input
104
+ * @param {string} [input.version]
105
+ * @param {object|string} [input.platform]
106
+ * @param {object} [input.capability] defaults to the canonical compat fixture
107
+ * @param {string} [input.connectionState]
108
+ * @param {Array} [input.reconnectHistory] [{at?, reason?}] — reasons redacted
109
+ * @param {object} [input.trace] {traceId, spanCount} summary (no content)
110
+ * @param {Array} [input.redactionEvents] [{surface, category, count}] — what was scrubbed
111
+ * @param {Array} [input.processes] [{pid, name, cpu?}] metadata
112
+ * @param {Array<number|object>} [input.ports]
113
+ * @param {Array<string>} [input.lockfiles]
114
+ * @param {Array} [input.worktrees] [{path, branch}]
115
+ * @param {object} [input.env] reduced to names only
116
+ * @param {string} [input.terminalOutput] included only when opts.includeTerminalOutput
117
+ * @param {string} [input.notes]
118
+ * @param {object} [opts]
119
+ * @param {boolean} [opts.includeTerminalOutput=false]
120
+ * @returns {object} the assembled (already-redacted) bundle
121
+ */
122
+ export function buildDiagnosticBundle(input = {}, opts = {}) {
123
+ const includeTerminal = opts.includeTerminalOutput === true;
124
+
125
+ const redactionEvents = asArray(input.redactionEvents)
126
+ .map((e) => ({
127
+ surface: SURFACES.includes(e?.surface) ? e.surface : "unknown",
128
+ category: asString(e?.category),
129
+ count: Number(e?.count) || 0,
130
+ }))
131
+ .filter((e) => e.category);
132
+
133
+ const bundle = {
134
+ schema: "cc-diagnostic-bundle/v1",
135
+ meta: {
136
+ version: asString(input.version) || null,
137
+ platform:
138
+ input.platform && typeof input.platform === "object"
139
+ ? input.platform
140
+ : asString(input.platform) || null,
141
+ excluded: [...EXCLUDED_BY_DEFAULT],
142
+ },
143
+ capability:
144
+ input.capability && typeof input.capability === "object"
145
+ ? input.capability
146
+ : buildCompatFixture(),
147
+ connection: {
148
+ state: asString(input.connectionState) || null,
149
+ reconnectHistory: asArray(input.reconnectHistory).map((r) => ({
150
+ at: r?.at ?? null,
151
+ reason: asString(r?.reason),
152
+ })),
153
+ },
154
+ trace: input.trace
155
+ ? {
156
+ traceId: asString(input.trace.traceId) || null,
157
+ spanCount: Number(input.trace.spanCount) || 0,
158
+ }
159
+ : null,
160
+ redactionEvents,
161
+ runtime: {
162
+ processes: asArray(input.processes).map((p) => ({
163
+ pid: p?.pid ?? null,
164
+ name: asString(p?.name),
165
+ cpu: p?.cpu ?? null,
166
+ })),
167
+ ports: asArray(input.ports).map((p) =>
168
+ typeof p === "object" && p
169
+ ? { port: Number(p.port) || null, proto: asString(p.proto) || null }
170
+ : { port: Number(p) || null, proto: null },
171
+ ),
172
+ lockfiles: asArray(input.lockfiles).map((f) => asString(f)),
173
+ worktrees: asArray(input.worktrees).map((w) => ({
174
+ path: asString(w?.path),
175
+ branch: asString(w?.branch) || null,
176
+ })),
177
+ },
178
+ // env is reduced to NAMES only up front (values never enter the bundle).
179
+ env: summarizeEnv(input.env),
180
+ notes: input.notes != null ? asString(input.notes) : null,
181
+ };
182
+
183
+ // Terminal output is unpermitted by default: dropped entirely, with a marker
184
+ // so the reader knows it was withheld (not merely absent).
185
+ if (includeTerminal && input.terminalOutput != null) {
186
+ bundle.terminalOutput = asString(input.terminalOutput);
187
+ } else {
188
+ bundle.terminalOutput = null;
189
+ bundle.terminalOutputWithheld = input.terminalOutput != null;
190
+ }
191
+
192
+ // Single de-identification pass over the WHOLE assembled bundle: every string
193
+ // field is secret-redacted regardless of which section it landed in, so the
194
+ // export gate is guaranteed clean no matter which signal carried a secret.
195
+ // env is already values-free, so its names (metadata) pass through untouched.
196
+ return deepRedact(bundle);
197
+ }
198
+
199
+ /**
200
+ * The mandatory pre-export gate ("导出前必须运行 Secret Scan"). Deep-scans the
201
+ * assembled bundle; fail-closed: if ANY residual secret survives, `ok` is false
202
+ * and `blocked` is true — the caller must NOT export it. On a clean bundle the
203
+ * gate returns it ready for export.
204
+ *
205
+ * @param {object} bundle a buildDiagnosticBundle() result
206
+ * @returns {{ok:boolean, blocked:boolean, findings:Array, bundle:object}}
207
+ */
208
+ export function secretScanGate(bundle) {
209
+ const findings = scanBundleForSecrets(bundle);
210
+ const ok = findings.length === 0;
211
+ return { ok, blocked: !ok, findings, bundle };
212
+ }
213
+
214
+ /**
215
+ * One-call assembly + gate. Builds the de-identified bundle and runs the secret
216
+ * scan. When the gate is clean, returns the exportable bundle; when it is not
217
+ * (should never happen given assembly redacts, but defense-in-depth), returns
218
+ * `ok:false` and the leak locations, and does NOT hand back an exportable body.
219
+ *
220
+ * @returns {{ok:boolean, bundle:(object|null), findings:Array}}
221
+ */
222
+ export function exportDiagnosticBundle(input = {}, opts = {}) {
223
+ const bundle = buildDiagnosticBundle(input, opts);
224
+ const gate = secretScanGate(bundle);
225
+ return {
226
+ ok: gate.ok,
227
+ bundle: gate.ok ? bundle : null,
228
+ findings: gate.findings,
229
+ };
230
+ }