chainlesschain 0.165.5 → 0.165.8

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 (256) hide show
  1. package/package.json +2 -1
  2. package/src/assets/web-panel/assets/{AIOps-CEeYTeDB.js → AIOps-B3gWmXjo.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BeRUQ3V2.js → ActionButton-G9pdT-0e.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DrV718XF.js → Analytics-Ca30J7m2.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-Cd2Frt7Q.js → AppLayout-BtVTm8PU.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-CUJMlORU.js → Artifacts-BhViNa6u.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CXJcfT6p.js → Audit-DhKq-FnY.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-DOv6Z7H9.js → BackgroundAgents-BL0SVbnE.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BwdDyBI0.js → Backup-BWkblnFi.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-BmLn4kYH.js → BaseInput-ClaRNrF8.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-COg8pAW2.js → Chat-sZE2Z77o.js} +4 -4
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-BAfKOnRe.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-BCP4uoU8.js → Checkbox-Cvhay5WM.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-pRt6Y9JI.js → Codegen-BpejL-4N.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DLaprKhS.js → Col-g2Ki6g_1.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DyATsz5f.js → Community-D2xKbNo9.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-BXAaupZK.js → Compact-DbL1Pds3.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-DpcJBELr.js → Compliance-DGtZCV4c.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-DG4EFWyd.js → Cowork-4kQpoNsI.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-F6Oy5n2P.js → Cron-BxTYXXAN.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BOdgtalX.js → Crosschain-k0J0LT2N.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-DT1dSkjH.js → DID-BaqhoPgN.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-NBUIqWzC.js → Dashboard-T4RVPIM1.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CSpHeMYB.js → Dropdown-DszOToWq.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-CEG0xISr.js → EmailListRenderer-C9KJU7HF.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BTZ0BZna.js → FamilyGuardDashboard-Cmr0JnsA.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-BuuCTn2V.js → Federation-BI-_2xZX.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-KAVceevk.js → FormItemContext-DsnDEPsl.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-DnHpcQvW.js → GenericCardRenderer-BLncAKKK.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-C_B_0HwB.js → Git-BPQhGXDn.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-DPSZuzuu.js → Governance-CyElbUQW.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-BV-06V7q.js → Inference-CcSntBQD.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-wpDuupn6.js → KnowledgeGraph-p6e7HZ2r.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-Bseca1_I.js → Logs-CNEtr-wz.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-Bj9xI0YH.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-d3kM4DDL.js → Marketplace-Dd2notAJ.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-DQ_xZ23i.js → McpTools-BBsN2CSW.js} +4 -4
  38. package/src/assets/web-panel/assets/{Memory-CRIeYO4L.js → Memory-Dg2uTvwx.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BsllIa9l.js → MobileBridge-r1YMRiJV.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-BUuQy30X.js → MobileProjects-C-89kSm4.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-BOFDIzrC.js → Mtc-CrPZcVgf.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-BQg-vyvG.js → MtcAudit-sxi7hqJb.js} +5 -5
  43. package/src/assets/web-panel/assets/{Multisig-Bo1jrPdN.js → Multisig-CAgOMfF1.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-Bmod8alw.js → NLProgramming-DafaPts3.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CGGFL8w-.js → Notes-CJM-xUW_.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-BsZvCJxC.js → NotificationSettings-L0ZjfAh_.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-C1-Quo_h.js → OrderTableRenderer-BB1CvCK0.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-BjOeDYOi.js → Organization-DeLtgAD8.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-DwVV3l5A.js → Overflow-CKC2b10z.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-Cu3DQT9W.js → P2P-eyf2rzY1.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Cg0gR7dH.js → PdhVaultBrowser-5upNIOW8.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-CsAXxRN5.js → Permissions-B8p3W6JK.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-C-nNmnYp.js → PersonalDataHub-BnRMxTq3.js} +5 -5
  54. package/src/assets/web-panel/assets/{Pipeline-BOnW7nNc.js → Pipeline-DjCNI1li.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-ClLSyGKt.js → Privacy-u6gXLbWT.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-DuLvjjq7.js → ProjectInit-BZAw4tkM.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-DQYYbDD9.js → ProjectSettings-B1Tr72q7.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-Drw8MBGK.js → Projects-DO7HFuBQ.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-ckqHQe6K.js → Providers-BjCztyhj.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DW5BXk-G.js → QrScannerModal-ZWyCUbR1.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-DDLnhNTp.js → QuickAsk-DhDjeW4G.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-PJQ2C91U.js → Recommend-R_CN2MuU.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-s6wmda34.js → RemoteSession-uxFcujeu.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-DC2sb-I3.js → Reputation-DX8D7rks.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-hPGk1eGj.js → Row-BYahQILD.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-RIvfZql0.js → RssFeed-CUSQ2tm-.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DFg1G1eR.js → Search-CnN_S8Cs.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-D6H5p8Dp.js → Security-DcZlh-O4.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-nxYiFV_w.js → Services-ZhBOjzIX.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-BmgFNAdB.js → Skeleton-D5Qnq_lI.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-CsaaD2av.js → Skills-BC9cZ3Be.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-yM5y9yvK.js → Sla-DauJ3E8_.js} +1 -1
  73. package/src/assets/web-panel/assets/SpeechSettings-DbNeZ_fh.js +1 -0
  74. package/src/assets/web-panel/assets/{SyncSettings-CFVhEyyr.js → SyncSettings-DUvJbSct.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-DdO02Inn.js → Tasks-BXa-5noQ.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-Eo2d8Llu.js → Templates-JxaiUt_3.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-ChESOXgy.js → Tenant-BW4ERQN8.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BJ7oDxTI.js → Terminal-DltUbdtv.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-hWX0fUvz.js → TimelineRenderer-CMoZz0Tc.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-DVBZQnd-.js → Tokens-D_VEV60K.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-D89cQfVq.js → Trigger-CCXTCs0R.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-BIVSb4-V.js → Trust-lkGRIPcG.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-B58gWopl.js → UkeySign-BfGeQ9HX.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-DhHI7bmG.js → VideoEditing-BEC7pYvc.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-C4FheWS_.js → Wallet-BZdG24o0.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-DnPRq-Bx.js → WebAuthn-yoEddedp.js} +3 -3
  87. package/src/assets/web-panel/assets/{WorkflowEditor-BerRPjpJ.js → WorkflowEditor-BXZEprOW.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-BzZ52iKr.js → chat-gg0bzZal.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-BI_E4BAn.js → colors-DjRVRuin.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-d50mr3yk.js → compact-item-su-dieAc.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-C6FsQ1IH.js → createContext-Ci3S8oKF.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-CxbFX9uH.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DLZEMgLP.js → hasIn-D8F10heH.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-D-aBumtY.js → index-11wtYQiF.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CjuKZBqJ.js → index-1IgxgALJ.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-9H_M2jju.js → index-1agckWSq.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DE0V6cp-.js → index-5JqhujnR.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Y2-q7QLm.js → index-6Tniqk1s.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DoFtCVZ7.js → index-8Z-8BIlN.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-KNGgO4Wm.js → index-BCI5v0yB.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DY4j5zkF.js → index-BDJntYmJ.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BZjjdQ62.js → index-BGzixOGZ.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BTpIOTfR.js → index-BMMOVoLD.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BB9SqVSt.js → index-BTFpdBtt.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-AHr7ryWf.js → index-Ba_HyPnE.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Pvf30m8Q.js → index-BbF1IHof.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CRN5TRTQ.js → index-BgT9su_e.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CUaEHfQ0.js → index-BzPB6kGZ.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CdKt9fOd.js → index-C6ygzplr.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Dec0Kf_Z.js → index-CC3ZDjzY.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-IPkwSAW_.js → index-CEmXRhAE.js} +1 -1
  112. package/src/assets/web-panel/assets/index-CJGOlduY.js +1 -0
  113. package/src/assets/web-panel/assets/{index-C3k8kevG.js → index-CMPqd5Cv.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-A5CDWP7y.js → index-COWd8uGx.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CO8DAF7U.js → index-CPIYSp4H.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CVzuawCX.js → index-CR0h6seT.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Co7s9LsB.js → index-CUmDhC4j.js} +1 -1
  118. package/src/assets/web-panel/assets/index-CkBCN5yH.js +1 -0
  119. package/src/assets/web-panel/assets/{index-CLoTQr0W.js → index-CusgZPtc.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-B81mo1Et.js → index-DBAETXft.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BcSUl9fA.js → index-DIJ2qFOC.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Q5Tn735s.js → index-Dbq4NMET.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-GXE0D8Nx.js → index-DkMEF5VT.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-TWG-_D1M.js → index-DkfQR3hM.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BAE3hwev.js → index-DnLucOn9.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CgkoH3_g.js → index-DugB_aKa.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BRGx3w6T.js → index-DyLUarhZ.js} +3 -3
  128. package/src/assets/web-panel/assets/{index-BeM4hfht.js → index-T6_6ygJZ.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DsscwxPE.js → index-Tu8eWqh-.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Bmr_vxXD.js → index-W7XUgdVL.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BoNZlzhQ.js → index-dHZinfko.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DqkbHHg8.js → index-jyNgp5FI.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-D-4Hzgej.js → initDefaultProps-Cv4p8e4w.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-D4Wl4xU7.js → motion-B8EwmMOw.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-Cd5eQvL0.js → move-B8R9F8TK.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-B4ilslCW.js → mtc-parser-DoFYz5Bp.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-Ckarx-tf.js → omit-CHWYLBjI.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-QJ0bpCmc.js → pickAttrs-CXuPDO77.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-CKHm-tq8.js → placementArrow-BaAtUUon.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-CV6a_w_Y.js → responsiveObserve-DCc4zcDD.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-mHpeQFgk.js → slide-B_lx89oZ.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-DIaQNQWA.js → statusUtils-DE8DPmR0.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-PJpzavV8.js → styleChecker-1ktXOsL_.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-BH7zTw8S.js → useFlexGapSupport-xH89-wjh.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-BDNG3JDd.js → useFs-DscvfW5p.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-CY2VWdUg.js → usePersonalDataHub-RdMKQta7.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-D0kw9jhz.js → vnode-Z3wTni2T.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-B5g7TN7r.js → zoom-B0iChCsm.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/command-help-index.json +3 -3
  151. package/src/commands/agent.js +105 -62
  152. package/src/commands/background-session.js +100 -10
  153. package/src/commands/git.js +39 -0
  154. package/src/commands/mcp.js +5 -0
  155. package/src/commands/plugin.js +160 -29
  156. package/src/commands/remote-control.js +295 -110
  157. package/src/commands/session-group.js +131 -0
  158. package/src/commands/session-location.js +48 -0
  159. package/src/commands/session-message.js +171 -0
  160. package/src/commands/session.js +22 -0
  161. package/src/commands/sync-providers.js +4 -1
  162. package/src/commands/update.js +1 -0
  163. package/src/data/changelog.json +41 -2
  164. package/src/gateways/ws/ws-agent-handler.js +20 -12
  165. package/src/harness/jsonl-session-store.js +279 -37
  166. package/src/harness/mcp-client.js +832 -91
  167. package/src/harness/session-index.js +15 -4
  168. package/src/lib/agent-worktree.js +8 -1
  169. package/src/lib/auto-mode-safety-classifier.js +3 -17
  170. package/src/lib/background-agent-keeper-protocol.js +26 -6
  171. package/src/lib/background-agent-supervisor.js +17 -1
  172. package/src/lib/background-session-transport.js +153 -27
  173. package/src/lib/browser-evidence.js +1405 -0
  174. package/src/lib/chrome-connector.js +465 -79
  175. package/src/lib/claude-project-auto-memory.js +481 -0
  176. package/src/lib/claude-project-storage-layout.js +104 -0
  177. package/src/lib/cloud/cloud-client.js +40 -0
  178. package/src/lib/execution-location-local-supervisor.mjs +226 -0
  179. package/src/lib/execution-location-runner-lifecycle.js +991 -0
  180. package/src/lib/execution-location-target-preflight.js +333 -0
  181. package/src/lib/execution-location-target.js +783 -13
  182. package/src/lib/file-lines.js +6 -1
  183. package/src/lib/gateway-client-resilience.js +571 -0
  184. package/src/lib/gitlab-mr-worktree.js +176 -0
  185. package/src/lib/hooks-v2-producers.js +119 -15
  186. package/src/lib/hooks-v2-workspace-context.js +44 -1
  187. package/src/lib/host-resource-budget.js +295 -0
  188. package/src/lib/ide-context.js +15 -2
  189. package/src/lib/llm-config-defaults.js +43 -0
  190. package/src/lib/mcp-client.js +2 -0
  191. package/src/lib/mcp-headers-helper.js +47 -5
  192. package/src/lib/mcp-lifecycle-authority.js +887 -0
  193. package/src/lib/mcp-oauth.js +156 -26
  194. package/src/lib/mcp-tls.js +330 -0
  195. package/src/lib/output-styles.js +12 -0
  196. package/src/lib/paths.js +260 -32
  197. package/src/lib/permission-request.js +48 -0
  198. package/src/lib/plugin-runtime/bin.js +3 -1
  199. package/src/lib/plugin-runtime/hooks.js +3 -1
  200. package/src/lib/plugin-runtime/install.js +730 -71
  201. package/src/lib/plugin-runtime/lsp.js +4 -4
  202. package/src/lib/plugin-runtime/marketplace-archive-source.js +366 -0
  203. package/src/lib/plugin-runtime/marketplace-catalog.js +63 -6
  204. package/src/lib/plugin-runtime/marketplace-command-descriptor.js +343 -0
  205. package/src/lib/plugin-runtime/marketplace-command-source.js +580 -0
  206. package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1 -0
  207. package/src/lib/plugin-runtime/marketplace-source-adapter.js +188 -0
  208. package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
  209. package/src/lib/plugin-runtime/mcp.js +3 -1
  210. package/src/lib/plugin-runtime/monitors.js +3 -1
  211. package/src/lib/plugin-runtime/remote-source.js +583 -58
  212. package/src/lib/plugin-runtime/settings.js +3 -1
  213. package/src/lib/plugin-runtime/trust.js +274 -13
  214. package/src/lib/plugin-security.js +393 -41
  215. package/src/lib/plugin-source-identity.js +850 -0
  216. package/src/lib/process-execution-broker/credential-agent.js +210 -41
  217. package/src/lib/process-execution-broker/index.js +15 -1
  218. package/src/lib/process-execution-broker/linux-cgroup-v2.js +391 -0
  219. package/src/lib/process-execution-broker/platform-sandbox.js +50 -28
  220. package/src/lib/project-mcp-trust.js +165 -10
  221. package/src/lib/remote-approval-bridge.js +136 -46
  222. package/src/lib/remote-control.js +217 -31
  223. package/src/lib/resume-session.js +9 -6
  224. package/src/lib/session-core-singletons.js +26 -11
  225. package/src/lib/session-host-lease.js +52 -16
  226. package/src/lib/session-lifecycle-scope.js +153 -0
  227. package/src/lib/session-message-fabric.js +1252 -0
  228. package/src/lib/session-projection.js +286 -3
  229. package/src/lib/session-runtime-retention.js +155 -0
  230. package/src/lib/session-store-guard.js +3 -1
  231. package/src/lib/session-workbench-store.js +466 -0
  232. package/src/lib/settings-loader.cjs +29 -0
  233. package/src/lib/spellcheck.js +211 -0
  234. package/src/lib/sub-agent-context.js +9 -9
  235. package/src/lib/sync-cli-db.js +485 -129
  236. package/src/lib/sync-credentials.js +31 -20
  237. package/src/lib/web-fetch.js +112 -40
  238. package/src/lib/web-search.js +127 -61
  239. package/src/lib/workspace-trust.js +793 -0
  240. package/src/repl/agent-repl.js +175 -19
  241. package/src/repl/permission-prompt.js +29 -8
  242. package/src/repl/repl-keybindings.js +55 -0
  243. package/src/runtime/agent-core.js +362 -20
  244. package/src/runtime/bootstrap.js +37 -13
  245. package/src/runtime/coding-agent-contract-shared.cjs +11 -4
  246. package/src/runtime/headless-runner.js +281 -102
  247. package/src/runtime/headless-stream.js +199 -30
  248. package/src/runtime/mcp-config.js +298 -6
  249. package/src/runtime/policies/agent-policy.js +10 -0
  250. package/src/runtime/status-diagnostics-lite.js +9 -1
  251. package/src/assets/web-panel/assets/ChatBubbleRenderer-4gGkdGgA.js +0 -1
  252. package/src/assets/web-panel/assets/MarkdownRenderer-B4RLp2zs.js +0 -1
  253. package/src/assets/web-panel/assets/SpeechSettings-DPy1XMYh.js +0 -1
  254. package/src/assets/web-panel/assets/devWarning-CagEcIex.js +0 -1
  255. package/src/assets/web-panel/assets/index-pzT9PpOU.js +0 -1
  256. package/src/assets/web-panel/assets/index-z1kFadaX.js +0 -1
@@ -16,11 +16,12 @@
16
16
  * 2. Single-plugin manifest — one plugin, no selection needed:
17
17
  * { "name": "toml-tools", "source": "https://github.com/x/toml.git", "ref": "v1.0.0" }
18
18
  *
19
- * The remote layer is an INDIRECTION: it resolves a name → a git source string
20
- * that the existing installer (`installFromSource`) already knows how to clone.
21
- * This keeps the sync install core untouched and avoids bundling a tarball
22
- * extractor. Downloaded registry JSON is cached content-addressed so browsing /
23
- * installing works OFFLINE, and a bearer token can gate a PRIVATE registry.
19
+ * The remote layer is an INDIRECTION: it resolves a name to a static package
20
+ * source that the existing installer (`installFromSource`) can consume.
21
+ * Static git and digest-bound archive sources both reuse the existing install
22
+ * core. Downloaded registry JSON and archives are cached content-addressed so
23
+ * browsing / installing works OFFLINE, while bearer tokens never become
24
+ * persisted source authority.
24
25
  */
25
26
 
26
27
  import fs from "fs";
@@ -28,7 +29,26 @@ import os from "os";
28
29
  import path from "path";
29
30
  import crypto from "crypto";
30
31
  import { getElectronUserDataDir } from "../paths.js";
32
+ import {
33
+ enforcePluginSourcePolicy,
34
+ resolvePluginManagedPolicy,
35
+ } from "../plugin-security.js";
36
+ import {
37
+ assertPluginGitTransportSafe,
38
+ parsePluginGitSource,
39
+ redactPluginSourceForDisplay,
40
+ } from "../plugin-source-identity.js";
31
41
  import { createMarketplaceNetworkTransport } from "./marketplace-network.js";
42
+ import { fetchAndMaterializeMarketplaceArchive } from "./marketplace-archive-source.js";
43
+ import {
44
+ createSameOriginMarketplaceHeaderFetch,
45
+ runMarketplaceCommandSource,
46
+ runMarketplaceHeadersHelper,
47
+ } from "./marketplace-command-source.js";
48
+ import {
49
+ assertMarketplaceSourceExecutable,
50
+ normalizeMarketplacePackageSource,
51
+ } from "./marketplace-source-adapter.js";
32
52
 
33
53
  export const _deps = {
34
54
  // Node 22+ global fetch; injectable for tests.
@@ -52,6 +72,11 @@ const DEFAULT_TIMEOUT_MS = 15_000;
52
72
  export const MAX_REGISTRY_DOCUMENT_BYTES = 4 * 1024 * 1024;
53
73
  export const MAX_REGISTRY_CACHE_CANDIDATES = 64;
54
74
  const SHA256_RE = /^[a-f0-9]{64}$/;
75
+ const CONTROL_RE = /[\u0000-\u001f\u007f]/u;
76
+ const MAX_REGISTRY_SOURCE_CHARS = 4096;
77
+ const MAX_REGISTRY_REF_CHARS = 256;
78
+ const fetchedRegistryAuthorities = new WeakMap();
79
+ const registryResolutionAuthorities = new WeakMap();
55
80
 
56
81
  /** True when `raw` looks like a remote registry/manifest URL (http(s) + .json). */
57
82
  export function isRemoteSource(raw) {
@@ -67,15 +92,19 @@ export function isRemoteSource(raw) {
67
92
  * explicit opt-in (opts.allowInsecure / CC_PLUGIN_REGISTRY_ALLOW_HTTP=1).
68
93
  */
69
94
  export function assertRegistryUrlSafe(url, { allowInsecure = false } = {}) {
95
+ const displayUrl = redactPluginSourceForDisplay(url);
70
96
  let u;
71
97
  try {
72
98
  u = new URL(String(url));
73
99
  } catch {
74
- throw new Error(`invalid registry URL: ${url}`);
100
+ throw new Error(`invalid registry URL: ${displayUrl}`);
101
+ }
102
+ if (u.username || u.password) {
103
+ throw new Error("registry URL credentials are not supported");
75
104
  }
76
105
  if (u.protocol === "https:") return;
77
106
  if (u.protocol !== "http:") {
78
- throw new Error(`registry URL must be http(s): ${url}`);
107
+ throw new Error(`registry URL must be http(s): ${displayUrl}`);
79
108
  }
80
109
  // WHATWG URL keeps the brackets on IPv6 hostnames — strip before comparing.
81
110
  const host = u.hostname.replace(/^\[|\]$/g, "").toLowerCase();
@@ -85,7 +114,7 @@ export function assertRegistryUrlSafe(url, { allowInsecure = false } = {}) {
85
114
  allowInsecure === true || process.env.CC_PLUGIN_REGISTRY_ALLOW_HTTP === "1";
86
115
  if (loopback || optIn) return;
87
116
  throw new Error(
88
- `plain-HTTP registry rejected: ${url} — a network MITM controls both the ` +
117
+ `plain-HTTP registry rejected: ${displayUrl} — a network MITM controls both the ` +
89
118
  `source and its sha256, so the integrity check verifies nothing. Use ` +
90
119
  `https, or opt in with --allow-insecure-registry / ` +
91
120
  `CC_PLUGIN_REGISTRY_ALLOW_HTTP=1 on a trusted network.`,
@@ -143,6 +172,16 @@ export function resolveRegistryToken(url, { token, config } = {}) {
143
172
  * documentSha256: string }>}
144
173
  */
145
174
  export async function fetchRegistry(url, opts = {}) {
175
+ const displayUrl = redactPluginSourceForDisplay(url);
176
+ assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
177
+ const managedPolicy = resolveManagedPolicy(opts);
178
+ // This gate intentionally runs before transport construction. A blocked or
179
+ // non-allowlisted marketplace must not reach proxy helpers, DNS, or fetch.
180
+ enforcePluginSourcePolicy(url, managedPolicy, {
181
+ action: opts.policyAction || "registry-fetch",
182
+ cwd: opts.cwd,
183
+ kindHint: "registry",
184
+ });
146
185
  const {
147
186
  token,
148
187
  cacheDir,
@@ -150,7 +189,6 @@ export async function fetchRegistry(url, opts = {}) {
150
189
  allowCache = true,
151
190
  offline = false,
152
191
  } = opts;
153
- assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
154
192
  const expectedSha256 = normalizeRegistrySha256(
155
193
  opts.expectedSha256,
156
194
  "expected registry document SHA-256",
@@ -163,24 +201,46 @@ export async function fetchRegistry(url, opts = {}) {
163
201
  if (!allowCache) {
164
202
  throw new Error("offline registry mode requires immutable cache access");
165
203
  }
166
- const cached = readCachedRegistry({
167
- url,
168
- cacheDir,
169
- expectedSha256,
170
- });
171
- if (cached) return cached;
204
+ let cached;
205
+ try {
206
+ cached = readCachedRegistry({
207
+ url,
208
+ cacheDir,
209
+ expectedSha256,
210
+ });
211
+ } catch (error) {
212
+ throw new Error(
213
+ `registry ${displayUrl} verified immutable cache rejected: ${publicRegistryCacheErrorMessage(error)}`,
214
+ );
215
+ }
216
+ if (cached) return bindFetchedRegistry(cached, url, managedPolicy);
172
217
  throw new Error(
173
- `registry ${url} is unavailable in the verified immutable cache` +
218
+ `registry ${displayUrl} is unavailable in the verified immutable cache` +
174
219
  (expectedSha256 ? ` at SHA-256 ${expectedSha256}` : ""),
175
220
  );
176
221
  }
177
222
 
223
+ // This descriptor is local operator configuration, never registry metadata.
224
+ // It is intentionally evaluated only for the registry request and later
225
+ // carried in an opaque authority for the selected same-origin archive.
226
+ const marketplaceRequestHeaders = await resolveMarketplaceCatalogHeaders(
227
+ url,
228
+ opts,
229
+ );
230
+
178
231
  const networkTransport = createMarketplaceNetworkTransport({
179
232
  proxyUrl: opts.proxyUrl,
180
233
  pacFile: opts.pacFile,
181
234
  caFile: opts.caFile,
182
235
  });
183
- const fetchImpl = networkTransport?.fetch || _deps.fetch;
236
+ const transportFetch = networkTransport?.fetch || _deps.fetch;
237
+ const fetchImpl = marketplaceRequestHeaders
238
+ ? createSameOriginMarketplaceHeaderFetch(
239
+ transportFetch,
240
+ new URL(url).origin,
241
+ marketplaceRequestHeaders,
242
+ )
243
+ : transportFetch;
184
244
  let networkErr = null;
185
245
  let cacheFallbackAllowed = false;
186
246
  try {
@@ -190,37 +250,61 @@ export async function fetchRegistry(url, opts = {}) {
190
250
  try {
191
251
  res = await fetchImpl(url, {
192
252
  headers,
193
- redirect: "follow",
253
+ redirect: "manual",
194
254
  signal: controller.signal,
195
255
  });
196
256
  } finally {
197
257
  clearTimeout(timer);
198
258
  }
259
+ if (
260
+ res?.redirected === true ||
261
+ (Number(res?.status) >= 300 && Number(res?.status) < 400) ||
262
+ responseUrlChanged(url, res?.url)
263
+ ) {
264
+ throw publicRegistryFailure(
265
+ "registry redirects are disabled because each hop cannot be policy-validated",
266
+ );
267
+ }
199
268
  if (!res.ok) {
200
- const error = new Error(
201
- `registry fetch failed: HTTP ${res.status} ${res.statusText || ""}`.trim(),
269
+ const status = Number(res.status);
270
+ throw publicRegistryFailure(
271
+ `registry fetch failed: HTTP ${Number.isInteger(status) ? status : "unknown"}`,
272
+ { cacheFallbackAllowed: status >= 500 },
202
273
  );
203
- error.cacheFallbackAllowed = Number(res.status) >= 500;
204
- throw error;
205
274
  }
206
- const bytes = await readBoundedRegistryResponse(res);
275
+ let bytes;
276
+ try {
277
+ bytes = await readBoundedRegistryResponse(res);
278
+ } catch {
279
+ throw publicRegistryFailure("registry response body is invalid");
280
+ }
207
281
  const documentSha256 = sha256(bytes);
208
282
  if (expectedSha256 && documentSha256 !== expectedSha256) {
209
- throw new Error(
283
+ throw publicRegistryFailure(
210
284
  `registry document SHA-256 mismatch: expected ${expectedSha256}, got ${documentSha256}`,
211
285
  );
212
286
  }
213
- const text = decodeRegistryUtf8(bytes);
214
- const registry = validateRegistry(JSON.parse(text), url);
287
+ let registry;
288
+ try {
289
+ const text = decodeRegistryUtf8(bytes);
290
+ registry = validateRegistry(JSON.parse(text), url);
291
+ } catch {
292
+ throw publicRegistryFailure("registry document is invalid");
293
+ }
215
294
  if (allowCache) writeImmutableCache(url, cacheDir, bytes, documentSha256);
216
- return {
217
- registry,
218
- fromCache: false,
219
- documentSha256,
220
- ...(networkTransport?.authority
221
- ? { networkAuthority: networkTransport.authority }
222
- : {}),
223
- };
295
+ return bindFetchedRegistry(
296
+ {
297
+ registry,
298
+ fromCache: false,
299
+ documentSha256,
300
+ ...(networkTransport?.authority
301
+ ? { networkAuthority: networkTransport.authority }
302
+ : {}),
303
+ },
304
+ url,
305
+ managedPolicy,
306
+ marketplaceRequestHeaders,
307
+ );
224
308
  } catch (err) {
225
309
  networkErr = err;
226
310
  cacheFallbackAllowed =
@@ -228,7 +312,12 @@ export async function fetchRegistry(url, opts = {}) {
228
312
  err?.name === "AbortError" ||
229
313
  err instanceof TypeError;
230
314
  } finally {
231
- await networkTransport?.close();
315
+ try {
316
+ await networkTransport?.close();
317
+ } catch {
318
+ // Transport teardown is best-effort and must not override a verified
319
+ // response or surface an implementation error that echoed request URLs.
320
+ }
232
321
  }
233
322
 
234
323
  // Only transport failures and server-side outages may fall back. Authentication,
@@ -240,21 +329,60 @@ export async function fetchRegistry(url, opts = {}) {
240
329
  cacheDir,
241
330
  expectedSha256,
242
331
  });
243
- if (cached) return cached;
332
+ if (cached) {
333
+ return bindFetchedRegistry(
334
+ cached,
335
+ url,
336
+ managedPolicy,
337
+ marketplaceRequestHeaders,
338
+ );
339
+ }
244
340
  } catch (cacheError) {
245
341
  throw new Error(
246
- `could not fetch registry ${url}: ${networkErr.message}; verified immutable cache rejected: ${cacheError.message}`,
342
+ `could not fetch registry ${displayUrl}: ${publicRegistryErrorMessage(networkErr)}; verified immutable cache rejected: ${publicRegistryCacheErrorMessage(cacheError)}`,
247
343
  );
248
344
  }
249
345
  }
250
346
  throw new Error(
251
- `could not fetch registry ${url}: ${networkErr ? networkErr.message : "unknown error"}` +
347
+ `could not fetch registry ${displayUrl}: ${publicRegistryErrorMessage(networkErr)}` +
252
348
  (allowCache && cacheFallbackAllowed
253
349
  ? " (no verified immutable cache available)"
254
350
  : ""),
255
351
  );
256
352
  }
257
353
 
354
+ function configuredMarketplaceHeadersHelper(url, opts) {
355
+ if (opts.headersHelper != null) return opts.headersHelper;
356
+ const marketplace = opts.config?.plugins?.marketplace;
357
+ if (!marketplace || typeof marketplace !== "object") return null;
358
+ let origin = null;
359
+ try {
360
+ origin = new URL(url).origin;
361
+ } catch {
362
+ return null;
363
+ }
364
+ const perOrigin = marketplace.headersHelpers;
365
+ if (
366
+ perOrigin &&
367
+ typeof perOrigin === "object" &&
368
+ Object.prototype.hasOwnProperty.call(perOrigin, origin)
369
+ ) {
370
+ return perOrigin[origin];
371
+ }
372
+ return marketplace.headersHelper ?? null;
373
+ }
374
+
375
+ async function resolveMarketplaceCatalogHeaders(url, opts) {
376
+ const descriptor = configuredMarketplaceHeadersHelper(url, opts);
377
+ if (descriptor == null) return null;
378
+ return runMarketplaceHeadersHelper(descriptor, {
379
+ platform: opts.platform,
380
+ spawn: opts.headersHelperSpawn,
381
+ spawnSync: opts.headersHelperSpawnSync,
382
+ kill: opts.headersHelperKill,
383
+ });
384
+ }
385
+
258
386
  async function readBoundedRegistryResponse(response) {
259
387
  const contentLength = Number(response?.headers?.get?.("content-length"));
260
388
  if (
@@ -475,37 +603,220 @@ function sha256(value) {
475
603
 
476
604
  /** Validate + normalize a fetched document into a registry object. Throws on garbage. */
477
605
  export function validateRegistry(doc, url = "") {
606
+ const displayUrl = redactPluginSourceForDisplay(url);
478
607
  if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
479
- throw new Error(`registry at ${url} is not a JSON object`);
608
+ throw new Error(`registry at ${displayUrl} is not a JSON object`);
480
609
  }
481
610
  if (Array.isArray(doc.plugins)) {
482
611
  for (const p of doc.plugins) {
483
- if (!p || typeof p !== "object" || !p.name || !p.source) {
612
+ if (
613
+ !p ||
614
+ typeof p !== "object" ||
615
+ typeof p.name !== "string" ||
616
+ !p.name.trim() ||
617
+ (p.source == null && p.command == null && p.headersHelper == null)
618
+ ) {
484
619
  throw new Error(
485
- `registry at ${url} has a plugin entry missing name/source`,
620
+ `registry at ${displayUrl} has a plugin entry missing name/source`,
486
621
  );
487
622
  }
623
+ const normalized = normalizeMarketplacePackageSource(p);
624
+ if (normalized.type === "git") normalizeRegistryPluginSource(p);
488
625
  }
489
626
  return doc;
490
627
  }
491
628
  // Single-plugin manifest.
492
- if (doc.source) {
629
+ if (doc.source != null || doc.command != null || doc.headersHelper != null) {
630
+ const normalized = normalizeMarketplacePackageSource(doc);
631
+ if (normalized.type === "git") normalizeRegistryPluginSource(doc);
493
632
  return { plugins: [doc] };
494
633
  }
495
634
  throw new Error(
496
- `registry at ${url} must have a "plugins" array or a top-level "source"`,
635
+ `registry at ${displayUrl} must have a "plugins" array or a top-level "source"`,
636
+ );
637
+ }
638
+
639
+ function freezeRegistryValue(value) {
640
+ if (!value || typeof value !== "object" || Object.isFrozen(value)) {
641
+ return value;
642
+ }
643
+ for (const child of Object.values(value)) freezeRegistryValue(child);
644
+ return Object.freeze(value);
645
+ }
646
+
647
+ function bindFetchedRegistry(
648
+ result,
649
+ registryUrl,
650
+ managedPolicy,
651
+ marketplaceRequestHeaders = null,
652
+ ) {
653
+ const registry = freezeRegistryValue(result.registry);
654
+ fetchedRegistryAuthorities.set(
655
+ registry,
656
+ Object.freeze({
657
+ registryUrl: String(registryUrl),
658
+ documentSha256: result.documentSha256 || null,
659
+ managedPolicy,
660
+ // Credential values live only in this per-process, opaque binding. They
661
+ // never enter catalog, cache, provenance, or result JSON.
662
+ marketplaceRequestHeaders: marketplaceRequestHeaders
663
+ ? Object.freeze({ ...marketplaceRequestHeaders })
664
+ : null,
665
+ }),
666
+ );
667
+ return { ...result, registry };
668
+ }
669
+
670
+ /** Validate one catalog entry as a bounded, remote Git source. */
671
+ export function normalizeRegistryPluginSource(entry) {
672
+ if (!entry || typeof entry !== "object") {
673
+ throw new Error("registry plugin entry is invalid");
674
+ }
675
+ if (
676
+ typeof entry.source !== "string" ||
677
+ entry.source !== entry.source.trim() ||
678
+ !entry.source ||
679
+ entry.source.length > MAX_REGISTRY_SOURCE_CHARS ||
680
+ CONTROL_RE.test(entry.source) ||
681
+ entry.source.includes("#")
682
+ ) {
683
+ throw new Error("registry plugin source must be a bounded string");
684
+ }
685
+ let ref = null;
686
+ if (entry.ref != null && entry.ref !== "") {
687
+ if (
688
+ typeof entry.ref !== "string" ||
689
+ entry.ref !== entry.ref.trim() ||
690
+ !entry.ref ||
691
+ entry.ref.length > MAX_REGISTRY_REF_CHARS ||
692
+ CONTROL_RE.test(entry.ref) ||
693
+ entry.ref.includes("#") ||
694
+ entry.source.includes("#")
695
+ ) {
696
+ throw new Error("registry plugin ref must be a bounded exact string");
697
+ }
698
+ ref = entry.ref;
699
+ }
700
+ const source = ref ? `${entry.source}#${ref}` : entry.source;
701
+ let git;
702
+ try {
703
+ git = parsePluginGitSource(source);
704
+ assertPluginGitTransportSafe(git, {
705
+ allowFile: false,
706
+ requireRemote: true,
707
+ });
708
+ } catch {
709
+ throw new Error("registry plugin source must be a safe remote Git locator");
710
+ }
711
+ return { source, ref, git };
712
+ }
713
+
714
+ /**
715
+ * Issue an unforgeable in-process capability for a selected entry. The entry
716
+ * must be an exact member of a registry object returned by fetchRegistry;
717
+ * callers cannot manufacture registry metadata to downgrade source policy.
718
+ */
719
+ export function authorizeRegistryPluginEntry(
720
+ registry,
721
+ entry,
722
+ { registryUrl, cwd } = {},
723
+ ) {
724
+ const fetched = fetchedRegistryAuthorities.get(registry);
725
+ if (!fetched || fetched.registryUrl !== String(registryUrl)) {
726
+ throw new Error("registry source authority is missing or mismatched");
727
+ }
728
+ if (!registry.plugins?.some((candidate) => candidate === entry)) {
729
+ throw new Error("registry entry is not bound to the fetched catalog");
730
+ }
731
+ const normalized = inspectRegistryEntrySource(entry);
732
+ if (normalized.sourceType === "git") {
733
+ enforcePluginSourcePolicy(normalized.source, fetched.managedPolicy, {
734
+ action: "registry-resolved-source",
735
+ blockedOnly: true,
736
+ cwd,
737
+ kindHint: "git",
738
+ });
739
+ } else if (normalized.sourceType === "archive") {
740
+ let registryOrigin;
741
+ let archiveOrigin;
742
+ try {
743
+ registryOrigin = new URL(fetched.registryUrl).origin;
744
+ archiveOrigin = new URL(normalized.sourceIdentity).origin;
745
+ } catch {
746
+ throw new Error("registry archive source URL authority is invalid");
747
+ }
748
+ if (archiveOrigin !== registryOrigin) {
749
+ throw new Error("registry archive source must use the registry origin");
750
+ }
751
+ } else if (normalized.sourceType !== "command") {
752
+ throw new Error("registry package source type is unsupported");
753
+ }
754
+ const authority = Object.freeze({});
755
+ registryResolutionAuthorities.set(
756
+ authority,
757
+ Object.freeze({
758
+ registryUrl: fetched.registryUrl,
759
+ source: normalized.source,
760
+ declaredSource: normalized.sourceIdentity,
761
+ sourceType: normalized.sourceType,
762
+ ref: normalized.ref,
763
+ archiveSha256:
764
+ normalized.sourceType === "archive"
765
+ ? normalized.archiveSpec.sha256
766
+ : null,
767
+ payloadSha256: null,
768
+ documentSha256: fetched.documentSha256,
769
+ commandMode:
770
+ normalized.sourceType === "command" ? normalized.commandSpec.mode : null,
771
+ marketplaceRequestHeaders: fetched.marketplaceRequestHeaders,
772
+ }),
497
773
  );
774
+ return {
775
+ ...normalized,
776
+ registryResolutionAuthority: authority,
777
+ };
778
+ }
779
+
780
+ /** Consume-only validation used by the installer before any target I/O. */
781
+ export function assertRegistryResolutionAuthority(
782
+ authority,
783
+ { registryUrl, source, ref } = {},
784
+ ) {
785
+ const issued = registryResolutionAuthorities.get(authority);
786
+ if (
787
+ !issued ||
788
+ issued.registryUrl !== String(registryUrl) ||
789
+ (issued.source ?? null) !== (source ?? null) ||
790
+ (issued.ref || null) !== (ref || null)
791
+ ) {
792
+ throw new Error("registry source authority is missing or mismatched");
793
+ }
794
+ return issued;
498
795
  }
499
796
 
500
797
  /** List installable entries from a registry (for `cc plugin search`). */
501
798
  export function listRegistryPlugins(registry) {
502
- return (registry.plugins || []).map((p) => ({
503
- name: p.name,
504
- version: p.version || null,
505
- source: p.source,
506
- ref: p.ref || null,
507
- description: p.description || "",
508
- }));
799
+ return (registry.plugins || []).map((p) => {
800
+ const source = normalizeMarketplacePackageSource(p);
801
+ return {
802
+ name: p.name,
803
+ version: p.version || null,
804
+ source:
805
+ source.type === "archive"
806
+ ? source.url
807
+ : source.type === "git"
808
+ ? source.source
809
+ : null,
810
+ sourceType: source.type,
811
+ ref: source.type === "git" ? source.ref : null,
812
+ ...(source.type === "command" ? { mode: source.mode } : {}),
813
+ description: p.description || "",
814
+ enabled: source.type !== "dynamic-disabled",
815
+ ...(source.type === "dynamic-disabled"
816
+ ? { disabledReason: source.reason }
817
+ : {}),
818
+ };
819
+ });
509
820
  }
510
821
 
511
822
  /**
@@ -543,25 +854,239 @@ export function resolvePluginEntry(registry, name) {
543
854
  * entry: object, fromCache: boolean }>}
544
855
  */
545
856
  export async function resolveRemoteSource(url, opts = {}) {
857
+ assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
858
+ const managedPolicy = resolveManagedPolicy(opts);
546
859
  const token = resolveRegistryToken(url, opts);
547
860
  const { registry, fromCache, documentSha256, networkAuthority } =
548
861
  await fetchRegistry(url, {
549
862
  ...opts,
550
863
  token,
864
+ managedPolicy,
551
865
  });
552
866
  const entry = resolvePluginEntry(registry, opts.name);
553
- // Carry a `#ref` on the source string so installFromSource pins the checkout,
554
- // matching its existing `owner/repo#ref` / `url#ref` convention.
555
- const source = entry.ref ? `${entry.source}#${entry.ref}` : entry.source;
556
- return {
557
- source,
558
- ref: entry.ref || null,
559
- sha256: entry.sha256 || null,
560
- entry,
867
+ const authorized = authorizeRegistryPluginEntry(registry, entry, {
868
+ registryUrl: url,
869
+ cwd: opts.cwd,
870
+ });
871
+ const resolvedSource =
872
+ opts.materialize === false
873
+ ? authorized
874
+ : await resolveRegistryEntrySource(url, entry, {
875
+ ...opts,
876
+ token,
877
+ registryResolutionAuthority: authorized.registryResolutionAuthority,
878
+ });
879
+ const result = {
880
+ ...resolvedSource,
561
881
  fromCache,
562
882
  documentSha256,
563
883
  ...(networkAuthority ? { networkAuthority } : {}),
564
884
  };
885
+ // Command descriptors can contain local paths or credentials. Retain the
886
+ // exact registry entry for the immediate authorized materialization flow,
887
+ // but keep it out of generic result serialization/audit output.
888
+ Object.defineProperty(result, "entry", {
889
+ value: entry,
890
+ enumerable: false,
891
+ });
892
+ return result;
893
+ }
894
+
895
+ /** Inspect one selected entry without fetching any candidate source bytes. */
896
+ export function inspectRegistryEntrySource(entry) {
897
+ const packageSource = assertMarketplaceSourceExecutable(entry);
898
+ if (packageSource.type === "archive") {
899
+ return {
900
+ source: null,
901
+ sourceIdentity: packageSource.url,
902
+ sourceType: "archive",
903
+ ref: null,
904
+ sha256: entry.sha256 || null,
905
+ archiveSpec: packageSource,
906
+ };
907
+ }
908
+ if (packageSource.type === "command") {
909
+ const inspected = {
910
+ source: null,
911
+ sourceIdentity: packageSource.identity,
912
+ sourceType: "command",
913
+ ref: null,
914
+ sha256: entry.sha256 || null,
915
+ };
916
+ // The descriptor may contain local credentials. It remains available to
917
+ // the immediate materializer but never spreads into a catalog/result.
918
+ Object.defineProperty(inspected, "commandSpec", {
919
+ value: packageSource,
920
+ enumerable: false,
921
+ });
922
+ return inspected;
923
+ }
924
+ // Preserve the canonical remote Git validation from the registry security
925
+ // boundary rather than accepting the adapter's more general local shape.
926
+ const normalized = normalizeRegistryPluginSource(entry);
927
+ return {
928
+ source: normalized.source,
929
+ sourceIdentity: entry.source,
930
+ sourceType: "git",
931
+ ref: normalized.ref,
932
+ sha256: entry.sha256 || null,
933
+ };
934
+ }
935
+
936
+ /** Materialize one already-selected, preflight-approved registry entry. */
937
+ export async function resolveRegistryEntrySource(url, entry, opts = {}) {
938
+ const inspected = inspectRegistryEntrySource(entry);
939
+ const issued = assertRegistryResolutionAuthority(
940
+ opts.registryResolutionAuthority,
941
+ {
942
+ registryUrl: url,
943
+ source: inspected.source,
944
+ ref: inspected.ref,
945
+ },
946
+ );
947
+ if (
948
+ issued.sourceType !== inspected.sourceType ||
949
+ issued.declaredSource !== inspected.sourceIdentity ||
950
+ issued.archiveSha256 !==
951
+ (inspected.sourceType === "archive" ? inspected.archiveSpec.sha256 : null)
952
+ ) {
953
+ throw new Error("registry source authority is missing or mismatched");
954
+ }
955
+ if (inspected.sourceType === "archive") {
956
+ const archive = await fetchAndMaterializeMarketplaceArchive({
957
+ registryUrl: url,
958
+ url: inspected.archiveSpec.requestUrl || inspected.archiveSpec.url,
959
+ sha256: inspected.archiveSpec.sha256,
960
+ token: opts.token,
961
+ allowInsecure: opts.allowInsecure === true,
962
+ offline: opts.offline === true,
963
+ artifactCacheDir: opts.artifactCacheDir,
964
+ sourceCacheDir: opts.archiveSourceCacheDir,
965
+ timeoutMs: opts.timeoutMs,
966
+ fetchImpl: opts.archiveFetchImpl,
967
+ resolveHostname: opts.resolveHostname,
968
+ proxyUrl: opts.proxyUrl,
969
+ pacFile: opts.pacFile,
970
+ caFile: opts.caFile,
971
+ requestHeaders: issued.marketplaceRequestHeaders,
972
+ });
973
+ const materializedAuthority = Object.freeze({});
974
+ registryResolutionAuthorities.set(
975
+ materializedAuthority,
976
+ Object.freeze({
977
+ ...issued,
978
+ source: archive.dir,
979
+ payloadSha256: archive.authority.payloadSha256,
980
+ }),
981
+ );
982
+ return {
983
+ ...inspected,
984
+ source: archive.dir,
985
+ archiveAuthority: archive.authority,
986
+ registryResolutionAuthority: materializedAuthority,
987
+ };
988
+ }
989
+ if (inspected.sourceType === "command") {
990
+ const command = await runMarketplaceCommandSource(
991
+ {
992
+ executable: inspected.commandSpec.executable,
993
+ args: inspected.commandSpec.args,
994
+ cwd: inspected.commandSpec.cwd,
995
+ env: inspected.commandSpec.env,
996
+ timeoutMs: inspected.commandSpec.timeoutMs,
997
+ maxOutputBytes: inspected.commandSpec.maxOutputBytes,
998
+ mode: inspected.commandSpec.mode,
999
+ },
1000
+ {
1001
+ platform: opts.platform,
1002
+ spawn: opts.commandSpawn,
1003
+ spawnSync: opts.commandSpawnSync,
1004
+ kill: opts.commandKill,
1005
+ },
1006
+ );
1007
+ const materializedAuthority = Object.freeze({});
1008
+ registryResolutionAuthorities.set(
1009
+ materializedAuthority,
1010
+ Object.freeze({
1011
+ ...issued,
1012
+ source: command.source,
1013
+ payloadSha256: command.payloadSha256,
1014
+ commandMode: command.mode,
1015
+ fileCount: command.fileCount,
1016
+ totalBytes: command.totalBytes,
1017
+ }),
1018
+ );
1019
+ return {
1020
+ ...inspected,
1021
+ source: command.source,
1022
+ commandAuthority: Object.freeze({
1023
+ schemaVersion: "cc-plugin-marketplace-command-source/v1",
1024
+ status: "descriptor-executed-and-verified",
1025
+ descriptorIdentity: inspected.sourceIdentity,
1026
+ mode: command.mode,
1027
+ payloadSha256: command.payloadSha256,
1028
+ fileCount: command.fileCount,
1029
+ totalBytes: command.totalBytes,
1030
+ }),
1031
+ registryResolutionAuthority: materializedAuthority,
1032
+ };
1033
+ }
1034
+ return {
1035
+ ...inspected,
1036
+ registryResolutionAuthority: opts.registryResolutionAuthority,
1037
+ };
1038
+ }
1039
+
1040
+ function resolveManagedPolicy(opts) {
1041
+ return resolvePluginManagedPolicy(opts);
1042
+ }
1043
+
1044
+ function responseUrlChanged(requested, effective) {
1045
+ if (!effective) return false;
1046
+ try {
1047
+ const left = new URL(String(requested));
1048
+ const right = new URL(String(effective));
1049
+ for (const value of [left, right]) {
1050
+ value.username = "";
1051
+ value.password = "";
1052
+ value.hash = "";
1053
+ }
1054
+ return left.href !== right.href;
1055
+ } catch {
1056
+ return true;
1057
+ }
1058
+ }
1059
+
1060
+ function publicRegistryErrorMessage(error) {
1061
+ if (!error) return "unknown error";
1062
+ if (typeof error.publicMessage === "string") return error.publicMessage;
1063
+ if (error.name === "AbortError") return "registry request timed out";
1064
+ if (error instanceof TypeError) return "registry transport failed";
1065
+ return "registry request failed";
1066
+ }
1067
+
1068
+ function publicRegistryCacheErrorMessage(error) {
1069
+ const message = String(error?.message || "");
1070
+ if (message.includes("digest mismatch")) {
1071
+ return "registry immutable cache digest mismatch";
1072
+ }
1073
+ if (message.includes("ambiguous")) {
1074
+ return "registry immutable cache is ambiguous";
1075
+ }
1076
+ if (message.includes("more than")) {
1077
+ return "registry immutable cache has too many candidates";
1078
+ }
1079
+ if (message.includes("single-link regular file")) {
1080
+ return "registry immutable cache entry is invalid";
1081
+ }
1082
+ return "registry immutable cache document is invalid";
1083
+ }
1084
+
1085
+ function publicRegistryFailure(message, { cacheFallbackAllowed = false } = {}) {
1086
+ const error = new Error(message);
1087
+ error.publicMessage = message;
1088
+ error.cacheFallbackAllowed = cacheFallbackAllowed;
1089
+ return error;
565
1090
  }
566
1091
 
567
1092
  /** Best-effort local temp dir helper (kept here so tests can stub fs deps). */