chainlesschain 0.162.180 → 0.162.181

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 (249) hide show
  1. package/package.json +6 -3
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-D_TIL54O.js → AIOps-cq0dwbnf.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-eQwy9r-f.js → ActionButton-CvcgGl3w.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-D2eNuDgT.js → Analytics-hM2-jFqg.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-CFhwOFsf.js → AppLayout-B9_Gsd_g.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-CJUMw6PB.js → Artifacts-B7bu52Og.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-CDB9V58o.js → Audit-DThfDgqB.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-TBppYFjN.js → BackgroundAgents-DjlFiD4v.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-CYEOUpzJ.js → Backup-CK9xJKnG.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-C88fFT2u.js → BaseInput-DEiSU8K8.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-mkr1vC3N.js → Chat-CpT96czj.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-C7bofJuA.js → ChatBubbleRenderer-BKPPNmZI.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-CVU4xqti.js → Checkbox-C1GfDnO0.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-rrNcByz3.js → Codegen-3DAKgiLx.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-Cqu-zCvh.js → Col-74cOwZU1.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-RGRlSgXV.js → Community-tn9Kkz9j.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-DBm6epOK.js → Compact-BcoXH7m4.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-BgKw5iaW.js → Compliance-DIPGUBgi.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-BqQcNO3Z.js → Cowork-doD5_91c.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-DO5gTFNb.js → Cron-C-IDvubz.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-B8hbJNuN.js → Crosschain-CdcCoBdj.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-B08bFJ2C.js → DID-DdH5aMMA.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-xNdy_ifo.js → Dashboard-DHLA4mm0.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-D-KIZgWL.js → Dropdown-dEeCt7Md.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-DKCvsaGo.js → EmailListRenderer-BMaecFv9.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D_cfqama.js → FamilyGuardDashboard-DUaUukTN.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-B-UqQD68.js → Federation-CMBE0_rp.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-BWWvWuJW.js → FormItemContext-PG-JDbXq.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-CPchx6N7.js → GenericCardRenderer-Bcb0p88m.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Vx4co9Ff.js → Git-Bgv15myB.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-D4x0pp_m.js → Governance-DB8YTArI.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-T3KvwPYR.js → Inference-BQMwCWaS.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-Ddv3KUv3.js → KnowledgeGraph-B1_ZziiB.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-DfH-Dkae.js → Logs-DovEEnt_.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-DHW3PbCq.js → MarkdownRenderer-D201OL4T.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-tjgb0R3D.js → Marketplace-BlCXSpyB.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DHAk5nYX.js → McpTools-CsCR09Wj.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-A2Yx7thM.js → Memory-CBVRLKno.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-BEmTVfN5.js → MobileBridge-C9OIafgA.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-DuFHUgE4.js → MobileProjects-D_zoJ8Xs.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-BDSll0OZ.js → Mtc-vE-HdvLj.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-CuBvluaS.js → MtcAudit-CPLaekCd.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-BmXAA-Sb.js → Multisig-C_UP9C2o.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-ci2stZAe.js → NLProgramming-2nODJcU1.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-D-1RpqnS.js → Notes-BhE-SJGu.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-C0GyitIT.js → NotificationSettings-D21q7BNU.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-U2bN-hj5.js → OrderTableRenderer-0b62GYOD.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-BNZgpPci.js → Organization-CEqmw_sb.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-CDidKmUN.js → Overflow-MW-gMDO_.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BnreBaXq.js → P2P-CY0hjI_G.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-DwJUxO9j.js → PdhVaultBrowser-BbWh7Man.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-BQGgJPH0.js → Permissions-ChqEA_4Q.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-k_fSkCwL.js → PersonalDataHub-B3YrbYfD.js} +5 -5
  55. package/src/assets/web-panel/assets/{Pipeline-WGqKMqau.js → Pipeline-B8WBhhlW.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-DtHj9Ymq.js → Privacy-Cmb0PXNx.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-B3wH2SFC.js → ProjectInit-hF6zW3Nz.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-Cd0cgcA1.js → ProjectSettings-BFSVfpW0.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-DAWkVFkc.js +1 -0
  60. package/src/assets/web-panel/assets/{Providers-DTpxEXur.js → Providers-B8yux8Dx.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-B2v6gR-C.js → QrScannerModal-N76cQluO.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-D3Nr9xXR.js → QuickAsk-ClvZ0KYR.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-C9inoMky.js → Recommend-B-GfgBpT.js} +1 -1
  64. package/src/assets/web-panel/assets/RemoteSession-mlglCdQi.js +5 -0
  65. package/src/assets/web-panel/assets/{Reputation-41KFG646.js → Reputation-DJIZzQeG.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DqAaqqCv.js → Row-COzxu_bb.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-CMDpCDKc.js → RssFeed-CoYsJQuE.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-B6mtegNL.js → Search-DC77Mq9S.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-BaePFVZ8.js → Security-Loy2EdMO.js} +3 -3
  70. package/src/assets/web-panel/assets/{Services-DeG3Qscl.js → Services-GUD07Eu0.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-D_QP7F90.js → Skeleton-DWzkptYU.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-DLGyUgIA.js → Skills-CEDn1r-Z.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-BN2pxW1Q.js → Sla-ub_k4pXh.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-BWsZPd8B.js → SpeechSettings-D7HLX9CA.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-VOZRPODW.js → SyncSettings-DbwOvl6m.js} +2 -2
  76. package/src/assets/web-panel/assets/Tasks-CBQzoAu5.js +1 -0
  77. package/src/assets/web-panel/assets/{Templates-eJSob-q0.js → Templates-BNVeIhud.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-Bdwk8Rwy.js → Tenant-PS9AzhXU.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-CsMLKIoV.js → Terminal-Cgb4eLB_.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-D5qkbPKT.js → TimelineRenderer-CrmXMOkg.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-DKgwn8hI.js → Tokens-DD-VDHw1.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-HlEHniGM.js → Trigger-BsoN_n-n.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-CdPcsBpN.js → Trust-n8LmN4ZV.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-vAdXd-vG.js → UkeySign-C9_7MImA.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-9hHBYFnH.js → VideoEditing-DyBteNgn.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-Dxh5d1yR.js → Wallet-BS5ixeXI.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-BsFmLHkp.js → WebAuthn-spc4UImU.js} +3 -3
  88. package/src/assets/web-panel/assets/{WorkflowEditor-Cn0OIwLn.js → WorkflowEditor-D7iVvaBO.js} +1 -1
  89. package/src/assets/web-panel/assets/chat-6CmBkHrL.js +1 -0
  90. package/src/assets/web-panel/assets/{colors-Du9jzLN0.js → colors-BGoCLkxI.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-DhEscfzw.js → compact-item-fYZBetMO.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-CwzEfF3i.js → createContext-BeM-1UVj.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-BzPwHnw8.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DT_GSE6J.js → hasIn-CE7ZuEnV.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-53z1YUxX.js → index--lap6cf7.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-NA2oBLSB.js → index-ATcSr_2O.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DWL3SYN3.js → index-B2U-ObMq.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-D-QkW1fp.js → index-B3CDgvNW.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BF-ZCVGE.js → index-B4wiTEOM.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-D4kZoXNr.js → index-B8BsdNV_.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DnVVYKCE.js → index-BEVm0lCS.js} +10 -10
  102. package/src/assets/web-panel/assets/{index-DfRDyY1q.js → index-BNZDILcO.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-B8s5FxJY.js → index-BU_2qiDk.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BnHhFnwt.js → index-BV551878.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DhXay17G.js → index-BfPL-m46.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-B6QDWRLk.js → index-BowH1eLQ.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DN60VBn5.js → index-BsMfoaeo.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Cnok8mhZ.js → index-BunaQCa0.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Dny_ZnAO.js → index-C0v6YKKO.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-D8gt0j7r.js → index-C4SJVjs4.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Mobw3PHs.js → index-C9Y3k0HI.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CAVW0cWM.js → index-CIpVrf5P.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CyEhUJcu.js → index-CRLjEP1H.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DzjzrOli.js → index-CRnUE8Cb.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-D5IytTND.js → index-Cip8emOQ.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BQWENaZf.js → index-CpIGIB85.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CQWxDOvM.js → index-D1r9tT3J.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BrkJ7JPm.js → index-DT3zPSVX.js} +1 -1
  119. package/src/assets/web-panel/assets/index-DacrAdc-.js +1 -0
  120. package/src/assets/web-panel/assets/{index-BFD0cEna.js → index-De0quFDw.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-v5HLYCVa.js → index-DoXk5KJ8.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DtRwLhee.js → index-DsvfrESp.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BgSR2WMG.js → index-Dw1PyYPm.js} +1 -1
  124. package/src/assets/web-panel/assets/index-EHzNJozo.js +1 -0
  125. package/src/assets/web-panel/assets/{index-CGvgnfl9.js → index-MtOuq2i6.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-1LoutPJd.js → index-Puq6Luf1.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-tND8-TDU.js → index-QIpnpZgz.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-D3llLkYM.js → index-RMZ8a7Yt.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-C4SN3by5.js → index-WUrJ6S1j.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-VeHs6qH0.js → index-YjcumOZ4.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-Cc9aPkv-.js → index-lJBjB1QX.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BIYTjbv_.js → index-ukg99D1V.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-gFjYdstW.js → index-zb7rVOhV.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-Cvt59c10.js → initDefaultProps-CYHMs1ag.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-D15pPEzl.js → motion-DDB2Ffk5.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-N55KeXJ7.js → move-Bg3cD-dX.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-CyFO1bYP.js → mtc-parser-B5nedjTS.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-B5IXfxU2.js → omit-BHyuUILu.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-CpVqsulV.js → pickAttrs-C9YQnEN9.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-ME36sMF3.js → placementArrow-DBUFYUe7.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-BXAIR1r-.js → responsiveObserve-nBs2Obnk.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-BkOX3-Dp.js → slide-DkCEaXNU.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-Cy4Aklpt.js → statusUtils-aqE1Wi6U.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-gUd2T206.js → styleChecker-CN5bhfHB.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-CaVD4o99.js → useFlexGapSupport-B_oX_kvJ.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs--pFGl0KD.js → useFs-rCuXHeme.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-D45iP86i.js → usePersonalDataHub-DZR9Abyd.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BayoChH_.js → vnode-ConCYQlX.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-CpP38poi.js → zoom-CUdDxG5M.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/agent.js +4 -1
  152. package/src/commands/background-session.js +2 -1
  153. package/src/commands/context.js +69 -2
  154. package/src/data/changelog.json +13 -0
  155. package/src/gateways/ws/background-agent-protocol.js +38 -0
  156. package/src/gateways/ws/message-dispatcher.js +1 -0
  157. package/src/gateways/ws/remote-session-protocol.js +65 -6
  158. package/src/gateways/ws/ws-server.js +8 -1
  159. package/src/harness/feature-flags.js +15 -8
  160. package/src/harness/mcp-client.js +618 -43
  161. package/src/lazy-dispatch.js +30 -1
  162. package/src/lib/agent-authority.js +22 -0
  163. package/src/lib/agent-core.js +2 -0
  164. package/src/lib/agent-sandbox.js +161 -0
  165. package/src/lib/agent-team/team-runner.js +22 -0
  166. package/src/lib/artifact-store.js +35 -0
  167. package/src/lib/async-hook-supervisor.cjs +13 -2
  168. package/src/lib/audit-mtc.js +3 -2
  169. package/src/lib/background-agent-supervisor.js +104 -3
  170. package/src/lib/background-interaction-journal.js +407 -0
  171. package/src/lib/background-interaction-resolver.js +23 -34
  172. package/src/lib/background-session-transport.js +12 -2
  173. package/src/lib/capability-manifest.js +41 -3
  174. package/src/lib/capability-negotiation.js +1 -1
  175. package/src/lib/channels/channel-manager.js +33 -0
  176. package/src/lib/channels/telegram-channel.js +23 -5
  177. package/src/lib/channels/webhook-channel.js +38 -15
  178. package/src/lib/chrome-connector.js +169 -23
  179. package/src/lib/config-manager.js +23 -9
  180. package/src/lib/context-breakdown.js +31 -0
  181. package/src/lib/cowork-cron.js +274 -32
  182. package/src/lib/cross-chain-mtc.js +3 -2
  183. package/src/lib/durable-security-store.js +124 -0
  184. package/src/lib/event-runtime-host.js +67 -6
  185. package/src/lib/event-runtime-store.js +163 -0
  186. package/src/lib/goal-store.js +32 -12
  187. package/src/lib/headless-manifest.js +2 -1
  188. package/src/lib/hook-environment.cjs +58 -0
  189. package/src/lib/hook-manager.js +12 -5
  190. package/src/lib/hook-runner.cjs +122 -28
  191. package/src/lib/hook-runner.js +1 -0
  192. package/src/lib/hooks-v2-producers.js +36 -0
  193. package/src/lib/hooks-v2-runtime.js +329 -52
  194. package/src/lib/interaction-adapter.js +60 -19
  195. package/src/lib/interaction-binding.js +64 -0
  196. package/src/lib/json-schema-output.js +439 -14
  197. package/src/lib/json-schema-validate.js +514 -883
  198. package/src/lib/lan-pairing-tokens.js +115 -57
  199. package/src/lib/lsp/__tests__/lsp-client.test.js +32 -0
  200. package/src/lib/lsp/__tests__/lsp-manager.test.js +25 -0
  201. package/src/lib/lsp/lsp-client.js +4 -0
  202. package/src/lib/lsp/lsp-manager.js +3 -0
  203. package/src/lib/lsp/lsp-server-registry.js +18 -0
  204. package/src/lib/mcp-client.js +1 -0
  205. package/src/lib/mcp-oauth.js +35 -21
  206. package/src/lib/packer/pkg-config-generator.js +1 -0
  207. package/src/lib/permission-decision.js +99 -0
  208. package/src/lib/plugin-monitor-supervisor.js +3 -0
  209. package/src/lib/plugin-runtime/capabilities.js +50 -0
  210. package/src/lib/plugin-runtime/capability-consent.js +42 -35
  211. package/src/lib/plugin-runtime/hooks.js +36 -9
  212. package/src/lib/plugin-runtime/install.js +23 -7
  213. package/src/lib/plugin-runtime/lsp.js +4 -0
  214. package/src/lib/plugin-runtime/manifest.js +50 -2
  215. package/src/lib/plugin-runtime/mcp.js +30 -5
  216. package/src/lib/plugin-runtime/monitors.js +11 -0
  217. package/src/lib/plugin-runtime/plugin-options.js +94 -38
  218. package/src/lib/plugin-runtime/policy.js +29 -2
  219. package/src/lib/plugin-runtime/sandbox-policy.js +84 -0
  220. package/src/lib/plugin-runtime/scopes.js +7 -3
  221. package/src/lib/plugin-runtime/trust.js +36 -31
  222. package/src/lib/process-execution-broker/credential-agent.js +114 -16
  223. package/src/lib/process-execution-broker/credential-transport-worker.js +312 -0
  224. package/src/lib/process-execution-broker/credential-transport.js +378 -0
  225. package/src/lib/process-execution-broker/index.js +408 -19
  226. package/src/lib/process-execution-broker/platform-sandbox.js +561 -21
  227. package/src/lib/process-execution-broker/windows-sandbox.ps1 +999 -0
  228. package/src/lib/project-mcp-trust.js +21 -26
  229. package/src/lib/protocol-replay.js +19 -12
  230. package/src/lib/settings-hooks.cjs +13 -0
  231. package/src/lib/skill-loader.js +83 -10
  232. package/src/lib/sync-credentials.js +5 -4
  233. package/src/lib/turn-binding.js +8 -6
  234. package/src/repl/agent-repl.js +113 -28
  235. package/src/repl/repl-turn-binding.js +5 -2
  236. package/src/runtime/agent-core.js +331 -26
  237. package/src/runtime/coding-agent-contract-shared.cjs +3 -3
  238. package/src/runtime/headless-runner.js +101 -4
  239. package/src/runtime/headless-stream.js +209 -23
  240. package/src/runtime/mcp-config.js +29 -17
  241. package/src/runtime/policies/agent-policy.js +2 -0
  242. package/src/workers/background-agent-worker.js +128 -13
  243. package/src/assets/web-panel/assets/Projects-BbgmyQnS.js +0 -1
  244. package/src/assets/web-panel/assets/RemoteSession-Bqk3EYSu.js +0 -4
  245. package/src/assets/web-panel/assets/Tasks-3bDAUz8K.js +0 -1
  246. package/src/assets/web-panel/assets/chat-CJ19O6Dh.js +0 -1
  247. package/src/assets/web-panel/assets/devWarning-BBMFnsja.js +0 -1
  248. package/src/assets/web-panel/assets/index-Do8JMV4N.js +0 -1
  249. package/src/assets/web-panel/assets/index-UYAJ6iNM.js +0 -1
@@ -239,6 +239,56 @@ export function componentCapabilityDenial(manifest, capabilityNames) {
239
239
  return finding ? { reason: finding.reason } : null;
240
240
  }
241
241
 
242
+ /**
243
+ * Enforce a declared network domain for a direct URL-based plugin component.
244
+ * Stdio children have no direct target here and remain subject to the process
245
+ * sandbox boundary; URL transports can be rejected before any connection.
246
+ */
247
+ export function networkTargetDenial(manifest, targetUrl) {
248
+ if (!manifest || manifest.capabilitiesDeclared !== true || !targetUrl) {
249
+ return null;
250
+ }
251
+ let hostname;
252
+ try {
253
+ hostname = new URL(String(targetUrl)).hostname
254
+ .toLowerCase()
255
+ .replace(/\.$/, "");
256
+ } catch {
257
+ return { reason: `invalid network target URL: ${targetUrl}` };
258
+ }
259
+ const network = manifest.capabilities?.network || {
260
+ any: false,
261
+ domains: [],
262
+ };
263
+ if (network.any === true) return null;
264
+ const allowed = (network.domains || []).some((raw) => {
265
+ let domain = String(raw || "")
266
+ .trim()
267
+ .toLowerCase()
268
+ .replace(/\.$/, "");
269
+ if (!domain) return false;
270
+ if (domain.includes("://")) {
271
+ try {
272
+ domain = new URL(domain).hostname.toLowerCase().replace(/\.$/, "");
273
+ } catch {
274
+ return false;
275
+ }
276
+ } else {
277
+ domain = domain.replace(/:\d+$/, "");
278
+ }
279
+ if (domain.startsWith("*.")) {
280
+ const suffix = domain.slice(1);
281
+ return hostname.endsWith(suffix) && hostname.length > suffix.length;
282
+ }
283
+ return hostname === domain;
284
+ });
285
+ return allowed
286
+ ? null
287
+ : {
288
+ reason: `network target "${hostname}" is outside the plugin's declared domains`,
289
+ };
290
+ }
291
+
242
292
  // ─── options schema ─────────────────────────────────────────────────────────
243
293
 
244
294
  export const OPTION_TYPES = Object.freeze([
@@ -18,21 +18,22 @@
18
18
  * never touch the real user-data dir.
19
19
  */
20
20
 
21
- import fs from "fs";
22
21
  import path from "path";
23
22
  import { getElectronUserDataDir } from "../paths.js";
23
+ import { withFileLock } from "../with-file-lock.js";
24
24
  import {
25
25
  normalizeCapabilities,
26
26
  diffCapabilities,
27
27
  describeCapabilities,
28
28
  } from "./capabilities.js";
29
+ import {
30
+ mutateSecurityStore,
31
+ readSecurityStore,
32
+ } from "../durable-security-store.js";
29
33
 
30
34
  export const _deps = {
31
- existsSync: fs.existsSync,
32
- readFileSync: fs.readFileSync,
33
- writeFileSync: fs.writeFileSync,
34
- mkdirSync: fs.mkdirSync,
35
35
  now: () => new Date().toISOString(),
36
+ withFileLock,
36
37
  // Injectable so unit tests never touch the real user-data dir.
37
38
  storePath: () =>
38
39
  path.join(getElectronUserDataDir(), "plugin-capability-consent.json"),
@@ -43,20 +44,7 @@ function consentKey(scope, name) {
43
44
  }
44
45
 
45
46
  export function loadConsentStore() {
46
- try {
47
- const p = _deps.storePath();
48
- if (!_deps.existsSync(p)) return {};
49
- const data = JSON.parse(_deps.readFileSync(p, "utf8"));
50
- return data && typeof data === "object" && !Array.isArray(data) ? data : {};
51
- } catch {
52
- return {};
53
- }
54
- }
55
-
56
- function saveConsentStore(store) {
57
- const p = _deps.storePath();
58
- _deps.mkdirSync(path.dirname(p), { recursive: true });
59
- _deps.writeFileSync(p, JSON.stringify(store, null, 2), "utf8");
47
+ return readSecurityStore(_deps.storePath(), "plugin capability consent");
60
48
  }
61
49
 
62
50
  /**
@@ -158,8 +146,15 @@ export function resolveConsentAction(
158
146
  /** Load the store and report whether one plugin's declared caps are consented. */
159
147
  export function isPluginCapabilityConsented(plugin, declaredCaps) {
160
148
  if (!plugin || !plugin.name) return false;
161
- const entry = loadConsentStore()[consentKey(plugin.scope, plugin.name)];
162
- return capabilityConsentStatus(declaredCaps, entry).consented;
149
+ if (capabilitiesAreEmpty(declaredCaps)) return true;
150
+ try {
151
+ const entry = loadConsentStore()[consentKey(plugin.scope, plugin.name)];
152
+ return capabilityConsentStatus(declaredCaps, entry).consented;
153
+ } catch {
154
+ // Security reads fail closed: an unavailable/corrupt consent store never
155
+ // turns into permission to use a capability.
156
+ return false;
157
+ }
163
158
  }
164
159
 
165
160
  /** Record consent for a plugin's currently-declared capability set. */
@@ -168,24 +163,36 @@ export function consentPluginCapabilities(
168
163
  { scope = "project", version, capabilities } = {},
169
164
  ) {
170
165
  if (!version) throw new Error("consentPluginCapabilities requires a version");
171
- const store = loadConsentStore();
172
- store[consentKey(scope, name)] = {
173
- version,
174
- capabilities: canonicalCaps(capabilities),
175
- consentedAt: _deps.now(),
176
- };
177
- saveConsentStore(store);
178
- return { name, scope, version };
166
+ const file = _deps.storePath();
167
+ return mutateSecurityStore(
168
+ file,
169
+ "plugin capability consent",
170
+ (store) => {
171
+ store[consentKey(scope, name)] = {
172
+ version,
173
+ capabilities: canonicalCaps(capabilities),
174
+ consentedAt: _deps.now(),
175
+ };
176
+ return { name, scope, version };
177
+ },
178
+ { lock: _deps.withFileLock },
179
+ );
179
180
  }
180
181
 
181
182
  /** Revoke capability consent for a plugin at a scope. */
182
183
  export function revokeCapabilityConsent(name, { scope = "project" } = {}) {
183
- const store = loadConsentStore();
184
- const key = consentKey(scope, name);
185
- const existed = Object.prototype.hasOwnProperty.call(store, key);
186
- delete store[key];
187
- saveConsentStore(store);
188
- return { name, scope, removed: existed };
184
+ const file = _deps.storePath();
185
+ return mutateSecurityStore(
186
+ file,
187
+ "plugin capability consent",
188
+ (store) => {
189
+ const key = consentKey(scope, name);
190
+ const existed = Object.prototype.hasOwnProperty.call(store, key);
191
+ delete store[key];
192
+ return { name, scope, removed: existed };
193
+ },
194
+ { lock: _deps.withFileLock },
195
+ );
189
196
  }
190
197
 
191
198
  /** All consent entries (for `cc plugin consent --list`). */
@@ -18,6 +18,7 @@ import fs from "fs";
18
18
  import { discoverPlugins } from "./scopes.js";
19
19
  import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
20
20
  import { componentCapabilityDenial } from "./capabilities.js";
21
+ import { mergePluginSandboxPolicies } from "./sandbox-policy.js";
21
22
 
22
23
  export const _deps = { readFileSync: fs.readFileSync };
23
24
 
@@ -124,17 +125,43 @@ export function collectPluginHooks(opts = {}) {
124
125
  if (!Array.isArray(entries)) continue;
125
126
  const tagged = entries.map((group) => {
126
127
  if (!group || typeof group !== "object" || !Array.isArray(group.hooks)) return group;
128
+ let groupSandboxPolicy;
129
+ try {
130
+ groupSandboxPolicy = mergePluginSandboxPolicies(
131
+ p.manifest.sandboxPolicy,
132
+ group.sandboxPolicy,
133
+ );
134
+ } catch {
135
+ return null;
136
+ }
137
+ const { sandboxPolicy: _rawGroupPolicy, ...groupConfig } = group;
127
138
  return {
128
- ...group,
129
- hooks: group.hooks.map((hook) => ({
130
- ...hook,
131
- origin: "plugin:hook",
132
- pluginId: p.name,
133
- pluginVersion: p.version || null,
134
- pluginSource: p.manifest?.manifestPath || null,
135
- })),
139
+ ...groupConfig,
140
+ hooks: group.hooks
141
+ .map((hook) => {
142
+ if (!hook || typeof hook !== "object") return hook;
143
+ let sandboxPolicy;
144
+ try {
145
+ sandboxPolicy = mergePluginSandboxPolicies(
146
+ groupSandboxPolicy,
147
+ hook.sandboxPolicy,
148
+ );
149
+ } catch {
150
+ return null;
151
+ }
152
+ const { sandboxPolicy: _rawHookPolicy, ...hookConfig } = hook;
153
+ return {
154
+ ...hookConfig,
155
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
156
+ origin: "plugin:hook",
157
+ pluginId: p.name,
158
+ pluginVersion: p.version || null,
159
+ pluginSource: p.manifest?.manifestPath || null,
160
+ };
161
+ })
162
+ .filter(Boolean),
136
163
  };
137
- });
164
+ }).filter(Boolean);
138
165
  merged[event] = (merged[event] || []).concat(tagged);
139
166
  }
140
167
  }
@@ -35,6 +35,8 @@ import {
35
35
  } from "./signature.js";
36
36
  import executionBroker from "../process-execution-broker/index.js";
37
37
 
38
+ export const MAX_LISTED_PLUGIN_VERSIONS = 64;
39
+
38
40
  export const _deps = {
39
41
  existsSync: fs.existsSync,
40
42
  mkdirSync: fs.mkdirSync,
@@ -321,13 +323,27 @@ export function listInstalled(opts = {}) {
321
323
  cwd: opts.cwd,
322
324
  scopes: opts.scopes,
323
325
  skipPolicy: true,
324
- }).map((p) => ({
325
- name: p.name,
326
- version: p.version,
327
- scope: p.scope,
328
- dir: p.root,
329
- ok: p.manifest?.ok === true,
330
- }));
326
+ }).map((p) => {
327
+ const allVersions = listInstalledVersions(p.scope, p.name, {
328
+ cwd: opts.cwd,
329
+ });
330
+ const versions = allVersions.slice(0, MAX_LISTED_PLUGIN_VERSIONS);
331
+ if (
332
+ p.version &&
333
+ !versions.includes(p.version) &&
334
+ allVersions.includes(p.version)
335
+ ) {
336
+ versions[versions.length - 1] = p.version;
337
+ }
338
+ return {
339
+ name: p.name,
340
+ version: p.version,
341
+ versions,
342
+ scope: p.scope,
343
+ dir: p.root,
344
+ ok: p.manifest?.ok === true,
345
+ };
346
+ });
331
347
  }
