chainlesschain 0.162.159 → 0.162.161

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 (199) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-ByIiF5bT.js → AIOps-DMn528q-.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-NlgSFZq5.js → ActionButton-D9ZcDCJL.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-D17cOUcb.js → Analytics-A_K-8PX0.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-B4mEZcbl.js → AppLayout-D4pFW3X6.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-CASsmyQw.js → Artifacts-Dz46COAd.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-DYAia2e2.js → Audit-cbMjbyg0.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-C1lvYUSu.js → BackgroundAgents-Dtf88GAj.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-B6agK6bA.js → Backup-C5KRxn52.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-DM43R2vy.js → BaseInput-Dd2UK3BN.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-Dtdp3eXp.js → Chat-lptKTFdG.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BL5c_OQ1.js → ChatBubbleRenderer-C2KLtwHz.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-D2xf7uZt.js → Checkbox-_yspG6rG.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-D3j45jJL.js → Codegen-BGq_GAha.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-voPJ9fUZ.js → Col-CePor71g.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-DEsyTESF.js → Community-CLOWhIT7.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-OHV_QXLD.js → Compact-CivJt2vT.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-eDTJWC-J.js → Compliance-Btmo1GGE.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-BBvy-6d1.js → Cowork-Ct35OlG4.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-Dw2eoJxu.js → Cron-CTc2GmhB.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-DQHms_Bh.js → Crosschain-ZtiBETk1.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-Cyu7Id1c.js → DID-BNqMQQXw.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-sIDzkG92.js → Dashboard-BmzZd-9V.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-DUW3T2ey.js → Dropdown-CMVgvP2M.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-CECELXW8.js → EmailListRenderer-CcqQFAfT.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BIZR37ov.js → FamilyGuardDashboard-CTqB1_4V.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-BEZIQX84.js → Federation-CiKOOzlb.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-BcdUMznL.js → FormItemContext-Wk-OT0gr.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-DsN_fr5H.js → GenericCardRenderer-DbEQT6ue.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-63LhjBx2.js → Git-0ZvCPSD9.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-CYtQtIcH.js → Governance-CmVuKkPb.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-BwR6UUox.js → Inference-CTWE3Jwg.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-D9lVNTUA.js → KnowledgeGraph-CBEIF00L.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-C4q5h7Ut.js → Logs-DLhxDwVG.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-DxywvhTU.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-Plksliuw.js → Marketplace-BgsCBrnc.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DZUFfeuG.js → McpTools-ClBUGDS2.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-NoDYt29V.js → Memory-CnG_ZnYw.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-CEXGTkdh.js → MobileBridge-BUOAbR1Z.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-DnbJkbZo.js → MobileProjects-Dnt_9-WB.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-DTAa0cwy.js → Mtc-CLjt4kCD.js} +5 -5
  43. package/src/assets/web-panel/assets/{MtcAudit-CVV7LnNS.js → MtcAudit-f24U9Aes.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-BqqGrjt_.js → Multisig-GrmGQbSq.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-C4WP8DRp.js → NLProgramming-DADaWn-n.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-Zo4DJUVI.js → Notes-xtf5RX6C.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-DBcjRaz2.js → NotificationSettings-Cd27Rgtx.js} +1 -1
  48. package/src/assets/web-panel/assets/OrderTableRenderer-fXRxlDjl.js +1 -0
  49. package/src/assets/web-panel/assets/{Organization-BT9WPBOX.js → Organization-joCFlNB0.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-D6b4bZ3q.js → Overflow-B1HoURq0.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-Cw3OsYx-.js → P2P-DiwsKbT7.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-C0nyiK2b.js → PdhVaultBrowser-Xe-BsW6S.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-zVBZEB_C.js → Permissions-C23qi8kl.js} +3 -3
  54. package/src/assets/web-panel/assets/{PersonalDataHub-BOspsFew.js → PersonalDataHub-BS1syX2f.js} +2 -2
  55. package/src/assets/web-panel/assets/{Pipeline-CQWfZ4XH.js → Pipeline-DRYwwDyh.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-C2oGJ_WL.js → Privacy-BQKaDHPH.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-BZMR4qY1.js → ProjectInit-BC6F9xiG.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-DAujoP63.js → ProjectSettings-ClFPc1Nd.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-Dn-f5aYe.js +1 -0
  60. package/src/assets/web-panel/assets/{Providers-D_RGHM-v.js → Providers-CLWuOUNt.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-Mf6KfL2J.js → QrScannerModal-DiqULTxb.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-5D8URoI_.js → QuickAsk-9A03P-E_.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-Df4kC8Gy.js → Recommend-CJZAnbq_.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-DGi3f3vO.js → RemoteSession-C9A1R77h.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-jmkGFNWb.js → Reputation-BIZc6Rky.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DP6Pcu3m.js → Row-DfIPNtuG.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-D60hSuPO.js → RssFeed-C5znFcQ1.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-0mf1BQUK.js → Search-DSs2z3OX.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-CfZtLFYV.js → Security-CV6LcrLY.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-D9zpZLWz.js → Services-cmhoX0Xj.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-D1_44eMD.js → Skeleton-tCsg2po7.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-cVFa8zkz.js → Skills-BK1KUDiy.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-Cinpzc3L.js → Sla-yRgyeA82.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-bqdiJE_P.js → SpeechSettings-c1mo1mlp.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-Dgl_SLgj.js → SyncSettings-koIAfuT3.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-DawWAEhy.js → Tasks-DkkreAaB.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-Ca0nY651.js → Templates-fy5Ctf9M.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-CgaGEhLm.js → Tenant-Bw70Jf6S.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-D-9EnpRx.js → Terminal-BKh6Rx-4.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-CSOxDHZ6.js → TimelineRenderer-Dg54WP0c.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BpwLZpL3.js → Tokens-BvvD5SxJ.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-DgowwBK5.js → Trigger-B7Ug9x4e.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-BZutapjE.js → Trust-BjvuMORY.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-QZPt8_Sp.js → UkeySign-CZqhkx8p.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-DlQx5UDJ.js → VideoEditing-ClR50lhX.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-ZfdTgtWA.js → Wallet-DlfppYjx.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-CAp0y_Cr.js → WebAuthn-BirgVr8B.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-BVITZEmr.js → WorkflowEditor-B8DP_bax.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-CC2tPaNg.js → chat-DA8AUZzL.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-31brc9CA.js → colors-C38pLGp1.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-DLMkC9C-.js → compact-item-81rtr98o.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-BMtveI6F.js → createContext-D34ixq8t.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-CHDIEOeN.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DbD6WHW2.js → hasIn-D4bzqTrZ.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BoO4WEJB.js → index-9qoVpPt6.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CJhREOlG.js → index-B0QArPQt.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BnFLzejT.js → index-B8mkLM4x.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Bbc2JeQG.js → index-BIP62a6r.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CFXKbOek.js → index-BP-zkXK8.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-C-cGlCDW.js → index-BS3M5H9V.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-AaIKG60c.js → index-BXd2_LmH.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CEXa-4zd.js → index-BlxEphl7.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DZl-UXB6.js → index-BqIhLAbA.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BOM-VFlv.js → index-BtGxuQrO.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CcVszn2b.js → index-BvD7b2IK.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-C21pdBqs.js → index-BvL_3J_n.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DJvxFyIz.js → index-COe_cOLJ.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-ALvoNTRF.js → index-CP5alL4t.js} +1 -1
  109. package/src/assets/web-panel/assets/index-CY1NURWZ.js +1 -0
  110. package/src/assets/web-panel/assets/{index-Dm0byjm1.js → index-CbhE3j5j.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-WQUO2vvK.js → index-Cd3KuNK5.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-bdaJwTXJ.js → index-CoVgZw_b.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-COHHQ9cU.js → index-CuS-UPoj.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-C7LHNw_N.js → index-D0Ih7Hwn.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Cod97Urs.js → index-D3zCCZRS.js} +3 -3
  116. package/src/assets/web-panel/assets/{index-Cwbqh0KZ.js → index-D6GfvEQG.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BKAB6-6V.js → index-DBFSo1NF.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CEwjBA9q.js → index-DFHa1HIg.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DndNnYc0.js → index-DFyWKOBD.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-3oBsXZmF.js → index-DMgnRwrK.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-C_eUKGYP.js → index-DUQLXLQM.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CUmLMFsy.js → index-D_2VYPr1.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BPpS-lGb.js → index-DakfFZ-k.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-ep6JchNG.js → index-Ddk0e_Pi.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-D7cV0tu0.js → index-DkHqMnn3.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-jG6h22Qg.js → index-Dmo3dFHb.js} +1 -1
  127. package/src/assets/web-panel/assets/index-DokUYqpq.js +1 -0
  128. package/src/assets/web-panel/assets/{index-C1PfeI88.js → index-DvfCEJG-.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-ntVQqSML.js → index-E1OJRXs4.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CWgfipcC.js → index-F76w1NiL.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BiRQUoyE.js → index-ZRJRItm0.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BFRLmCR3.js → index-fI5sXMLg.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-D67kIagl.js → index-tKiC8T-4.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-D5UoBRhN.js → initDefaultProps-ByyvflIR.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-DltOw0CR.js → motion-B3FQm2By.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-_oPkk5zj.js → move-BSfns6Ef.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-EES8ROCS.js → mtc-parser-CTG1Ieri.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-B-_39x_M.js → omit-DObM_2j6.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-l6D0yJHz.js → pickAttrs-DOfelQ1t.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-DIeUK31L.js → placementArrow-CHZILds_.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-dG4EzUQo.js → responsiveObserve-REbucKSx.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-DWFzYGdH.js → slide-C6EZzD7o.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-CECdTA5H.js → statusUtils-BxkWS-g3.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-D6kP-mMK.js → styleChecker-Chklyoq6.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-BWfna_rd.js → useFlexGapSupport-CRVwKrrI.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-Bb-j6rzo.js → useFs-DV24ik_J.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-D5OIsYM4.js → usePersonalDataHub-C7vIo4Pg.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-DNpwzFTM.js → vnode-By-v5GlF.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-vNLpFQBW.js → zoom-B1w9Am3r.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/agenda.js +15 -1
  152. package/src/commands/agent.js +72 -6
  153. package/src/commands/background-session.js +46 -2
  154. package/src/commands/mcp.js +46 -0
  155. package/src/commands/session.js +70 -0
  156. package/src/data/changelog.json +12 -2
  157. package/src/gateways/ws/background-agent-protocol.js +88 -14
  158. package/src/harness/jsonl-session-store.js +70 -1
  159. package/src/harness/mcp-client.js +50 -0
  160. package/src/lib/agent-authority.js +251 -0
  161. package/src/lib/agent-sandbox.js +68 -0
  162. package/src/lib/agents.js +12 -4
  163. package/src/lib/api-key-helper.js +62 -0
  164. package/src/lib/background-agent-phase.js +100 -0
  165. package/src/lib/background-agent-supervisor.js +320 -8
  166. package/src/lib/backpressure-policy.js +105 -0
  167. package/src/lib/capability-negotiation.js +212 -0
  168. package/src/lib/credential-guard.js +11 -0
  169. package/src/lib/credential-proxy.js +182 -0
  170. package/src/lib/dependency-install-policy.js +130 -0
  171. package/src/lib/event-seq-replay.js +167 -0
  172. package/src/lib/exit-codes.cjs +77 -0
  173. package/src/lib/goal-condition-engine.js +369 -0
  174. package/src/lib/headless-manifest.js +188 -0
  175. package/src/lib/hook-runner.cjs +70 -4
  176. package/src/lib/ide-context-redaction.js +130 -0
  177. package/src/lib/ide-context.js +128 -13
  178. package/src/lib/jsonl-session-store.js +2 -0
  179. package/src/lib/llm-config-defaults.js +24 -8
  180. package/src/lib/project-instructions.js +98 -1
  181. package/src/lib/project-mcp-trust.js +87 -0
  182. package/src/lib/remote-path-mapping.js +229 -0
  183. package/src/lib/schedule-planner.js +136 -0
  184. package/src/lib/sensitive-file-guard.js +51 -0
  185. package/src/lib/sub-agent-context.js +7 -3
  186. package/src/repl/bg-dashboard.js +13 -12
  187. package/src/runtime/agent-core.js +113 -10
  188. package/src/runtime/coding-agent-contract-shared.cjs +19 -2
  189. package/src/runtime/headless-runner.js +66 -7
  190. package/src/runtime/headless-stream.js +224 -12
  191. package/src/runtime/mcp-config.js +32 -1
  192. package/src/runtime/system-prompt.js +3 -0
  193. package/src/workers/background-agent-worker.js +37 -2
  194. package/src/assets/web-panel/assets/MarkdownRenderer-DexLlXfN.js +0 -1
  195. package/src/assets/web-panel/assets/OrderTableRenderer-BCQel9bD.js +0 -1
  196. package/src/assets/web-panel/assets/Projects-DvH_P2Uq.js +0 -1
  197. package/src/assets/web-panel/assets/devWarning-CB8pPOtd.js +0 -1
  198. package/src/assets/web-panel/assets/index-BhXiaiev.js +0 -1
  199. package/src/assets/web-panel/assets/index-WpaYaE3D.js +0 -1
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Bidirectional capability negotiation with N / N-1 downgrade for Agent
3
+ * Protocol (agent-sdk docs/PROTOCOL.md §1.3).
4
+ *
5
+ * The CLI advertises what it can speak one-directionally (buildAgentCapabilities
6
+ * → `cc agent --capabilities`, and the `system/init` line's protocol_version).
7
+ * That is not negotiation: a client (VS Code / JetBrains panel) had no way to
8
+ * announce what IT understands, and the CLI had no rule to pick a common level
9
+ * or step DOWN when the two disagree. When the protocol later grows a v2 line
10
+ * shape, a v1-only client would silently mis-parse it.
11
+ *
12
+ * This module is the missing algorithm. A client MAY send a `hello` as its
13
+ * first stream-json input line:
14
+ * {"type":"hello","protocol_version":2,"min_protocol_version":1,
15
+ * "features":["event_seq","trace_id"]}
16
+ * The CLI negotiates it against its own offer and echoes the agreed level as a
17
+ * `system/negotiated` line, then honors it for the rest of the run.
18
+ *
19
+ * Rules:
20
+ * - agreedVersion = min(server.max, client.max) — never above what either
21
+ * can parse.
22
+ * - Incompatible when agreedVersion < max(server.min, client.min): the two
23
+ * version ranges don't overlap. ok:false — the caller keeps a safe baseline
24
+ * or errors out; it never speaks a version the peer can't read.
25
+ * - effective features = the server-offered features the client also accepts
26
+ * (intersection), minus any whose minimum protocol version is above the
27
+ * agreed version (an N-only field is dropped for an N-1 session).
28
+ * - No client offer at all → legacy client that predates negotiation. The
29
+ * additive fields are defined "consumers MUST tolerate absence AND
30
+ * presence" (docs/PROTOCOL.md §1.2.1), so we keep FULL behavior unchanged
31
+ * (byte-for-byte) and mark clientAware:false. Negotiation only ever
32
+ * RESTRICTS when a client explicitly narrows the set.
33
+ *
34
+ * Pure: no fs / process / clock. The same algorithm is mirrored in the Java
35
+ * twin (jetbrains-plugin CapabilityNegotiation.java); both read the shared
36
+ * fixture __tests__/fixtures/capability-negotiation-cases.json.
37
+ */
38
+
39
+ /** The current protocol version the CLI speaks (mirror of PROTOCOL_VERSION). */
40
+ export const PROTOCOL_VERSION = 1;
41
+
42
+ /**
43
+ * The oldest protocol version the CLI can still speak end-to-end (the N-1 in
44
+ * "N / N-1"). At v1 there is no older line shape, so min === current; once v2
45
+ * ships this drops to 1 so a v1-only client negotiates a v1 session.
46
+ */
47
+ export const PROTOCOL_MIN_VERSION = 1;
48
+
49
+ /**
50
+ * The wire-protocol features subject to negotiation — the additive per-line
51
+ * fields a client may or may not understand. Runtime capabilities (bare,
52
+ * worktree, mcp, …) are NOT negotiated: they change what the CLI can do, not
53
+ * the shape of a line the client must parse.
54
+ */
55
+ export const PROTOCOL_FEATURES = ["event_seq", "tool_use_id", "trace_id"];
56
+
57
+ /**
58
+ * 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.
61
+ * @type {Record<string, number>}
62
+ */
63
+ export const FEATURE_MIN_VERSION = {};
64
+
65
+ function intOr(value, fallback) {
66
+ const n = Number(value);
67
+ return Number.isInteger(n) && n > 0 ? n : fallback;
68
+ }
69
+
70
+ /**
71
+ * Coerce a features declaration into a sorted, de-duplicated array of keys.
72
+ * Accepts an array of strings, or an object whose truthy keys are the features
73
+ * (the nested manifest shape). Anything else → [].
74
+ * @returns {string[]}
75
+ */
76
+ export function normalizeFeatureList(input) {
77
+ let keys;
78
+ if (Array.isArray(input)) {
79
+ keys = input.filter((k) => typeof k === "string" && k);
80
+ } else if (input && typeof input === "object") {
81
+ keys = Object.keys(input).filter((k) => input[k]);
82
+ } else {
83
+ return [];
84
+ }
85
+ return [...new Set(keys)].sort();
86
+ }
87
+
88
+ /**
89
+ * Extract the server's negotiation offer from a `cc agent --capabilities`
90
+ * manifest (buildAgentCapabilities output). Only the negotiable wire features
91
+ * the manifest advertises truthy are offered.
92
+ * @param {object} manifest
93
+ * @returns {{protocolVersion:number, minProtocolVersion:number, features:string[]}}
94
+ */
95
+ export function buildServerOffer(manifest = {}) {
96
+ const protocolVersion = intOr(manifest.protocol_version, PROTOCOL_VERSION);
97
+ const minProtocolVersion = intOr(
98
+ manifest.min_protocol_version,
99
+ Math.min(PROTOCOL_MIN_VERSION, protocolVersion),
100
+ );
101
+ const advertised = normalizeFeatureList(manifest.features);
102
+ const features = PROTOCOL_FEATURES.filter((f) => advertised.includes(f));
103
+ return { protocolVersion, minProtocolVersion, features };
104
+ }
105
+
106
+ /**
107
+ * Negotiate the effective protocol version + feature set between a server offer
108
+ * and a client offer.
109
+ *
110
+ * @param {{protocolVersion:number, minProtocolVersion?:number, features:string[]}} server
111
+ * @param {null|{protocolVersion?:number, minProtocolVersion?:number, features?:string[]}} clientOffer
112
+ * @param {object} [opts]
113
+ * @param {Record<string,number>} [opts.featureMinVersion=FEATURE_MIN_VERSION]
114
+ * @returns {{
115
+ * ok:boolean, agreedVersion:(number|null), features:string[],
116
+ * downgraded:boolean, disabledFeatures:string[], clientAware:boolean,
117
+ * reason:(string|null)
118
+ * }}
119
+ */
120
+ export function negotiateProtocol(server = {}, clientOffer = null, opts = {}) {
121
+ const featureMinVersion = opts.featureMinVersion || FEATURE_MIN_VERSION;
122
+ const serverMax = intOr(server.protocolVersion, PROTOCOL_VERSION);
123
+ const serverMin = intOr(server.minProtocolVersion, serverMax);
124
+ const serverFeatures = normalizeFeatureList(server.features);
125
+
126
+ const versionOk = (f, v) => (featureMinVersion[f] || 0) <= v;
127
+
128
+ // No client offer → legacy peer. Keep full behavior (only version-gate the
129
+ // server's own features against its own max), byte-for-byte unchanged.
130
+ if (clientOffer == null) {
131
+ return {
132
+ ok: true,
133
+ agreedVersion: serverMax,
134
+ features: serverFeatures.filter((f) => versionOk(f, serverMax)),
135
+ downgraded: false,
136
+ disabledFeatures: [],
137
+ clientAware: false,
138
+ reason: null,
139
+ };
140
+ }
141
+
142
+ const clientMax = intOr(clientOffer.protocolVersion, serverMax);
143
+ const clientMin = intOr(clientOffer.minProtocolVersion, clientMax);
144
+ const agreedVersion = Math.min(serverMax, clientMax);
145
+ const floor = Math.max(serverMin, clientMin);
146
+
147
+ if (agreedVersion < floor) {
148
+ return {
149
+ ok: false,
150
+ agreedVersion: null,
151
+ features: [],
152
+ downgraded: true,
153
+ disabledFeatures: [...serverFeatures].sort(),
154
+ clientAware: true,
155
+ reason:
156
+ `no common protocol version (server ${serverMin}-${serverMax}, ` +
157
+ `client ${clientMin}-${clientMax})`,
158
+ };
159
+ }
160
+
161
+ // A client that omits `features` accepts whatever the agreed version offers;
162
+ // a client that sends the array narrows to the intersection.
163
+ const clientFeatures =
164
+ clientOffer.features === undefined
165
+ ? null
166
+ : new Set(normalizeFeatureList(clientOffer.features));
167
+
168
+ const enabled = [];
169
+ const disabled = [];
170
+ for (const f of serverFeatures) {
171
+ const okVersion = versionOk(f, agreedVersion);
172
+ const okClient = clientFeatures == null || clientFeatures.has(f);
173
+ if (okVersion && okClient) enabled.push(f);
174
+ else disabled.push(f);
175
+ }
176
+
177
+ return {
178
+ ok: true,
179
+ agreedVersion,
180
+ features: enabled.sort(),
181
+ downgraded: agreedVersion < serverMax || disabled.length > 0,
182
+ disabledFeatures: disabled.sort(),
183
+ clientAware: true,
184
+ reason: null,
185
+ };
186
+ }
187
+
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
+ };
194
+
195
+ /**
196
+ * Fold a negotiation result into a live field-gate the emitter reads per line:
197
+ * a field stays stamped only if its feature survived negotiation. On an
198
+ * incompatible (ok:false) result nothing is changed (the caller keeps its safe
199
+ * baseline). Mutates `gate` in place and returns it.
200
+ *
201
+ * @param {{features:string[], ok:boolean}} result
202
+ * @param {{seq?:boolean, trace_id?:boolean, tool_use_id?:boolean}} gate
203
+ */
204
+ export function applyNegotiationToGate(result, gate = {}) {
205
+ if (!result || result.ok === false) return gate;
206
+ const enabled = new Set(result.features || []);
207
+ for (const feature of PROTOCOL_FEATURES) {
208
+ const field = FEATURE_TO_FIELD[feature];
209
+ if (field) gate[field] = enabled.has(feature);
210
+ }
211
+ return gate;
212
+ }
@@ -213,6 +213,17 @@ const PRINT_COMMANDS = new Set([
213
213
  const SECRET_VAR_RE =
214
214
  /(?:^|_)(KEY|KEYS|TOKEN|SECRET|SECRETS|PASSWORD|PASSWD|PASSPHRASE|CREDENTIAL|CREDENTIALS|PRIVATE|APIKEY|ACCESSKEY)(?:_|$)/i;
215
215
 
216
+ /**
217
+ * True when an environment-variable NAME looks like it holds a secret
218
+ * (ANTHROPIC_API_KEY, GITHUB_TOKEN, DB_PASSWORD, …). Shared single source of
219
+ * truth so the credential READ guard and the credential PROXY
220
+ * ([[credential-proxy.js]] — which keeps secrets out of subprocess envs)
221
+ * classify identically. `MONKEY`/`KEYBOARD`/`TOKENIZER` do NOT match.
222
+ */
223
+ export function isSecretEnvName(name) {
224
+ return typeof name === "string" && SECRET_VAR_RE.test(name);
225
+ }
226
+
216
227
  // Ways a secret var is referenced inside a shell segment.
217
228
  const SECRET_REF_PATTERNS = [
218
229
  /\$env:([A-Za-z_][A-Za-z0-9_]*)/gi, // PowerShell $env:NAME
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Credential proxy — keep the agent's real, long-lived credentials out of the
3
+ * environment that run_shell / run_code / hook / plugin subprocesses inherit
4
+ * (P0 §"跨平台沙箱与凭据代理").
5
+ *
6
+ * By default a spawned command inherits the WHOLE parent environment — including
7
+ * the agent's ANTHROPIC_API_KEY, cloud keys and tokens. A compromised or
8
+ * careless command can then echo, log, or exfiltrate them. This module replaces
9
+ * credential-named vars with an opaque SENTINEL before the child starts and
10
+ * keeps the real values in a parent-held vault, to be injected only for an
11
+ * approved host (via the egress proxy) — never handed to the child wholesale.
12
+ * The audit log only ever sees redacted values, never a restored secret.
13
+ *
14
+ * Pairs with credential-guard.js: that guard stops the AGENT reading secrets
15
+ * into model context; this stops the SUBPROCESS inheriting them. Both classify
16
+ * secret var names through the same `isSecretEnvName` so they never drift.
17
+ *
18
+ * Opt-in for now (`CC_CREDENTIAL_PROXY=1` or `config.credentialProxy.enabled`)
19
+ * so existing workflows that legitimately read a token from env keep working;
20
+ * default-on is the eventual goal once per-host injection is wired everywhere.
21
+ * Pure + dependency-light so every spawn seam can share it.
22
+ */
23
+
24
+ import { isSecretEnvName } from "./credential-guard.js";
25
+
26
+ /** Opaque replacement a masked credential var carries into the child env. */
27
+ export const CREDENTIAL_SENTINEL_PREFIX = "cc-cred-redacted:";
28
+
29
+ /**
30
+ * Well-known credential vars whose NAME does not obviously match the generic
31
+ * KEY/TOKEN/SECRET/PASSWORD pattern. Most real ones already match; this is a
32
+ * small safety net, extendable per-project via `deny`.
33
+ */
34
+ const EXTRA_CREDENTIAL_NAMES = new Set([
35
+ "AWS_SESSION_TOKEN", // matches TOKEN, kept explicit
36
+ "GOOGLE_APPLICATION_CREDENTIALS", // path to a key file
37
+ "CLOUDSDK_AUTH_ACCESS_TOKEN",
38
+ "DIGITALOCEAN_ACCESS_TOKEN",
39
+ ]);
40
+
41
+ function toSet(value) {
42
+ if (value instanceof Set) return value;
43
+ return new Set(Array.isArray(value) ? value : value ? [value] : []);
44
+ }
45
+
46
+ /**
47
+ * Classify an env var NAME as credential-bearing. `opts.allow` forces
48
+ * pass-through (never masked); `opts.deny` forces masking; otherwise the shared
49
+ * secret-name classifier + the curated extra set decide.
50
+ */
51
+ export function isCredentialEnvName(name, opts = {}) {
52
+ if (typeof name !== "string" || !name) return false;
53
+ if (toSet(opts.allow).has(name)) return false;
54
+ if (toSet(opts.deny).has(name)) return true;
55
+ return isSecretEnvName(name) || EXTRA_CREDENTIAL_NAMES.has(name);
56
+ }
57
+
58
+ /** The sentinel a child sees in place of a credential's real value. */
59
+ export function makeSentinel(name) {
60
+ return CREDENTIAL_SENTINEL_PREFIX + String(name ?? "");
61
+ }
62
+
63
+ /** True when a value is a proxy sentinel (carries no secret). */
64
+ export function isSentinel(value) {
65
+ return (
66
+ typeof value === "string" && value.startsWith(CREDENTIAL_SENTINEL_PREFIX)
67
+ );
68
+ }
69
+
70
+ /**
71
+ * Mask credential-named vars in `env`. Returns a NEW object (never mutates the
72
+ * input):
73
+ * - `env` — credential vars replaced with a sentinel (mode "mask", the
74
+ * default) or removed entirely (mode "deny"); everything else
75
+ * copied through verbatim.
76
+ * - `masked` — sorted list of the var names that were masked/removed.
77
+ * - `vault` — Map<name, realValue> the PARENT keeps for approved injection;
78
+ * the child never receives it.
79
+ * Values that are already sentinels, null, or undefined are left as-is (a
80
+ * sentinel is never double-masked; an absent var is not invented).
81
+ */
82
+ export function maskCredentialEnv(env = {}, opts = {}) {
83
+ const mode = opts.mode === "deny" ? "deny" : "mask";
84
+ const out = {};
85
+ const masked = [];
86
+ const vault = new Map();
87
+ for (const [name, value] of Object.entries(env || {})) {
88
+ if (
89
+ value != null &&
90
+ !isSentinel(value) &&
91
+ isCredentialEnvName(name, opts)
92
+ ) {
93
+ masked.push(name);
94
+ vault.set(name, String(value));
95
+ if (mode === "mask") out[name] = makeSentinel(name);
96
+ // mode "deny": drop the var so the child does not even see a sentinel
97
+ } else {
98
+ out[name] = value;
99
+ }
100
+ }
101
+ masked.sort();
102
+ return { env: out, masked, vault };
103
+ }
104
+
105
+ /** Replace a secret value with a fixed marker — never the real value. */
106
+ export function redactSecretValue(value) {
107
+ if (value == null) return value;
108
+ return String(value) ? "***" : "";
109
+ }
110
+
111
+ /**
112
+ * A log-safe projection of an env for the audit trail: credential values →
113
+ * "***", sentinels kept verbatim (they hold no secret), everything else
114
+ * untouched. NEVER emits a restored/real credential value.
115
+ */
116
+ export function redactEnvForAudit(env = {}, opts = {}) {
117
+ const out = {};
118
+ for (const [name, value] of Object.entries(env || {})) {
119
+ if (isSentinel(value)) out[name] = value;
120
+ else if (isCredentialEnvName(name, opts))
121
+ out[name] = redactSecretValue(value);
122
+ else out[name] = value;
123
+ }
124
+ return out;
125
+ }
126
+
127
+ /**
128
+ * Resolve the real value for a masked credential — but ONLY when the target
129
+ * host is on the approved list. The whole point of the proxy is that the child
130
+ * never gets the raw secret; the parent injects it just-in-time for an approved
131
+ * destination (e.g. the egress proxy adding Authorization for an allowed API
132
+ * host). Returns null (fail closed) for an unknown var, an empty host, or a
133
+ * non-approved host.
134
+ */
135
+ export function resolveApprovedInjection(vault, name, opts = {}) {
136
+ if (!(vault instanceof Map) || !vault.has(name)) return null;
137
+ const host = String(opts.host || "").toLowerCase();
138
+ if (!host) return null;
139
+ const approved = (opts.approvedHosts || []).map((h) =>
140
+ String(h || "").toLowerCase(),
141
+ );
142
+ return approved.includes(host) ? vault.get(name) : null;
143
+ }
144
+
145
+ /**
146
+ * Resolve whether the credential proxy is enabled. Env var wins so a spawn
147
+ * seam without config access can still honor it; otherwise the config flag.
148
+ */
149
+ export function credentialProxyEnabled(config = {}, env = process.env) {
150
+ const raw = env && env.CC_CREDENTIAL_PROXY;
151
+ if (raw != null) {
152
+ const v = String(raw).toLowerCase();
153
+ return v === "1" || v === "true" || v === "on" || v === "yes";
154
+ }
155
+ return config?.credentialProxy?.enabled === true;
156
+ }
157
+
158
+ /**
159
+ * Apply the credential proxy to a child env when enabled; otherwise return the
160
+ * env UNCHANGED (same reference — the default path stays byte-identical). The
161
+ * returned `vault` lets the caller inject approved creds later.
162
+ *
163
+ * @returns {{env:object, masked:string[], vault:Map, enabled:boolean}}
164
+ */
165
+ export function applyCredentialProxy(env, options = {}) {
166
+ const procEnv = options.env || process.env;
167
+ const config = options.config || {};
168
+ if (!credentialProxyEnabled(config, procEnv)) {
169
+ return { env, masked: [], vault: new Map(), enabled: false };
170
+ }
171
+ const allow = new Set([
172
+ ...toSet(options.allow),
173
+ ...(config?.credentialProxy?.allow || []),
174
+ ]);
175
+ const deny = new Set([
176
+ ...toSet(options.deny),
177
+ ...(config?.credentialProxy?.deny || []),
178
+ ]);
179
+ const mode = options.mode || config?.credentialProxy?.mode || "mask";
180
+ const result = maskCredentialEnv(env, { allow, deny, mode });
181
+ return { ...result, enabled: true };
182
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Dependency-install policy for `run_code` (gap-analysis 2026-07-11,
3
+ * docs/CLAUDE_CODE_CLI_GAP_ANALYSIS.md P0 "依赖安装与凭据").
4
+ *
5
+ * Auto `pip install` on a Python import error used to be unconditional. It is
6
+ * now OPT-IN:
7
+ * - env `CC_RUN_CODE_AUTO_INSTALL=1|0` wins outright (kill-switch friendly)
8
+ * - settings.json `runCode.autoInstall: true` opts in per project/user
9
+ * - default: DISABLED — the tool result explains how to enable it
10
+ *
11
+ * When enabled, an optional `runCode.installAllowlist` (array of package
12
+ * names, PEP 503-normalized) restricts WHICH packages may be installed, and
13
+ * every attempt — including blocked/disabled ones — is appended to an audit
14
+ * log at ~/.chainlesschain/audit/dependency-install.jsonl.
15
+ */
16
+
17
+ import fs from "node:fs";
18
+ import os from "node:os";
19
+ import path from "node:path";
20
+ import { createRequire } from "node:module";
21
+
22
+ const _require = createRequire(import.meta.url);
23
+
24
+ /** PEP 503 name normalization: lowercase, runs of -_. collapse to "-". */
25
+ export function normalizePackageName(name) {
26
+ return String(name || "")
27
+ .trim()
28
+ .toLowerCase()
29
+ .replace(/[-_.]+/g, "-");
30
+ }
31
+
32
+ /**
33
+ * Resolve the effective auto-install policy.
34
+ * @param {{cwd?:string, env?:object, settingsFile?:string|null}} opts
35
+ * @returns {{enabled:boolean, allowlist:string[]|null, source:string}}
36
+ */
37
+ export function resolveAutoInstallPolicy({
38
+ cwd = process.cwd(),
39
+ env = process.env,
40
+ settingsFile = null,
41
+ } = {}) {
42
+ const raw = env.CC_RUN_CODE_AUTO_INSTALL;
43
+ if (raw === "1" || raw === "0") {
44
+ return {
45
+ enabled: raw === "1",
46
+ allowlist: raw === "1" ? readAllowlist({ cwd, settingsFile }) : null,
47
+ source: "env",
48
+ };
49
+ }
50
+ let enabled = false;
51
+ try {
52
+ const { readBooleanSetting } = _require("./settings-loader.cjs");
53
+ enabled =
54
+ readBooleanSetting("runCode.autoInstall", { cwd, settingsFile }) === true;
55
+ } catch {
56
+ enabled = false; // fail-closed: no settings → stay disabled
57
+ }
58
+ return {
59
+ enabled,
60
+ allowlist: enabled ? readAllowlist({ cwd, settingsFile }) : null,
61
+ source: enabled ? "settings" : "default",
62
+ };
63
+ }
64
+
65
+ function readAllowlist({ cwd = process.cwd(), settingsFile }) {
66
+ try {
67
+ const { settingsPaths, readSettingsFile, loadManagedSettings } = _require(
68
+ "./settings-loader.cjs",
69
+ );
70
+ let value;
71
+ for (const file of settingsPaths(cwd, settingsFile)) {
72
+ const node = readSettingsFile(file)?.runCode?.installAllowlist;
73
+ if (Array.isArray(node)) value = node; // closest layer wins
74
+ }
75
+ const managed = loadManagedSettings({});
76
+ const mNode = managed?.settings?.runCode?.installAllowlist;
77
+ if (Array.isArray(mNode)) value = mNode; // managed policy wins outright
78
+ if (!Array.isArray(value)) return null;
79
+ const list = value.map(normalizePackageName).filter(Boolean);
80
+ return list.length ? list : null;
81
+ } catch {
82
+ return null;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Whether a package may be installed under the policy's allowlist.
88
+ * No allowlist configured → any (validity is checked separately).
89
+ */
90
+ export function isPackageAllowed(name, allowlist) {
91
+ if (!allowlist) return true;
92
+ return allowlist.includes(normalizePackageName(name));
93
+ }
94
+
95
+ /** Human hint for the disabled-by-default path (returned in the tool result). */
96
+ export function autoInstallDisabledHint(packageName) {
97
+ return (
98
+ `Missing Python package "${packageName}". Auto-install is disabled by ` +
99
+ `default; install it yourself (pip install ${packageName}), or opt in via ` +
100
+ `settings.json {"runCode":{"autoInstall":true}} or CC_RUN_CODE_AUTO_INSTALL=1.`
101
+ );
102
+ }
103
+
104
+ /**
105
+ * Append an install-attempt audit record (best-effort, never throws).
106
+ * @param {{package:string, interpreter?:string, cwd?:string,
107
+ * outcome:"installed"|"failed"|"blocked"|"disabled", detail?:string}} entry
108
+ * @param {{baseDir?:string, now?:()=>number}} [opts] injection seams
109
+ */
110
+ export function recordInstallAudit(entry, opts = {}) {
111
+ try {
112
+ const baseDir =
113
+ opts.baseDir ||
114
+ process.env.CC_AUDIT_DIR ||
115
+ path.join(os.homedir(), ".chainlesschain", "audit");
116
+ fs.mkdirSync(baseDir, { recursive: true });
117
+ const line = JSON.stringify({
118
+ ts: new Date(opts.now ? opts.now() : Date.now()).toISOString(),
119
+ kind: "dependency-install",
120
+ ...entry,
121
+ });
122
+ fs.appendFileSync(
123
+ path.join(baseDir, "dependency-install.jsonl"),
124
+ line + "\n",
125
+ "utf-8",
126
+ );
127
+ } catch {
128
+ /* audit is best-effort — never affect the run */
129
+ }
130
+ }