chainlesschain 0.165.5 → 0.165.6

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 (193) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-CEeYTeDB.js → AIOps-vfoDb6HZ.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BeRUQ3V2.js → ActionButton-Cm5f7p93.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DrV718XF.js → Analytics-yRuvgWxa.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-Cd2Frt7Q.js → AppLayout-BROsC4h1.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-CUJMlORU.js → Artifacts-CimHApb1.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CXJcfT6p.js → Audit-DI8kr8zI.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-DOv6Z7H9.js → BackgroundAgents-DSLzKy-n.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BwdDyBI0.js → Backup-DkH_n2r1.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-BmLn4kYH.js → BaseInput-CKSrUoRA.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-COg8pAW2.js → Chat-ClwfMXUS.js} +4 -4
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-CjMaqfL7.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-BCP4uoU8.js → Checkbox-BYTehcQG.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-pRt6Y9JI.js → Codegen-B8AMcp6J.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DLaprKhS.js → Col-BNSogPo3.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DyATsz5f.js → Community-JxPvGb8O.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-BXAaupZK.js → Compact-DF7bFAgE.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-DpcJBELr.js → Compliance-Bvhqnwrq.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-DG4EFWyd.js → Cowork-BDS0CJvM.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-F6Oy5n2P.js → Cron-DqvRfxoQ.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BOdgtalX.js → Crosschain-CDcIvnqS.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-DT1dSkjH.js → DID-D7jaUxU3.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-NBUIqWzC.js → Dashboard-B5A18wEV.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CSpHeMYB.js → Dropdown-_6IVkGag.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-CEG0xISr.js → EmailListRenderer-C9PeiKCG.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BTZ0BZna.js → FamilyGuardDashboard-DXjuEHrX.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-BuuCTn2V.js → Federation-B4IAR9zv.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-KAVceevk.js → FormItemContext-CxQeSNx1.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-DnHpcQvW.js → GenericCardRenderer-B8GS-Y3N.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-C_B_0HwB.js → Git-BS8SouUX.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-DPSZuzuu.js → Governance-Dq9aYb5Q.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-BV-06V7q.js → Inference-BrCEe9AJ.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-wpDuupn6.js → KnowledgeGraph-DybOpiY2.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-Bseca1_I.js → Logs-DBpzbRfx.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-d3kM4DDL.js → Marketplace-Cc7CCSCu.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-DQ_xZ23i.js → McpTools-DFaIhOfu.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-CRIeYO4L.js → Memory-KCqlp9ol.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BsllIa9l.js → MobileBridge-Bys84ySN.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-BUuQy30X.js → MobileProjects-B-6qU-je.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-BOFDIzrC.js → Mtc-BgY-fvCq.js} +4 -4
  42. package/src/assets/web-panel/assets/{MtcAudit-BQg-vyvG.js → MtcAudit-B8pCmqQJ.js} +5 -5
  43. package/src/assets/web-panel/assets/{Multisig-Bo1jrPdN.js → Multisig-BbOmYutA.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-Bmod8alw.js → NLProgramming-BocLQz_M.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CGGFL8w-.js → Notes-CSwbxEop.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-BsZvCJxC.js → NotificationSettings-CqFNSYG3.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-C1-Quo_h.js → OrderTableRenderer-C6gMfAk7.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-BjOeDYOi.js → Organization-QrDXVdMJ.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-DwVV3l5A.js → Overflow-Cxntotjt.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-Cu3DQT9W.js → P2P-CIdBOwNq.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Cg0gR7dH.js → PdhVaultBrowser-CT5aP8rD.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-CsAXxRN5.js → Permissions-DH7OM8XU.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-C-nNmnYp.js → PersonalDataHub-CnJqbWce.js} +4 -4
  54. package/src/assets/web-panel/assets/{Pipeline-BOnW7nNc.js → Pipeline-5C3HG1kH.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-ClLSyGKt.js → Privacy-B4yIp7xK.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-DuLvjjq7.js → ProjectInit-BMvt-2M0.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-DQYYbDD9.js → ProjectSettings-CvSTEMRG.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-Drw8MBGK.js → Projects-BKBJxEek.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-ckqHQe6K.js → Providers-BdwUqFWN.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DW5BXk-G.js → QrScannerModal-DtnCKlTm.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-DDLnhNTp.js → QuickAsk-sTbgwyNr.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-PJQ2C91U.js → Recommend-Bzr14gSb.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-s6wmda34.js → RemoteSession-C_TF5IaJ.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-DC2sb-I3.js → Reputation-GCAM4H2K.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-hPGk1eGj.js → Row-Dir0r6OQ.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-RIvfZql0.js → RssFeed-CpGnlecX.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DFg1G1eR.js → Search-DMMamC6u.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-D6H5p8Dp.js → Security-yqay5yG-.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-nxYiFV_w.js → Services-C5i2wh9G.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-BmgFNAdB.js → Skeleton-vUqk35yj.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-CsaaD2av.js → Skills-DzcSkhot.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-yM5y9yvK.js → Sla-CB6qbYNr.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-DPy1XMYh.js → SpeechSettings-BHUI7cox.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CFVhEyyr.js → SyncSettings-D-RrsxTJ.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-DdO02Inn.js → Tasks-DFxdOJiq.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-Eo2d8Llu.js → Templates-SKAob_Aq.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-ChESOXgy.js → Tenant-BPfsfHQ-.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BJ7oDxTI.js → Terminal-8iw7FpUf.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-hWX0fUvz.js → TimelineRenderer-C4Gjj5Su.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-DVBZQnd-.js → Tokens-COYhN18e.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-D89cQfVq.js → Trigger-B-JzXcf6.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-BIVSb4-V.js → Trust-BJ5bg0nI.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-B58gWopl.js → UkeySign-BG_ByRMf.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-DhHI7bmG.js → VideoEditing-B3kOnSiU.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-C4FheWS_.js → Wallet-CcoGFnK7.js} +3 -3
  86. package/src/assets/web-panel/assets/{WebAuthn-DnPRq-Bx.js → WebAuthn-DdT8vRC_.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-BerRPjpJ.js → WorkflowEditor-CZu3M9N8.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-BzZ52iKr.js → chat-DxJt3Qba.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-BI_E4BAn.js → colors-DdYfwJaB.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-d50mr3yk.js → compact-item-BG3SUzwj.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-C6FsQ1IH.js → createContext-By51z2-1.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DLZEMgLP.js → hasIn-B0apZ3KJ.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BoNZlzhQ.js → index-3jWkIu5j.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-AHr7ryWf.js → index-B3rWuPMW.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-KNGgO4Wm.js → index-B5rLiEo0.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-IPkwSAW_.js → index-BBPf3TpM.js} +1 -1
  98. package/src/assets/web-panel/assets/index-BETiL0a3.js +1 -0
  99. package/src/assets/web-panel/assets/{index-Dec0Kf_Z.js → index-BIrJfqO4.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BTpIOTfR.js → index-BLIbGOLX.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BcSUl9fA.js → index-BP94CrBV.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-B81mo1Et.js → index-BWF8_mkx.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DoFtCVZ7.js → index-BYINoRLK.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DY4j5zkF.js → index-BZ_0qm4g.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CVzuawCX.js → index-BewKYrX9.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DE0V6cp-.js → index-BfWYsB9t.js} +1 -1
  107. package/src/assets/web-panel/assets/index-BhAVHuW8.js +1 -0
  108. package/src/assets/web-panel/assets/{index-BRGx3w6T.js → index-Bissefox.js} +3 -3
  109. package/src/assets/web-panel/assets/{index-CjuKZBqJ.js → index-BkvcEo4p.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-9H_M2jju.js → index-Bos5_aMV.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DqkbHHg8.js → index-C1C9i79A.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Y2-q7QLm.js → index-CAhHirVH.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Q5Tn735s.js → index-CNIxXMmN.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CO8DAF7U.js → index-CW93tZDv.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BZjjdQ62.js → index-CWEuZWCK.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-C3k8kevG.js → index-Cp6e-hGw.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CUaEHfQ0.js → index-CpjZhgNd.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-GXE0D8Nx.js → index-CzmQElZy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-Bmr_vxXD.js → index-DCn8IcxV.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-D-aBumtY.js → index-DE76D-kA.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BeM4hfht.js → index-DaIz0NAW.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Pvf30m8Q.js → index-Da_WBdLX.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BAE3hwev.js → index-Dz49VOyQ.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CdKt9fOd.js → index-Q2SCX-MI.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CRN5TRTQ.js → index-RD6O-0C6.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DsscwxPE.js → index-SQqOlR3d.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-TWG-_D1M.js → index-ZFbps9po.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CLoTQr0W.js → index-ZxtP8jG7.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BB9SqVSt.js → index-bP_VlDb4.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-A5CDWP7y.js → index-kf57LJ93.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CgkoH3_g.js → index-oxRrmFO9.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Co7s9LsB.js → index-p1_8SbI1.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-D-4Hzgej.js → initDefaultProps-BWJktcg8.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-D4Wl4xU7.js → motion-BEXBzUFV.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-Cd5eQvL0.js → move-BUKwn2sx.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-B4ilslCW.js → mtc-parser-FTI7btqQ.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-Ckarx-tf.js → omit-CsLtSPH3.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-QJ0bpCmc.js → pickAttrs-DWZxsT_S.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-CKHm-tq8.js → placementArrow-Bcfs43CX.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-CV6a_w_Y.js → responsiveObserve-a9bcC1p-.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-mHpeQFgk.js → slide-BDTzgOXN.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-DIaQNQWA.js → statusUtils-Iq1j_HUg.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-PJpzavV8.js → styleChecker-DAZEYxeh.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-BH7zTw8S.js → useFlexGapSupport-DVkjyDrh.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-BDNG3JDd.js → useFs-wXTpNTHt.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-CY2VWdUg.js → usePersonalDataHub-CtQM_aZr.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-D0kw9jhz.js → vnode-hjEoG85v.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-B5g7TN7r.js → zoom-BHVpyKg_.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/command-help-index.json +2 -2
  151. package/src/commands/agent.js +14 -2
  152. package/src/commands/background-session.js +100 -10
  153. package/src/commands/mcp.js +5 -0
  154. package/src/commands/plugin.js +145 -29
  155. package/src/commands/remote-control.js +295 -110
  156. package/src/commands/session-message.js +171 -0
  157. package/src/commands/session.js +11 -0
  158. package/src/data/changelog.json +15 -2
  159. package/src/harness/mcp-client.js +141 -10
  160. package/src/lib/background-session-transport.js +153 -27
  161. package/src/lib/mcp-client.js +2 -0
  162. package/src/lib/mcp-headers-helper.js +47 -5
  163. package/src/lib/mcp-oauth.js +55 -6
  164. package/src/lib/mcp-tls.js +230 -0
  165. package/src/lib/output-styles.js +12 -0
  166. package/src/lib/plugin-runtime/install.js +614 -71
  167. package/src/lib/plugin-runtime/marketplace-archive-source.js +331 -0
  168. package/src/lib/plugin-runtime/marketplace-catalog.js +52 -6
  169. package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1 -0
  170. package/src/lib/plugin-runtime/marketplace-source-adapter.js +163 -0
  171. package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
  172. package/src/lib/plugin-runtime/remote-source.js +435 -55
  173. package/src/lib/plugin-security.js +393 -41
  174. package/src/lib/plugin-source-identity.js +850 -0
  175. package/src/lib/remote-approval-bridge.js +136 -46
  176. package/src/lib/remote-control.js +217 -31
  177. package/src/lib/session-message-fabric.js +1105 -0
  178. package/src/lib/session-projection.js +45 -2
  179. package/src/lib/session-runtime-retention.js +155 -0
  180. package/src/lib/settings-loader.cjs +29 -0
  181. package/src/repl/agent-repl.js +51 -14
  182. package/src/repl/permission-prompt.js +31 -8
  183. package/src/repl/repl-keybindings.js +55 -0
  184. package/src/runtime/agent-core.js +51 -2
  185. package/src/runtime/headless-runner.js +47 -66
  186. package/src/runtime/headless-stream.js +18 -24
  187. package/src/runtime/mcp-config.js +2 -0
  188. package/src/runtime/policies/agent-policy.js +2 -0
  189. package/src/assets/web-panel/assets/ChatBubbleRenderer-4gGkdGgA.js +0 -1
  190. package/src/assets/web-panel/assets/MarkdownRenderer-B4RLp2zs.js +0 -1
  191. package/src/assets/web-panel/assets/devWarning-CagEcIex.js +0 -1
  192. package/src/assets/web-panel/assets/index-pzT9PpOU.js +0 -1
  193. package/src/assets/web-panel/assets/index-z1kFadaX.js +0 -1
@@ -33,6 +33,10 @@ import {
33
33
 
34
34
  const HELLO_TIMEOUT_MS = 5000;
35
35
  const MAX_LINE_BYTES = 1024 * 1024;
36
+ export const BACKGROUND_SESSION_BACKLOG_LIMITS = Object.freeze({
37
+ messages: 256,
38
+ bytes: 1024 * 1024,
39
+ });
36
40
 
37
41
  /** Pipe endpoint for a session id (Windows named pipe / POSIX socket file). */
38
42
  export function transportPipePath(id, dir, options = {}) {
@@ -70,14 +74,128 @@ export function createNdjsonReader(onMessage, onError = () => {}) {
70
74
  };
71
75
  }
72
76
 
73
- function writeMessage(socket, message) {
74
- if (!socket || socket.destroyed) return false;
75
- try {
76
- socket.write(`${JSON.stringify(message)}\n`);
77
- return true;
78
- } catch {
77
+ /**
78
+ * A socket writer with an application-level queue above Node's native write
79
+ * buffer. Once `socket.write()` reports backpressure, later messages wait for
80
+ * `drain`; a consumer that exceeds either cap is disconnected instead of
81
+ * retaining an unbounded event history in the worker.
82
+ */
83
+ export function createBoundedSocketWriter(socket, configured = {}) {
84
+ const maxMessages = Math.max(
85
+ 1,
86
+ Number.isFinite(configured.messages)
87
+ ? Math.floor(configured.messages)
88
+ : BACKGROUND_SESSION_BACKLOG_LIMITS.messages,
89
+ );
90
+ const maxBytes = Math.max(
91
+ 1024,
92
+ Number.isFinite(configured.bytes)
93
+ ? Math.floor(configured.bytes)
94
+ : BACKGROUND_SESSION_BACKLOG_LIMITS.bytes,
95
+ );
96
+ const queue = [];
97
+ let queuedBytes = 0;
98
+ let waitingForDrain = false;
99
+ let closed = false;
100
+ let overflowed = false;
101
+
102
+ const nativeBufferedBytes = () =>
103
+ Number.isFinite(socket?.writableLength)
104
+ ? Math.max(0, socket.writableLength)
105
+ : 0;
106
+ const clear = () => {
107
+ queue.length = 0;
108
+ queuedBytes = 0;
109
+ waitingForDrain = false;
110
+ };
111
+ const overflow = () => {
112
+ if (overflowed) return false;
113
+ overflowed = true;
114
+ closed = true;
115
+ clear();
116
+ try {
117
+ configured.onOverflow?.({ maxMessages, maxBytes });
118
+ } catch {
119
+ /* observer errors do not change the fail-closed outcome */
120
+ }
121
+ try {
122
+ socket?.destroy();
123
+ } catch {
124
+ /* already closed */
125
+ }
79
126
  return false;
80
- }
127
+ };
128
+ const writePayload = (payload) => {
129
+ try {
130
+ if (socket.write(payload) === false) waitingForDrain = true;
131
+ return true;
132
+ } catch {
133
+ closed = true;
134
+ clear();
135
+ return false;
136
+ }
137
+ };
138
+ const flush = () => {
139
+ if (closed || socket?.destroyed) {
140
+ closed = true;
141
+ clear();
142
+ return;
143
+ }
144
+ waitingForDrain = false;
145
+ while (queue.length > 0 && !waitingForDrain) {
146
+ const next = queue.shift();
147
+ queuedBytes -= next.bytes;
148
+ if (!writePayload(next.payload)) break;
149
+ }
150
+ };
151
+ const close = () => {
152
+ if (closed) return;
153
+ closed = true;
154
+ clear();
155
+ socket?.removeListener?.("drain", flush);
156
+ };
157
+
158
+ socket?.on?.("drain", flush);
159
+ socket?.once?.("close", close);
160
+ socket?.once?.("error", close);
161
+
162
+ return Object.freeze({
163
+ write(message) {
164
+ if (closed || !socket || socket.destroyed) return false;
165
+ let payload;
166
+ try {
167
+ payload = `${JSON.stringify(message)}\n`;
168
+ } catch {
169
+ return false;
170
+ }
171
+ const bytes = Buffer.byteLength(payload, "utf8");
172
+ if (bytes > maxBytes) return overflow();
173
+ if (waitingForDrain || queue.length > 0) {
174
+ if (
175
+ queue.length + 1 > maxMessages ||
176
+ nativeBufferedBytes() + queuedBytes + bytes > maxBytes
177
+ ) {
178
+ return overflow();
179
+ }
180
+ queue.push({ payload, bytes });
181
+ queuedBytes += bytes;
182
+ return true;
183
+ }
184
+ if (nativeBufferedBytes() + bytes > maxBytes) return overflow();
185
+ return writePayload(payload);
186
+ },
187
+ close,
188
+ state: () => ({
189
+ queuedMessages: queue.length,
190
+ queuedBytes,
191
+ nativeBufferedBytes: nativeBufferedBytes(),
192
+ waitingForDrain,
193
+ overflowed,
194
+ closed,
195
+ maxMessages,
196
+ maxBytes,
197
+ }),
198
+ });
81
199
  }
82
200
 
83
201
  /**
@@ -117,7 +235,7 @@ export function startBackgroundSessionServer(opts) {
117
235
  /* fine — usually ENOENT */
118
236
  }
119
237
  }
120
- const clients = new Set();
238
+ const clients = new Map();
121
239
  const notifyClients = () => {
122
240
  try {
123
241
  onClientChange?.(clients.size);
@@ -128,6 +246,8 @@ export function startBackgroundSessionServer(opts) {
128
246
 
129
247
  const server = net.createServer((socket) => {
130
248
  let authed = false;
249
+ const writer = createBoundedSocketWriter(socket);
250
+ const send = (message) => writer.write(message);
131
251
  const helloTimer = setTimeout(() => {
132
252
  if (!authed) socket.destroy();
133
253
  }, HELLO_TIMEOUT_MS);
@@ -141,8 +261,8 @@ export function startBackgroundSessionServer(opts) {
141
261
  if (message.type === "hello" && message.token === token) {
142
262
  authed = true;
143
263
  clearTimeout(helloTimer);
144
- clients.add(socket);
145
- writeMessage(socket, {
264
+ clients.set(socket, writer);
265
+ send({
146
266
  type: "hello",
147
267
  ...(getStatus ? getStatus() : { id }),
148
268
  });
@@ -156,7 +276,7 @@ export function startBackgroundSessionServer(opts) {
156
276
  case "prompt": {
157
277
  const text = String(message.text || "").trim();
158
278
  if (!text) {
159
- writeMessage(socket, {
279
+ send({
160
280
  type: "error",
161
281
  message: "prompt text is empty",
162
282
  });
@@ -164,12 +284,12 @@ export function startBackgroundSessionServer(opts) {
164
284
  }
165
285
  try {
166
286
  const result = onPrompt(text);
167
- writeMessage(socket, {
287
+ send({
168
288
  type: "accepted",
169
289
  queued: result?.queued ?? 1,
170
290
  });
171
291
  } catch (error) {
172
- writeMessage(socket, {
292
+ send({
173
293
  type: "error",
174
294
  message: error?.message || String(error),
175
295
  });
@@ -177,7 +297,7 @@ export function startBackgroundSessionServer(opts) {
177
297
  return;
178
298
  }
179
299
  case "status": {
180
- writeMessage(socket, {
300
+ send({
181
301
  type: "status",
182
302
  ...(getStatus ? getStatus() : { id }),
183
303
  });
@@ -186,9 +306,9 @@ export function startBackgroundSessionServer(opts) {
186
306
  case "stop": {
187
307
  try {
188
308
  onStop?.();
189
- writeMessage(socket, { type: "stopping" });
309
+ send({ type: "stopping" });
190
310
  } catch (error) {
191
- writeMessage(socket, {
311
+ send({
192
312
  type: "error",
193
313
  message: error?.message || String(error),
194
314
  });
@@ -206,7 +326,7 @@ export function startBackgroundSessionServer(opts) {
206
326
  message;
207
327
  const resolvedId = requestId || intId;
208
328
  if (!resolvedId) {
209
- writeMessage(socket, {
329
+ send({
210
330
  type: "error",
211
331
  message: "interaction_response requires requestId or intId",
212
332
  });
@@ -219,7 +339,7 @@ export function startBackgroundSessionServer(opts) {
219
339
  answer: answer ?? result,
220
340
  error,
221
341
  });
222
- writeMessage(socket, {
342
+ send({
223
343
  type: "received",
224
344
  requestId: resolvedId,
225
345
  accepted:
@@ -232,7 +352,7 @@ export function startBackgroundSessionServer(opts) {
232
352
  : false,
233
353
  });
234
354
  } catch (err) {
235
- writeMessage(socket, {
355
+ send({
236
356
  type: "error",
237
357
  message: err?.message || String(err),
238
358
  });
@@ -240,7 +360,7 @@ export function startBackgroundSessionServer(opts) {
240
360
  return;
241
361
  }
242
362
  default:
243
- writeMessage(socket, {
363
+ send({
244
364
  type: "error",
245
365
  message: `unknown message type: ${message.type}`,
246
366
  });
@@ -256,6 +376,7 @@ export function startBackgroundSessionServer(opts) {
256
376
 
257
377
  const drop = () => {
258
378
  clearTimeout(helloTimer);
379
+ writer.close();
259
380
  if (clients.delete(socket)) notifyClients();
260
381
  };
261
382
  socket.on("close", drop);
@@ -281,11 +402,11 @@ export function startBackgroundSessionServer(opts) {
281
402
  pipePath,
282
403
  clientCount: () => clients.size,
283
404
  broadcast: (message) => {
284
- for (const socket of clients) writeMessage(socket, message);
405
+ for (const writer of clients.values()) writer.write(message);
285
406
  },
286
407
  broadcastInteractionRequest: (intId, payload, binding = null) => {
287
- for (const socket of clients)
288
- writeMessage(socket, {
408
+ for (const writer of clients.values())
409
+ writer.write({
289
410
  type: "interaction_request",
290
411
  intId,
291
412
  requestId: intId,
@@ -302,7 +423,10 @@ export function startBackgroundSessionServer(opts) {
302
423
  },
303
424
  close: () =>
304
425
  new Promise((done) => {
305
- for (const socket of clients) socket.destroy();
426
+ for (const [socket, writer] of clients) {
427
+ writer.close();
428
+ socket.destroy();
429
+ }
306
430
  clients.clear();
307
431
  server.close(() => {
308
432
  if (process.platform !== "win32") {
@@ -341,6 +465,7 @@ export function connectBackgroundSession(opts) {
341
465
  return new Promise((resolve, reject) => {
342
466
  let settled = false;
343
467
  const socket = net.connect(pipePath);
468
+ const writer = createBoundedSocketWriter(socket);
344
469
  const timer = setTimeout(() => {
345
470
  if (!settled) {
346
471
  settled = true;
@@ -367,7 +492,7 @@ export function connectBackgroundSession(opts) {
367
492
  socket.unref?.();
368
493
  resolve({
369
494
  hello: message,
370
- send: (msg) => writeMessage(socket, msg),
495
+ send: (msg) => writer.write(msg),
371
496
  close: () => {
372
497
  // Best-effort graceful detach, then HARD destroy so the client
373
498
  // handle is released synchronously. `socket.end()` alone leaves
@@ -380,10 +505,11 @@ export function connectBackgroundSession(opts) {
380
505
  // the worker sees the socket close and finalizes the same as it
381
506
  // would on an explicit detach.
382
507
  try {
383
- writeMessage(socket, { type: "detach" });
508
+ writer.write({ type: "detach" });
384
509
  } catch {
385
510
  /* peer already gone */
386
511
  }
512
+ writer.close();
387
513
  socket.destroy();
388
514
  },
389
515
  });
@@ -398,7 +524,7 @@ export function connectBackgroundSession(opts) {
398
524
  }),
399
525
  );
400
526
  socket.on("connect", () => {
401
- writeMessage(socket, { type: "hello", token });
527
+ writer.write({ type: "hello", token });
402
528
  });
403
529
  socket.on("error", (error) => {
404
530
  if (!settled) {
@@ -23,5 +23,7 @@ export {
23
23
  isMcpAuthenticationError,
24
24
  isMcpRpcError,
25
25
  isTransientMcpError,
26
+ SUPPORTED_MCP_PROTOCOL_VERSIONS,
27
+ assertSupportedMcpProtocolVersion,
26
28
  _deps,
27
29
  } from "../harness/mcp-client.js";
@@ -17,8 +17,8 @@ const { buildExplicitHookShellInvocation } = hookShellCommand;
17
17
  export const MCP_HEADERS_HELPER_TIMEOUT_MS = 10_000;
18
18
  export const MCP_HEADERS_HELPER_MAX_OUTPUT_BYTES = 64 * 1024;
19
19
  const MAX_HELPER_COMMAND_BYTES = 32 * 1024;
20
- const MAX_HEADER_COUNT = 128;
21
- const MAX_HEADER_VALUE_BYTES = 16 * 1024;
20
+ export const MCP_HEADERS_HELPER_MAX_HEADER_COUNT = 128;
21
+ export const MCP_HEADERS_HELPER_MAX_HEADER_VALUE_BYTES = 16 * 1024;
22
22
  const HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
23
23
  const TRANSPORT_OWNED_HEADERS = new Set([
24
24
  "accept",
@@ -37,6 +37,25 @@ const TRANSPORT_OWNED_HEADERS = new Set([
37
37
  "upgrade",
38
38
  ]);
39
39
  const PUBLIC_CONFIG_SCOPES = new Set(["local", "project", "user", "managed"]);
40
+ const HELPER_ENVIRONMENT_ALLOWLIST = new Set([
41
+ "COMSPEC",
42
+ "HOME",
43
+ "LANG",
44
+ "LC_ALL",
45
+ "LC_CTYPE",
46
+ "NODE_EXTRA_CA_CERTS",
47
+ "PATH",
48
+ "PATHEXT",
49
+ "SSL_CERT_DIR",
50
+ "SSL_CERT_FILE",
51
+ "SYSTEMROOT",
52
+ "TEMP",
53
+ "TMP",
54
+ "TMPDIR",
55
+ "USERPROFILE",
56
+ "WINDIR",
57
+ "XDG_CONFIG_HOME",
58
+ ]);
40
59
 
41
60
  function helperError(code, message) {
42
61
  const error = new Error(message);
@@ -98,6 +117,28 @@ function normalizeHelperCommand(value) {
98
117
  return value;
99
118
  }
100
119
 
120
+ export function sanitizeMcpHeadersHelperEnvironment(environment = process.env) {
121
+ if (!environment || typeof environment !== "object" || isProxy(environment)) {
122
+ throw helperError(
123
+ "CC_MCP_HEADERS_HELPER_ENV_INVALID",
124
+ "MCP headersHelper environment must be a non-Proxy object",
125
+ );
126
+ }
127
+ const sanitized = {};
128
+ for (const key of Object.keys(environment)) {
129
+ if (!HELPER_ENVIRONMENT_ALLOWLIST.has(key.toUpperCase())) continue;
130
+ const descriptor = Object.getOwnPropertyDescriptor(environment, key);
131
+ if (!descriptor || !("value" in descriptor)) {
132
+ throw helperError(
133
+ "CC_MCP_HEADERS_HELPER_ENV_INVALID",
134
+ "MCP headersHelper environment values must be own data properties",
135
+ );
136
+ }
137
+ if (typeof descriptor.value === "string") sanitized[key] = descriptor.value;
138
+ }
139
+ return sanitized;
140
+ }
141
+
101
142
  function canonicalHelperCwd(value, realpath = fs.realpathSync.native) {
102
143
  if (
103
144
  typeof value !== "string" ||
@@ -148,7 +189,7 @@ function normalizeHeaderMap(value, source) {
148
189
  );
149
190
  }
150
191
  const entries = Object.entries(value);
151
- if (entries.length > MAX_HEADER_COUNT) {
192
+ if (entries.length > MCP_HEADERS_HELPER_MAX_HEADER_COUNT) {
152
193
  throw helperError(
153
194
  "CC_MCP_HEADERS_HELPER_OUTPUT_INVALID",
154
195
  `MCP ${source} headers exceed the allowed entry count`,
@@ -160,7 +201,8 @@ function normalizeHeaderMap(value, source) {
160
201
  !HEADER_NAME.test(name) ||
161
202
  typeof headerValue !== "string" ||
162
203
  hasInvalidHeaderValueCharacter(headerValue) ||
163
- Buffer.byteLength(headerValue, "utf8") > MAX_HEADER_VALUE_BYTES ||
204
+ Buffer.byteLength(headerValue, "utf8") >
205
+ MCP_HEADERS_HELPER_MAX_HEADER_VALUE_BYTES ||
164
206
  (source === "dynamic" && TRANSPORT_OWNED_HEADERS.has(name.toLowerCase()))
165
207
  ) {
166
208
  throw helperError(
@@ -486,7 +528,7 @@ export function runMcpHeadersHelper(spec, deps = {}) {
486
528
  ),
487
529
  );
488
530
  const env = {
489
- ...(spec?.env || process.env),
531
+ ...sanitizeMcpHeadersHelperEnvironment(spec?.env || process.env),
490
532
  CLAUDE_CODE_MCP_SERVER_NAME: String(spec?.serverName || ""),
491
533
  CLAUDE_CODE_MCP_SERVER_URL: String(spec?.serverUrl || ""),
492
534
  ...(spec?.pluginRoot
@@ -52,6 +52,56 @@ export const _deps = {
52
52
  withStoreLock,
53
53
  };
54
54
 
55
+ /**
56
+ * Resolve the exact IPv4-loopback redirect used by both the listener and the
57
+ * OAuth wire exchange. Pre-registered clients need byte-for-byte redirect URI
58
+ * stability, so explicit values are accepted only in canonical form.
59
+ */
60
+ export function resolveOAuthLoopbackRedirect({
61
+ redirectUri,
62
+ host = "127.0.0.1",
63
+ port = 53682,
64
+ redirectPath = "/callback",
65
+ } = {}) {
66
+ if (redirectUri != null && typeof redirectUri !== "string") {
67
+ throw new Error("OAuth redirectUri must be a string");
68
+ }
69
+ const candidate =
70
+ redirectUri == null ? `http://${host}:${port}${redirectPath}` : redirectUri;
71
+ let parsed;
72
+ try {
73
+ parsed = new URL(candidate);
74
+ } catch {
75
+ throw new Error("OAuth redirectUri must be a valid loopback URL");
76
+ }
77
+ const parsedPort = Number(parsed.port);
78
+ const canonical = `http://127.0.0.1:${parsed.port}${parsed.pathname}`;
79
+ if (
80
+ parsed.protocol !== "http:" ||
81
+ parsed.hostname !== "127.0.0.1" ||
82
+ !parsed.port ||
83
+ !Number.isInteger(parsedPort) ||
84
+ parsedPort < 1 ||
85
+ parsedPort > 65535 ||
86
+ parsed.username ||
87
+ parsed.password ||
88
+ parsed.search ||
89
+ parsed.hash ||
90
+ !parsed.pathname.startsWith("/") ||
91
+ candidate !== canonical
92
+ ) {
93
+ throw new Error(
94
+ "OAuth redirectUri must be an exact canonical http://127.0.0.1:<port>/<path> URL",
95
+ );
96
+ }
97
+ return {
98
+ redirectUri: candidate,
99
+ host: "127.0.0.1",
100
+ port: parsedPort,
101
+ redirectPath: parsed.pathname,
102
+ };
103
+ }
104
+
55
105
  /** Discovery/token requests retry ONCE after a transient error (CC 2.1.191). */
56
106
  const MCP_OAUTH_RETRY_BACKOFF_MS = 300;
57
107
 
@@ -794,23 +844,22 @@ export function waitForCallback({
794
844
  * Run the full interactive Authorization Code + PKCE flow and persist the token.
795
845
  *
796
846
  * @param {string} serverUrl
797
- * @param {object} [opts] { scope, clientId, port=53682, host, redirectPath,
798
- * timeout, writeOut }
847
+ * @param {object} [opts] { scope, clientId, redirectUri, port=53682, host,
848
+ * redirectPath, timeout, writeOut }
799
849
  * @returns {Promise<{server, access_token, ...}>} the stored record
800
850
  */
801
851
  export async function authorizeInteractive(serverUrl, opts = {}) {
802
852
  const {
803
853
  scope,
804
854
  clientId: cfgClientId,
805
- port = 53682,
806
- host = "127.0.0.1",
807
- redirectPath = "/callback",
808
855
  timeout = 300_000,
809
856
  writeOut = (s) => process.stdout.write(s),
810
857
  } = opts;
811
858
 
859
+ const { redirectUri, host, port, redirectPath } =
860
+ resolveOAuthLoopbackRedirect(opts);
861
+
812
862
  const metadata = await discoverAuthMetadata(serverUrl);
813
- const redirectUri = `http://${host}:${port}${redirectPath}`;
814
863
 
815
864
  let clientId = cfgClientId;
816
865
  let clientSecret = null;