332
348
 
333
349
  /**
@@ -73,6 +73,7 @@ export function ensurePluginLspServers(opts = {}) {
73
73
 
74
74
  const denied = [];
75
75
  for (const p of trusted) {
76
+ if (!p.manifest || p.manifest.ok !== true) continue;
76
77
  const servers = p.manifest?.components?.lsp || [];
77
78
  if (servers.length === 0) continue;
78
79
  // Component-level capability gate: a plugin that declared a permissions
@@ -94,6 +95,9 @@ export function ensurePluginLspServers(opts = {}) {
94
95
  pluginId: p.name,
95
96
  pluginVersion: p.version,
96
97
  pluginSource: s.absPath || p.manifest.manifestPath,
98
+ ...(s.sandboxPolicy
99
+ ? { sandboxPolicy: s.sandboxPolicy }
100
+ : {}),
97
101
  });
98
102
  registered.push({ plugin: p.name, languageId: s.languageId, id: s.id });
99
103
  } catch {
@@ -32,6 +32,10 @@ import {
32
32
  normalizeOptionsSchema,
33
33
  auditDeclaredCapabilities,
34
34
  } from "./capabilities.js";
35
+ import {
36
+ mergePluginSandboxPolicies,
37
+ normalizePluginSandboxPolicy,
38
+ } from "./sandbox-policy.js";
35
39
 
36
40
  export const _deps = {
37
41
  existsSync: fs.existsSync,
@@ -137,6 +141,19 @@ export function parsePluginManifest(root) {
137
141
  license: strOr(manifest.license, ""),
138
142
  };
139
143
 
144
+ let manifestSandboxPolicy = null;
145
+ try {
146
+ manifestSandboxPolicy = normalizePluginSandboxPolicy(
147
+ manifest.sandboxPolicy,
148
+ { label: "manifest.sandboxPolicy" },
149
+ );
150
+ if (manifestSandboxPolicy) {
151
+ result.sandboxPolicy = manifestSandboxPolicy;
152
+ }
153
+ } catch (err) {
154
+ errors.push(err.message);
155
+ }
156
+
140
157
  // A helper that resolves a plugin-relative path safely, recording an error if
141
158
  // it escapes the root or is absolute. Returns the absolute path or null.
142
159
  const safeResolve = (rel, label) => {
@@ -174,9 +191,22 @@ export function parsePluginManifest(root) {
174
191
  conventionPath: ["monitors", "monitors.json"],
175
192
  listKey: "monitors",
176
193
  });
177
- c.lsp = resolveLsp(abs, manifest, safeResolve, errors, warnings);
194
+ c.lsp = resolveLsp(
195
+ abs,
196
+ manifest,
197
+ safeResolve,
198
+ errors,
199
+ warnings,
200
+ manifestSandboxPolicy,
201
+ );
178
202
  c.bin = resolveBin(abs, manifest, safeResolve, warnings);
179
203
  c.settings = resolveSettings(abs, manifest, safeResolve);
204
+ if (manifestSandboxPolicy && c.bin.length > 0) {
205
+ errors.push(
206
+ "manifest.sandboxPolicy cannot currently protect plugin bin executables; " +
207
+ "top-level sandbox requirements with bin are not supported",
208
+ );
209
+ }
180
210
 
181
211
  // ── declared capabilities + options schema (P1) ──
182
212
  const hasPermissions =
@@ -363,7 +393,14 @@ function extractList(obj, listKey) {
363
393
  return { count: 0, names: [] };
364
394
  }
365
395
 
366
- function resolveLsp(root, manifest, safeResolve, errors, warnings) {
396
+ function resolveLsp(
397
+ root,
398
+ manifest,
399
+ safeResolve,
400
+ errors,
401
+ warnings,
402
+ manifestSandboxPolicy,
403
+ ) {
367
404
  let content = manifest.lsp;
368
405
  // Convention file `.lsp.json`.
369
406
  if (!content) {
@@ -392,12 +429,23 @@ function resolveLsp(root, manifest, safeResolve, errors, warnings) {
392
429
  warnings.push("lsp server entry missing languageId/command — skipped");
393
430
  continue;
394
431
  }
432
+ let sandboxPolicy;
433
+ try {
434
+ sandboxPolicy = mergePluginSandboxPolicies(
435
+ manifestSandboxPolicy,
436
+ s.sandboxPolicy,
437
+ );
438
+ } catch (err) {
439
+ errors.push(`lsp server "${s.languageId}": ${err.message}`);
440
+ continue;
441
+ }
395
442
  out.push({
396
443
  languageId: s.languageId,
397
444
  command: s.command,
398
445
  args: Array.isArray(s.args) ? s.args.map(String) : [],
399
446
  extensions: Array.isArray(s.extensions) ? s.extensions.map(String) : [],
400
447
  id: typeof s.id === "string" ? s.id : s.command,
448
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
401
449
  });
402
450
  }
403
451
  return out;
@@ -17,7 +17,11 @@
17
17
  import fs from "fs";
18
18
  import { discoverPlugins } from "./scopes.js";
19
19
  import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
20
- import { componentCapabilityDenial } from "./capabilities.js";
20
+ import {
21
+ componentCapabilityDenial,
22
+ networkTargetDenial,
23
+ } from "./capabilities.js";
24
+ import { mergePluginSandboxPolicies } from "./sandbox-policy.js";
21
25
 
22
26
  export const _deps = { readFileSync: fs.readFileSync };
23
27
 
@@ -35,9 +39,8 @@ function warnMcpCapabilityDeniedOnce(entries) {
35
39
  const list = entries.map((e) => `${e.name} (${e.reason})`).join("; ");
36
40
  try {
37
41
  process.stderr.write(
38
- `[plugins] refused MCP server(s) from plugin(s) that declared a permissions ` +
39
- `block but did not declare the 'mcp' capability their server needs: ${list}\n` +
40
- ` add 'mcp' to the plugin's permissions block to enable them.\n`,
42
+ `[plugins] refused MCP server(s) outside declared plugin capabilities: ${list}\n` +
43
+ ` declare 'mcp' and every direct URL domain in the plugin permissions block.\n`,
41
44
  );
42
45
  } catch {
43
46
  /* stderr notice is best-effort */
@@ -119,8 +122,30 @@ export function collectPluginMcpServers(opts = {}) {
119
122
  for (const [name, cfg] of Object.entries(map)) {
120
123
  if (!cfg || typeof cfg !== "object") continue;
121
124
  if (name in servers) continue; // first plugin wins on a clash
125
+ let sandboxPolicy;
126
+ try {
127
+ sandboxPolicy = mergePluginSandboxPolicies(
128
+ p.manifest.sandboxPolicy,
129
+ cfg.sandboxPolicy,
130
+ );
131
+ } catch {
132
+ // An invalid explicit requirement must never degrade to an unsandboxed
133
+ // process. Refuse only this server so independent entries still load.
134
+ continue;
135
+ }
136
+ const targetUrl = cfg.url || cfg.transport?.url || null;
137
+ const networkDenial = networkTargetDenial(p.manifest, targetUrl);
138
+ if (networkDenial) {
139
+ denied.push({
140
+ name: `${p.name}/${name}`,
141
+ reason: networkDenial.reason,
142
+ });
143
+ continue;
144
+ }
145
+ const { sandboxPolicy: _rawSandboxPolicy, ...serverConfig } = cfg;
122
146
  servers[name] = {
123
- ...cfg,
147
+ ...serverConfig,
148
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
124
149
  origin: "plugin:mcp",
125
150
  policy: "allow",
126
151
  pluginId: p.name,
@@ -22,6 +22,7 @@ import fs from "fs";
22
22
  import { discoverPlugins } from "./scopes.js";
23
23
  import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
24
24
  import { componentCapabilityDenial } from "./capabilities.js";
25
+ import { mergePluginSandboxPolicies } from "./sandbox-policy.js";
25
26
 
26
27
  export const _deps = { readFileSync: fs.readFileSync };
27
28
 
@@ -68,6 +69,15 @@ function normalizeMonitor(plugin, raw) {
68
69
  raw.env && typeof raw.env === "object" && !Array.isArray(raw.env)
69
70
  ? raw.env
70
71
  : null;
72
+ let sandboxPolicy;
73
+ try {
74
+ sandboxPolicy = mergePluginSandboxPolicies(
75
+ plugin.manifest?.sandboxPolicy,
76
+ raw.sandboxPolicy,
77
+ );
78
+ } catch {
79
+ return null;
80
+ }
71
81
  return {
72
82
  plugin: plugin.name,
73
83
  scope: plugin.scope,
@@ -85,6 +95,7 @@ function normalizeMonitor(plugin, raw) {
85
95
  pluginId: plugin.name,
86
96
  pluginVersion: plugin.version,
87
97
  pluginSource: plugin.manifest?.manifestPath || null,
98
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
88
99
  };
89
100
  }
90
101
 
@@ -20,6 +20,7 @@
20
20
 
21
21
  import fs from "fs";
22
22
  import path from "path";
23
+ import crypto from "node:crypto";
23
24
  import { getElectronUserDataDir } from "../paths.js";
24
25
  import {
25
26
  optionDefaults,
@@ -31,12 +32,16 @@ import {
31
32
  isSecretRef,
32
33
  secretRef,
33
34
  } from "../secret-store.js";
35
+ import { withFileLock } from "../with-file-lock.js";
34
36
 
35
37
  export const _deps = {
36
38
  existsSync: fs.existsSync,
37
39
  readFileSync: fs.readFileSync,
40
+ renameSync: fs.renameSync,
41
+ unlinkSync: fs.unlinkSync,
38
42
  writeFileSync: fs.writeFileSync,
39
43
  mkdirSync: fs.mkdirSync,
44
+ withFileLock,
40
45
  secretStore: () => createSecretStore(),
41
46
  userStorePath: () =>
42
47
  path.join(getElectronUserDataDir(), "plugin-options.json"),
@@ -44,23 +49,54 @@ export const _deps = {
44
49
  path.join(cwd || process.cwd(), ".chainlesschain", "plugin-options.json"),
45
50
  };
46
51
 
47
- function readJsonObject(p) {
52
+ function readJsonObject(p, { failIfUnavailable = false } = {}) {
48
53
  try {
49
54
  if (!_deps.existsSync(p)) return {};
50
55
  const data = JSON.parse(_deps.readFileSync(p, "utf8"));
51
- return data && typeof data === "object" && !Array.isArray(data) ? data : {};
52
- } catch {
56
+ if (data && typeof data === "object" && !Array.isArray(data)) return data;
57
+ throw new TypeError("top-level value must be an object");
58
+ } catch (cause) {
59
+ if (failIfUnavailable) {
60
+ const error = new Error(`Plugin option store is unavailable: ${p}`, {
61
+ cause,
62
+ });
63
+ error.code = "PLUGIN_OPTION_STORE_UNAVAILABLE";
64
+ throw error;
65
+ }
53
66
  return {};
54
67
  }
55
68
  }
56
69
 
57
70
  function writeJsonObject(p, obj) {
58
71
  _deps.mkdirSync(path.dirname(p), { recursive: true });
59
- _deps.writeFileSync(p, JSON.stringify(obj, null, 2), "utf8");
72
+ const tmp = `${p}.${process.pid}.${crypto.randomUUID()}.tmp`;
73
+ try {
74
+ _deps.writeFileSync(tmp, `${JSON.stringify(obj, null, 2)}\n`, {
75
+ encoding: "utf8",
76
+ mode: 0o600,
77
+ });
78
+ _deps.renameSync(tmp, p);
79
+ } catch (error) {
80
+ try {
81
+ _deps.unlinkSync(tmp);
82
+ } catch {
83
+ // Preserve the original persistence error.
84
+ }
85
+ throw error;
86
+ }
87
+ }
88
+
89
+ function withOptionStoreLock(p, body) {
90
+ _deps.mkdirSync(path.dirname(p), { recursive: true, mode: 0o700 });
91
+ return _deps.withFileLock(p, body, { failIfUnavailable: true });
60
92
  }
61
93
 
62
94
  /** Load a plugin's option VALUES at the given scope (`user` | `project`). */
63
- export function loadPluginOptionValues(name, scope, { cwd, schema, secretStore } = {}) {
95
+ export function loadPluginOptionValues(
96
+ name,
97
+ scope,
98
+ { cwd, schema, secretStore } = {},
99
+ ) {
64
100
  const p =
65
101
  scope === "project" ? _deps.projectStorePath(cwd) : _deps.userStorePath();
66
102
  const store = readJsonObject(p);
@@ -71,7 +107,9 @@ export function loadPluginOptionValues(name, scope, { cwd, schema, secretStore }
71
107
  for (const [key, desc] of Object.entries(normalized)) {
72
108
  if (!desc?.sensitive || !isSecretRef(out[key])) continue;
73
109
  try {
74
- const value = (secretStore || _deps.secretStore)().get(out[key].__cc_secret_ref);
110
+ const value = (secretStore || _deps.secretStore)().get(
111
+ out[key].__cc_secret_ref,
112
+ );
75
113
  if (value != null) out[key] = value;
76
114
  else delete out[key];
77
115
  } catch {
@@ -92,40 +130,58 @@ export function setPluginOptionValues(
92
130
  ) {
93
131
  const p =
94
132
  scope === "project" ? _deps.projectStorePath(cwd) : _deps.userStorePath();
95
- const store = readJsonObject(p);
96
- const input = values && typeof values === "object" ? values : {};
97
- const persisted = { ...input };
98
- const normalized = schema || {};
99
- let secrets = null;
100
- const getSecrets = () => {
101
- if (!secrets) secrets = (secretStore || _deps.secretStore)();
102
- return secrets;
103
- };
104
- const rejectedSensitive = [];
105
- for (const [key, desc] of Object.entries(normalized)) {
106
- if (!desc?.sensitive || !Object.prototype.hasOwnProperty.call(input, key)) continue;
107
- // Project files are shareable and must never receive a sensitive value.
108
- if (scope === "project") {
109
- delete persisted[key];
110
- rejectedSensitive.push(key);
111
- continue;
133
+ return withOptionStoreLock(p, () => {
134
+ const store = readJsonObject(p, { failIfUnavailable: true });
135
+ const input = values && typeof values === "object" ? values : {};
136
+ const persisted = { ...input };
137
+ const normalized = schema || {};
138
+ let secrets = null;
139
+ const getSecrets = () => {
140
+ if (!secrets) secrets = (secretStore || _deps.secretStore)();
141
+ return secrets;
142
+ };
143
+ const rejectedSensitive = [];
144
+ for (const [key, desc] of Object.entries(normalized)) {
145
+ if (
146
+ !desc?.sensitive ||
147
+ !Object.prototype.hasOwnProperty.call(input, key)
148
+ ) {
149
+ continue;
150
+ }
151
+ // Project files are shareable and must never receive a sensitive value.
152
+ if (scope === "project") {
153
+ delete persisted[key];
154
+ rejectedSensitive.push(key);
155
+ continue;
156
+ }
157
+ if (input[key] == null || input[key] === "") {
158
+ delete persisted[key];
159
+ try {
160
+ getSecrets().delete(`${name}/${key}`);
161
+ } catch {
162
+ // Missing secrets already behave as absent on read.
163
+ }
164
+ continue;
165
+ }
166
+ getSecrets().set(`${name}/${key}`, input[key]);
167
+ persisted[key] = secretRef(`${name}/${key}`);
112
168
  }
113
- if (input[key] == null || input[key] === "") {
114
- delete persisted[key];
115
- try { getSecrets().delete(`${name}/${key}`); } catch {}
116
- continue;
169
+ if (rejectedSensitive.length > 0) {
170
+ persisted.__cc_rejected_sensitive = [
171
+ ...new Set(rejectedSensitive),
172
+ ].sort();
173
+ } else if (
174
+ Object.prototype.hasOwnProperty.call(
175
+ persisted,
176
+ "__cc_rejected_sensitive",
177
+ )
178
+ ) {
179
+ delete persisted.__cc_rejected_sensitive;
117
180
  }
118
- getSecrets().set(`${name}/${key}`, input[key]);
119
- persisted[key] = secretRef(`${name}/${key}`);
120
- }
121
- if (rejectedSensitive.length > 0) {
122
- persisted.__cc_rejected_sensitive = [...new Set(rejectedSensitive)].sort();
123
- } else if (Object.prototype.hasOwnProperty.call(persisted, "__cc_rejected_sensitive")) {
124
- delete persisted.__cc_rejected_sensitive;
125
- }
126
- store[name] = persisted;
127
- writeJsonObject(p, store);
128
- return { name, scope, path: p };
181
+ store[name] = persisted;
182
+ writeJsonObject(p, store);
183
+ return { name, scope, path: p };
184
+ });
129
185
  }
130
186
 
131
187
  /** Provided keys of `values` (that exist in schema) accepted at `scope`. */