chainlesschain 0.162.181 → 0.162.183

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 (195) hide show
  1. package/package.json +2 -1
  2. package/scripts/build-windows-sandbox-helper.ps1 +163 -0
  3. package/src/assets/web-panel/assets/{AIOps-cq0dwbnf.js → AIOps-BapS7lz4.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CvcgGl3w.js → ActionButton-CXYdXEUI.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-hM2-jFqg.js → Analytics-CKR5Lexn.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-B9_Gsd_g.js → AppLayout-DATVUOXB.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-B7bu52Og.js → Artifacts-vb_0ZlV1.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-DThfDgqB.js → Audit-BI1l9GWt.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-DjlFiD4v.js → BackgroundAgents-BFXbaUEf.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-CK9xJKnG.js → Backup-DEYBJJ6H.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-DEiSU8K8.js → BaseInput-COdzeO0M.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-CpT96czj.js → Chat-oSN6sfM5.js} +6 -6
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-CI_jT0ru.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-C1GfDnO0.js → Checkbox-BezEa4tm.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-3DAKgiLx.js → Codegen-CNEL0Ife.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-74cOwZU1.js → Col-Bf2Y2Bn1.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-tn9Kkz9j.js → Community-BfUlu4Hc.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-BcoXH7m4.js → Compact-BN4xWP9e.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-DIPGUBgi.js → Compliance-Bt6BKmEP.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-doD5_91c.js → Cowork-DKLp8YxO.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-C-IDvubz.js → Cron-o7Hhw6ky.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-CdcCoBdj.js → Crosschain-DaNXWJIw.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-DdH5aMMA.js → DID-B-Qa2mha.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-DHLA4mm0.js → Dashboard-CMX0Qnxw.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-dEeCt7Md.js → Dropdown-BLuLyRu_.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-BMaecFv9.js → EmailListRenderer-CFBAeMij.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DUaUukTN.js → FamilyGuardDashboard-DWzXQ67V.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-CMBE0_rp.js → Federation-DB1QSLgr.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-PG-JDbXq.js → FormItemContext-C-OKCToP.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-Bcb0p88m.js → GenericCardRenderer-DMJFtFZf.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Bgv15myB.js → Git-DsGSujOV.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-DB8YTArI.js → Governance-B0iS9NEW.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-BQMwCWaS.js → Inference-CH004H7c.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-B1_ZziiB.js → KnowledgeGraph-Q9MTQ83c.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-DovEEnt_.js → Logs-B_fyjZK9.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-D201OL4T.js → MarkdownRenderer-C0o4FtpD.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-BlCXSpyB.js → Marketplace-BIStF9d1.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-CsCR09Wj.js → McpTools-pGfal768.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-CBVRLKno.js → Memory-xbNqpIqB.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-C9OIafgA.js → MobileBridge-B5EQGrIK.js} +1 -1
  41. package/src/assets/web-panel/assets/MobileProjects-VTc3gT1y.js +1 -0
  42. package/src/assets/web-panel/assets/{Mtc-vE-HdvLj.js → Mtc-a_rmfxJH.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-CPLaekCd.js → MtcAudit-BmKuofUr.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-C_UP9C2o.js → Multisig-BQbJ-CAN.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-2nODJcU1.js → NLProgramming-JtPJKX25.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-BhE-SJGu.js → Notes-ZFKznTSG.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-D21q7BNU.js → NotificationSettings-B6aHRBBR.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-0b62GYOD.js → OrderTableRenderer-DdcsOqEz.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-CEqmw_sb.js → Organization-BFDTAUgS.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-MW-gMDO_.js → Overflow-0FoTfbWH.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-CY0hjI_G.js → P2P-CWd90etw.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-BbWh7Man.js → PdhVaultBrowser-DtCGC-W-.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-ChqEA_4Q.js → Permissions-CvQ7nr7T.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-B3YrbYfD.js → PersonalDataHub-rdNPcR-Z.js} +5 -5
  55. package/src/assets/web-panel/assets/{Pipeline-B8WBhhlW.js → Pipeline-CmrfiNOs.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-Cmb0PXNx.js → Privacy-D7ep1h_t.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-hF6zW3Nz.js → ProjectInit-C0xKINHS.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-BFSVfpW0.js → ProjectSettings-AOmZFp-F.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-BWHSkXZP.js +1 -0
  60. package/src/assets/web-panel/assets/{Providers-B8yux8Dx.js → Providers-Cb7JCTPL.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-N76cQluO.js → QrScannerModal-BDMehcXC.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-ClvZ0KYR.js → QuickAsk-BF6_1nvx.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-B-GfgBpT.js → Recommend-BHfT-A28.js} +1 -1
  64. package/src/assets/web-panel/assets/RemoteSession-B6EEf8hh.js +5 -0
  65. package/src/assets/web-panel/assets/{Reputation-DJIZzQeG.js → Reputation-CsWHLEcj.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-COzxu_bb.js → Row-D6srnWhm.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-CoYsJQuE.js → RssFeed-Xhr-KyVd.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-DC77Mq9S.js → Search-vIbIJvwL.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-Loy2EdMO.js → Security-Bzi5Aiko.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-GUD07Eu0.js → Services-BHtVX52P.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-DWzkptYU.js → Skeleton-C0R8B2jf.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-CEDn1r-Z.js → Skills-DtT6rWOX.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-ub_k4pXh.js → Sla-0B37mxVU.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-D7HLX9CA.js → SpeechSettings-0V2jE1Bs.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-DbwOvl6m.js → SyncSettings-CYedRzaj.js} +2 -2
  76. package/src/assets/web-panel/assets/Tasks-D-R1x8vm.js +1 -0
  77. package/src/assets/web-panel/assets/{Templates-BNVeIhud.js → Templates-BKoHztGT.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-PS9AzhXU.js → Tenant--17nwVNZ.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-Cgb4eLB_.js → Terminal-Dnkos7vW.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-CrmXMOkg.js → TimelineRenderer-C5jx4AE3.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-DD-VDHw1.js → Tokens-DYJKYa9t.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-BsoN_n-n.js → Trigger-DChpWiQ9.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-n8LmN4ZV.js → Trust-DfWG52EA.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-C9_7MImA.js → UkeySign-CMugYcyA.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-DyBteNgn.js → VideoEditing-BOSmrHAd.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-BS5ixeXI.js → Wallet-D5g3k3Sa.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-spc4UImU.js → WebAuthn-D-7u4Mkc.js} +3 -3
  88. package/src/assets/web-panel/assets/{WorkflowEditor-D7iVvaBO.js → WorkflowEditor-CFzZMSUJ.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-6CmBkHrL.js → chat-VbR6l6mc.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-BGoCLkxI.js → colors-BSZn8EDS.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-fYZBetMO.js → compact-item-y74obMEq.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-BeM-1UVj.js → createContext-C326bjex.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-D3Bho_6u.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-CE7ZuEnV.js → hasIn-D_INJQT_.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-ukg99D1V.js → index-4PIeTM--.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-B2U-ObMq.js → index-4eyH-eC2.js} +1 -1
  97. package/src/assets/web-panel/assets/index-5iAFwYx1.js +1 -0
  98. package/src/assets/web-panel/assets/{index-CRLjEP1H.js → index-9PIVuXqo.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BfPL-m46.js → index-B7SrC-On.js} +1 -1
  100. package/src/assets/web-panel/assets/{index--lap6cf7.js → index-BJi3UqBt.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-B3CDgvNW.js → index-BY8-nCgs.js} +1 -1
  102. package/src/assets/web-panel/assets/index-BfnB7LPx.js +1 -0
  103. package/src/assets/web-panel/assets/{index-De0quFDw.js → index-Bfw2hf2s.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BunaQCa0.js → index-BjcWKg1S.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BsMfoaeo.js → index-BnSfnSMP.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-C0v6YKKO.js → index-Bnua8IX3.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-C4SJVjs4.js → index-C-ZCaUJc.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BNZDILcO.js → index-C2NyllnW.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-YjcumOZ4.js → index-C327pLbw.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CRnUE8Cb.js → index-CMHqs3Jb.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CIpVrf5P.js → index-CfLJ42kv.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BEVm0lCS.js → index-Cr3uuz0C.js} +3 -3
  113. package/src/assets/web-panel/assets/{index-Dw1PyYPm.js → index-CsovTpx9.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-lJBjB1QX.js → index-CzE53HiB.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CpIGIB85.js → index-DBkzrpiL.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DoXk5KJ8.js → index-DNMxIsDg.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DsvfrESp.js → index-DUU2606o.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Cip8emOQ.js → index-DdyjrCAu.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-ATcSr_2O.js → index-DkH1JvEt.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BV551878.js → index-Dlxh9D8D.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BU_2qiDk.js → index-DuIkfnTF.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-zb7rVOhV.js → index-DwHSZLXg.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-MtOuq2i6.js → index-E_v1ZCTP.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B8BsdNV_.js → index-FQBsuExX.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-WUrJ6S1j.js → index-GA80ir_u.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-RMZ8a7Yt.js → index-IZAWt0H8.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-D1r9tT3J.js → index-Iw0FfqUz.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BowH1eLQ.js → index-KF9Zmqln.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Puq6Luf1.js → index-abmn_8Yz.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-QIpnpZgz.js → index-gINyI1rh.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-C9Y3k0HI.js → index-jYaogFE8.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-B4wiTEOM.js → index-nSn2-C4N.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DT3zPSVX.js → index-qMA4GYdQ.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-CYHMs1ag.js → initDefaultProps-DI0Zaf8-.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-DDB2Ffk5.js → motion-BZDKSy3Z.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-Bg3cD-dX.js → move-DsIW01Vp.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-B5nedjTS.js → mtc-parser-CohkDhsf.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-BHyuUILu.js → omit-DpiN5rzg.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-C9YQnEN9.js → pickAttrs-qbuqni3y.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-DBUFYUe7.js → placementArrow-mBky6714.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-nBs2Obnk.js → responsiveObserve-Bas3eQ2K.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-DkCEaXNU.js → slide-CBx_SKoK.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-aqE1Wi6U.js → statusUtils-BlYguJSV.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-CN5bhfHB.js → styleChecker-BCYQmRGv.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-B_oX_kvJ.js → useFlexGapSupport-Dp5dybLM.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-rCuXHeme.js → useFs-DHAMBgA2.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-DZR9Abyd.js → usePersonalDataHub-W6jsSw76.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-ConCYQlX.js → vnode-BkI6aayc.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-CUdDxG5M.js → zoom-C_IMGueq.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/agenda.js +115 -3
  152. package/src/commands/agent.js +15 -0
  153. package/src/commands/background-session.js +49 -0
  154. package/src/commands/plugin.js +235 -23
  155. package/src/commands/session.js +13 -0
  156. package/src/data/changelog.json +26 -0
  157. package/src/gateways/terminal/PtyManager.js +45 -4
  158. package/src/gateways/ws/ws-server.js +10 -1
  159. package/src/harness/background-task-manager.js +70 -2
  160. package/src/harness/jsonl-session-store.js +6 -4
  161. package/src/harness/worktree-isolator.js +14 -3
  162. package/src/lib/agent-core.js +1 -0
  163. package/src/lib/agent-schedule-store.js +45 -22
  164. package/src/lib/agent-team/team-runner.js +19 -1
  165. package/src/lib/background-agent-supervisor.js +49 -0
  166. package/src/lib/hook-runner.js +6 -2
  167. package/src/lib/packer/pkg-config-generator.js +3 -1
  168. package/src/lib/plugin-runtime/bin.js +1148 -70
  169. package/src/lib/plugin-runtime/install.js +226 -7
  170. package/src/lib/plugin-runtime/manifest.js +68 -12
  171. package/src/lib/plugin-runtime/scopes.js +33 -12
  172. package/src/lib/plugin-usage-attribution.js +44 -0
  173. package/src/lib/process-execution-broker/index.js +626 -48
  174. package/src/lib/process-execution-broker/platform-sandbox.js +4971 -266
  175. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  176. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  177. package/src/lib/process-execution-broker/windows-sandbox.cs +3856 -0
  178. package/src/lib/repl-bang-memorize.js +67 -10
  179. package/src/lib/session-usage.js +77 -0
  180. package/src/lib/slash-commands.js +27 -4
  181. package/src/repl/agent-repl.js +10 -9
  182. package/src/runtime/agent-core.js +376 -52
  183. package/src/runtime/agent-runtime.js +13 -1
  184. package/src/runtime/headless-runner.js +13 -4
  185. package/src/runtime/headless-stream.js +20 -1
  186. package/src/runtime/mcp-config.js +15 -5
  187. package/src/assets/web-panel/assets/ChatBubbleRenderer-BKPPNmZI.js +0 -1
  188. package/src/assets/web-panel/assets/MobileProjects-D_zoJ8Xs.js +0 -1
  189. package/src/assets/web-panel/assets/Projects-DAWkVFkc.js +0 -1
  190. package/src/assets/web-panel/assets/RemoteSession-mlglCdQi.js +0 -5
  191. package/src/assets/web-panel/assets/Tasks-CBQzoAu5.js +0 -1
  192. package/src/assets/web-panel/assets/devWarning-BzPwHnw8.js +0 -1
  193. package/src/assets/web-panel/assets/index-DacrAdc-.js +0 -1
  194. package/src/assets/web-panel/assets/index-EHzNJozo.js +0 -1
  195. package/src/lib/process-execution-broker/windows-sandbox.ps1 +0 -999
@@ -26,22 +26,32 @@ import {
26
26
  listInstalledVersions,
27
27
  activeVersion,
28
28
  discoverPlugins,
29
+ DISABLED_FILENAME,
29
30
  } from "./scopes.js";
30
- import { verifyPluginManifest } from "../plugin-security.js";
31
+ import {
32
+ enforcePluginPolicy,
33
+ verifyPluginManifest,
34
+ } from "../plugin-security.js";
31
35
  import {
32
36
  writePluginLock,
33
37
  buildPluginSbom,
34
38
  LOCK_FILENAME,
39
+ readPluginLock,
40
+ verifyInstalledSignature,
35
41
  } from "./signature.js";
42
+ import { loadManagedPluginPolicy, filterByManagedPolicy } from "./policy.js";
43
+ import { managedSettingsPath } from "../settings-loader.cjs";
36
44
  import executionBroker from "../process-execution-broker/index.js";
37
45
 
38
46
  export const MAX_LISTED_PLUGIN_VERSIONS = 64;
47
+ export const SOURCE_METADATA_FILENAME = ".plugin-source.json";
39
48
 
40
49
  export const _deps = {
41
50
  existsSync: fs.existsSync,
42
51
  mkdirSync: fs.mkdirSync,
43
52
  rmSync: fs.rmSync,
44
53
  readdirSync: fs.readdirSync,
54
+ readFileSync: fs.readFileSync,
45
55
  copyFileSync: fs.copyFileSync,
46
56
  lstatSync: fs.lstatSync,
47
57
  writeFileSync: fs.writeFileSync,
@@ -69,6 +79,17 @@ export function installFromDirectory(srcDir, opts = {}) {
69
79
  );
70
80
  }
71
81
  const { name, version } = manifest.metadata;
82
+ const sourceMetadata = normalizeSourceMetadata(opts.sourceMetadata);
83
+ if (opts.managedPolicy) {
84
+ enforcePluginPolicy(
85
+ {
86
+ name,
87
+ source: opts.policySource || sourceMetadata?.source || null,
88
+ action: "install",
89
+ },
90
+ opts.managedPolicy,
91
+ );
92
+ }
72
93
 
73
94
  // Optional signature/integrity verification of the manifest at install time
74
95
  // (Phase 3.3l). verifyPluginManifest THROWS on any mismatch/failure — a signed
@@ -108,6 +129,15 @@ export function installFromDirectory(srcDir, opts = {}) {
108
129
  // unsigned install that forged lock would otherwise survive into the
109
130
  // "immutable" version dir and defeat load-time requireSignedPlugins.
110
131
  _deps.rmSync(path.join(dest, LOCK_FILENAME), { force: true });
132
+ _deps.rmSync(path.join(dest, SOURCE_METADATA_FILENAME), { force: true });
133
+
134
+ if (sourceMetadata) {
135
+ _deps.writeFileSync(
136
+ path.join(dest, SOURCE_METADATA_FILENAME),
137
+ JSON.stringify(sourceMetadata, null, 2),
138
+ "utf8",
139
+ );
140
+ }
111
141
 
112
142
  // Record the verified signature into the installed (immutable) version dir so
113
143
  // load-time enforcement can re-check it. The manifest was copied verbatim, so
@@ -138,6 +168,8 @@ export function installFromDirectory(srcDir, opts = {}) {
138
168
  dir: dest,
139
169
  warnings: manifest.warnings,
140
170
  signatureVerified: verification?.signatureVerified === true,
171
+ sourceMetadata,
172
+ enabled: isPluginEnabled(name, { scope, cwd: opts.cwd }),
141
173
  };
142
174
  }
143
175
 
@@ -150,8 +182,21 @@ export function installFromDirectory(srcDir, opts = {}) {
150
182
  */
151
183
  export function installFromSource(source, opts = {}) {
152
184
  return _withMaterializedSource(source, (dir, info) => {
153
- const res = installFromDirectory(dir, opts);
154
- return info ? { ...res, source: info.url, ref: info.ref || null } : res;
185
+ const sourceMetadata =
186
+ normalizeSourceMetadata(opts.sourceMetadata) ||
187
+ normalizeSourceMetadata(
188
+ info
189
+ ? { type: "git", source: info.url, ref: info.ref || null }
190
+ : { type: "local", source: path.resolve(String(source || "")) },
191
+ );
192
+ const res = installFromDirectory(dir, { ...opts, sourceMetadata });
193
+ return info
194
+ ? {
195
+ ...res,
196
+ source: sourceMetadata?.source || null,
197
+ ref: sourceMetadata?.ref || null,
198
+ }
199
+ : res;
155
200
  });
156
201
  }
157
202
 
@@ -202,6 +247,13 @@ export function updatePlugin(source, opts = {}) {
202
247
  );
203
248
  }
204
249
  const { name, version } = manifest.metadata;
250
+ const sourceMetadata =
251
+ normalizeSourceMetadata(opts.sourceMetadata) ||
252
+ normalizeSourceMetadata(
253
+ info
254
+ ? { type: "git", source: info.url, ref: info.ref || null }
255
+ : { type: "local", source: path.resolve(String(source || "")) },
256
+ );
205
257
  const previousVersion = getActiveVersion(name, { scope, cwd: opts.cwd });
206
258
  const dest = pluginVersionDir(scope, name, version, { cwd: opts.cwd });
207
259
  const sameVersionExists = _deps.existsSync(dest);
@@ -218,18 +270,24 @@ export function updatePlugin(source, opts = {}) {
218
270
  previousVersion,
219
271
  updated: previousVersion !== version,
220
272
  reinstalled: false,
221
- source: info ? info.url : null,
273
+ source: sourceMetadata?.source || null,
274
+ ref: sourceMetadata?.ref || null,
222
275
  };
223
276
  }
224
277
 
225
- const res = installFromDirectory(dir, { ...opts, scope, force: true });
278
+ const res = installFromDirectory(dir, {
279
+ ...opts,
280
+ scope,
281
+ force: true,
282
+ sourceMetadata,
283
+ });
226
284
  return {
227
285
  ...res,
228
286
  previousVersion,
229
287
  updated: previousVersion !== version,
230
288
  reinstalled: sameVersionExists,
231
- source: info ? info.url : null,
232
- ref: info ? info.ref || null : null,
289
+ source: sourceMetadata?.source || null,
290
+ ref: sourceMetadata?.ref || null,
233
291
  };
234
292
  });
235
293
  }
@@ -319,10 +377,26 @@ export function fetchGitRepo(url, ref) {
319
377
  * invisible and impossible to inspect / uninstall / un-deny.
320
378
  */
321
379
  export function listInstalled(opts = {}) {
380
+ let managed = null;
381
+ let managedError = null;
382
+ try {
383
+ managed = loadManagedPluginPolicy({
384
+ env: opts.env,
385
+ managedSettingsFile: opts.managedSettingsFile,
386
+ });
387
+ } catch (error) {
388
+ managedError = error;
389
+ }
390
+ const policySource = managedSettingsPath({
391
+ env: opts.env,
392
+ managedSettingsFile: opts.managedSettingsFile,
393
+ });
394
+
322
395
  return discoverPlugins({
323
396
  cwd: opts.cwd,
324
397
  scopes: opts.scopes,
325
398
  skipPolicy: true,
399
+ includeDisabled: true,
326
400
  }).map((p) => {
327
401
  const allVersions = listInstalledVersions(p.scope, p.name, {
328
402
  cwd: opts.cwd,
@@ -335,6 +409,16 @@ export function listInstalled(opts = {}) {
335
409
  ) {
336
410
  versions[versions.length - 1] = p.version;
337
411
  }
412
+ const lock = readPluginLock(p.root);
413
+ const signature = verifyInstalledSignature(p);
414
+ const policyResult =
415
+ managed && !managedError
416
+ ? filterByManagedPolicy([p], managed)
417
+ : { kept: [p], dropped: [] };
418
+ const policyDrop = policyResult.dropped[0] || null;
419
+ const sbomFiles = Array.isArray(lock?.sbom?.files)
420
+ ? lock.sbom.files.slice(0, 100000)
421
+ : [];
338
422
  return {
339
423
  name: p.name,
340
424
  version: p.version,
@@ -342,6 +426,45 @@ export function listInstalled(opts = {}) {
342
426
  scope: p.scope,
343
427
  dir: p.root,
344
428
  ok: p.manifest?.ok === true,
429
+ enabled: p.enabled !== false,
430
+ source: readSourceMetadata(p.root),
431
+ integrity: {
432
+ signature: {
433
+ present: Boolean(lock?.signatureBase64 && lock?.publicKeyPem),
434
+ verified: signature.signed === true,
435
+ reason: signature.signed ? null : signature.reason || "unsigned",
436
+ manifestSha256: lock?.sha256 || null,
437
+ publicKeySha256:
438
+ signature.publicKeySha256 || lock?.publicKeySha256 || null,
439
+ },
440
+ sbom: {
441
+ present: Boolean(lock?.sbom),
442
+ digest: lock?.sbom?.digest || null,
443
+ fileCount: sbomFiles.length,
444
+ totalBytes: Math.min(
445
+ Number.MAX_SAFE_INTEGER,
446
+ sbomFiles.reduce(
447
+ (sum, file) =>
448
+ sum +
449
+ (Number.isFinite(Number(file?.bytes)) && Number(file.bytes) > 0
450
+ ? Number(file.bytes)
451
+ : 0),
452
+ 0,
453
+ ),
454
+ ),
455
+ },
456
+ },
457
+ policy: {
458
+ managed: Boolean(managed) || Boolean(managedError),
459
+ source: policySource,
460
+ allowed: !managedError && !policyDrop,
461
+ reason: managedError
462
+ ? managedError.message
463
+ : policyDrop?.reason || (managed ? "allowed by managed policy" : ""),
464
+ requireSigned:
465
+ managed?.requireSignedPlugins === true ||
466
+ managed?.requireSignedPlugins === "require",
467
+ },
345
468
  };
346
469
  });
347
470
  }
@@ -412,11 +535,107 @@ export function setActiveVersion(name, version, opts = {}) {
412
535
  return { name, version, scope, active: version };
413
536
  }
414
537
 
538
+ /** Enable or disable one scoped plugin without deleting immutable versions. */
539
+ export function setPluginEnabled(name, enabled, opts = {}) {
540
+ const scope = opts.scope || "user";
541
+ const cwd = opts.cwd;
542
+ const nameDir = pluginNameDir(scope, name, { cwd });
543
+ if (
544
+ !_deps.existsSync(nameDir) ||
545
+ listInstalledVersions(scope, name, { cwd }).length === 0
546
+ ) {
547
+ throw new Error(`${name} is not installed at ${scope} scope`);
548
+ }
549
+ const marker = path.join(nameDir, DISABLED_FILENAME);
550
+ if (enabled) {
551
+ _deps.rmSync(marker, { force: true });
552
+ } else {
553
+ _deps.writeFileSync(
554
+ marker,
555
+ JSON.stringify(
556
+ {
557
+ disabled: true,
558
+ reason: String(opts.reason || "disabled by user").slice(0, 256),
559
+ },
560
+ null,
561
+ 2,
562
+ ),
563
+ "utf8",
564
+ );
565
+ }
566
+ return { name, scope, enabled: Boolean(enabled) };
567
+ }
568
+
569
+ /** Current lifecycle state for one scoped plugin. */
570
+ export function isPluginEnabled(name, opts = {}) {
571
+ const scope = opts.scope || "user";
572
+ return !_deps.existsSync(
573
+ path.join(pluginNameDir(scope, name, { cwd: opts.cwd }), DISABLED_FILENAME),
574
+ );
575
+ }
576
+
415
577
  /** Which version is active for a plugin at a scope (or null). */
416
578
  export function getActiveVersion(name, opts = {}) {
417
579
  return activeVersion(opts.scope || "user", name, { cwd: opts.cwd });
418
580
  }
419
581
 
582
+ function readSourceMetadata(versionDir) {
583
+ const file = path.join(versionDir, SOURCE_METADATA_FILENAME);
584
+ if (!_deps.existsSync(file)) return null;
585
+ try {
586
+ return normalizeSourceMetadata(
587
+ JSON.parse(_deps.readFileSync(file, "utf8")),
588
+ );
589
+ } catch {
590
+ return null;
591
+ }
592
+ }
593
+
594
+ function normalizeSourceMetadata(value) {
595
+ if (!value || typeof value !== "object") return null;
596
+ const type = ["local", "git", "registry"].includes(value.type)
597
+ ? value.type
598
+ : "git";
599
+ const source = sanitizeSource(value.source, type === "local");
600
+ if (!source) return null;
601
+ const metadata = {
602
+ version: 1,
603
+ type,
604
+ source,
605
+ ref: cleanBounded(value.ref, 256),
606
+ };
607
+ const registry = sanitizeSource(value.registry);
608
+ const resolvedSource = sanitizeSource(value.resolvedSource);
609
+ const packageName = cleanBounded(value.package, 256);
610
+ if (registry) metadata.registry = registry;
611
+ if (resolvedSource) metadata.resolvedSource = resolvedSource;
612
+ if (packageName) metadata.package = packageName;
613
+ if (value.offline === true) metadata.offline = true;
614
+ return metadata;
615
+ }
616
+
617
+ function sanitizeSource(value, preservePath = false) {
618
+ const raw = cleanBounded(value, 4096);
619
+ if (!raw) return null;
620
+ if (preservePath) return raw;
621
+ try {
622
+ const parsed = new URL(raw);
623
+ parsed.username = "";
624
+ parsed.password = "";
625
+ parsed.search = "";
626
+ parsed.hash = "";
627
+ return parsed.toString();
628
+ } catch {
629
+ return raw;
630
+ }
631
+ }
632
+
633
+ function cleanBounded(value, max) {
634
+ if (typeof value !== "string") return null;
635
+ const clean = value.replace(/\p{Cc}/gu, "").trim();
636
+ return clean ? clean.slice(0, max) : null;
637
+ }
638
+
420
639
  // ── guarded recursive copy ────────────────────────────────────────────────
421
640
 
422
641
  /**
@@ -199,14 +199,15 @@ export function parsePluginManifest(root) {
199
199
  warnings,
200
200
  manifestSandboxPolicy,
201
201
  );
202
- c.bin = resolveBin(abs, manifest, safeResolve, warnings);
202
+ c.bin = resolveBin(
203
+ abs,
204
+ manifest,
205
+ safeResolve,
206
+ errors,
207
+ warnings,
208
+ manifestSandboxPolicy,
209
+ );
203
210
  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
- }
210
211
 
211
212
  // ── declared capabilities + options schema (P1) ──
212
213
  const hasPermissions =
@@ -451,10 +452,17 @@ function resolveLsp(
451
452
  return out;
452
453
  }
453
454
 
454
- function resolveBin(root, manifest, safeResolve, warnings) {
455
+ function resolveBin(
456
+ root,
457
+ manifest,
458
+ safeResolve,
459
+ errors,
460
+ warnings,
461
+ manifestSandboxPolicy,
462
+ ) {
455
463
  const out = [];
456
464
  const seen = new Set();
457
- const push = (name, rel) => {
465
+ const push = (name, rel, descriptorPolicy = null) => {
458
466
  const absPath = safeResolve(rel, `bin["${name}"]`);
459
467
  if (!absPath) return;
460
468
  if (!_deps.existsSync(absPath)) {
@@ -462,8 +470,29 @@ function resolveBin(root, manifest, safeResolve, warnings) {
462
470
  return;
463
471
  }
464
472
  if (seen.has(name)) return;
473
+ let sandboxPolicy;
474
+ try {
475
+ sandboxPolicy = mergePluginSandboxPolicies(
476
+ manifestSandboxPolicy,
477
+ descriptorPolicy,
478
+ );
479
+ } catch (err) {
480
+ errors.push(`bin "${name}": ${err.message}`);
481
+ return;
482
+ }
483
+ if (sandboxPolicy && !/^[A-Za-z0-9._-]+$/.test(name)) {
484
+ errors.push(
485
+ `bin alias "${name}" with sandboxPolicy contains unsupported characters; use letters, numbers, dot, underscore, or hyphen`,
486
+ );
487
+ return;
488
+ }
465
489
  seen.add(name);
466
- out.push({ name, path: rel, absPath });
490
+ out.push({
491
+ name,
492
+ path: rel,
493
+ absPath,
494
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
495
+ });
467
496
  };
468
497
 
469
498
  if (
@@ -471,8 +500,35 @@ function resolveBin(root, manifest, safeResolve, warnings) {
471
500
  typeof manifest.bin === "object" &&
472
501
  !Array.isArray(manifest.bin)
473
502
  ) {
474
- for (const [name, rel] of Object.entries(manifest.bin)) {
475
- if (typeof rel === "string") push(name, rel);
503
+ for (const [name, descriptor] of Object.entries(manifest.bin)) {
504
+ if (typeof descriptor === "string") {
505
+ push(name, descriptor);
506
+ continue;
507
+ }
508
+ if (
509
+ descriptor &&
510
+ typeof descriptor === "object" &&
511
+ !Array.isArray(descriptor)
512
+ ) {
513
+ const unsupported = Object.keys(descriptor).filter(
514
+ (key) => !["path", "sandboxPolicy"].includes(key),
515
+ );
516
+ if (unsupported.length > 0) {
517
+ errors.push(
518
+ `bin "${name}" contains unsupported field: ${unsupported[0]}`,
519
+ );
520
+ continue;
521
+ }
522
+ if (typeof descriptor.path !== "string") {
523
+ errors.push(`bin "${name}".path must be a non-empty string`);
524
+ continue;
525
+ }
526
+ push(name, descriptor.path, descriptor.sandboxPolicy);
527
+ continue;
528
+ }
529
+ errors.push(
530
+ `bin "${name}" must be a path string or { path, sandboxPolicy } object`,
531
+ );
476
532
  }
477
533
  return out;
478
534
  }
@@ -42,6 +42,7 @@ export const _deps = {
42
42
 
43
43
  // Lowest → highest precedence.
44
44
  export const SCOPES = ["user", "project", "local"];
45
+ export const DISABLED_FILENAME = ".disabled";
45
46
 
46
47
  /** Filesystem-safe encoding of a (possibly scoped, e.g. @org/name) plugin name. */
47
48
  export function encodeName(name) {
@@ -105,6 +106,13 @@ export function activeVersion(scope, name, opts = {}) {
105
106
  return versions[0];
106
107
  }
107
108
 
109
+ /** Whether one scoped plugin install is eligible for runtime discovery. */
110
+ export function isPluginEnabled(scope, name, opts = {}) {
111
+ return !_deps.existsSync(
112
+ path.join(pluginNameDir(scope, name, opts), DISABLED_FILENAME),
113
+ );
114
+ }
115
+
108
116
  /**
109
117
  * Discover every installed plugin's ACTIVE version across all scopes, applying
110
118
  * scope precedence (local > project > user) so a name installed at multiple
@@ -116,8 +124,8 @@ export function activeVersion(scope, name, opts = {}) {
116
124
  * for tooling that must see even blocked plugins (e.g. `cc plugin installed`
117
125
  * showing why something is blocked). No managed settings file → no filtering.
118
126
  *
119
- * @param {object} [opts] { cwd, scopes?, skipPolicy?, env?, managedSettingsFile? }
120
- * @returns {Array<{scope, name, version, root, manifest}>}
127
+ * @param {object} [opts] { cwd, scopes?, skipPolicy?, includeDisabled?, env?, managedSettingsFile? }
128
+ * @returns {Array<{scope, name, version, root, manifest, enabled}>}
121
129
  */
122
130
  export function discoverPlugins(opts = {}) {
123
131
  // `cc agent --bare` (CC_PLUGINS=0): the plugin runtime is fully off. Every
@@ -136,13 +144,22 @@ export function discoverPlugins(opts = {}) {
136
144
  const byName = new Map(); // name → record (later scope overrides earlier)
137
145
  for (const scope of scopes) {
138
146
  const root = scopeRoot(scope, opts);
139
- if (!dirExists(root)) continue;
140
- for (const encoded of listDirs(root)) {
147
+ if (!dirExists(root, opts.strictIo === true)) continue;
148
+ for (const encoded of listDirs(root, opts.strictIo === true)) {
141
149
  const nameDir = path.join(root, encoded);
142
- const version = activeVersionForDir(nameDir);
150
+ const enabled = !_deps.existsSync(path.join(nameDir, DISABLED_FILENAME));
151
+ if (!enabled && opts.includeDisabled !== true) continue;
152
+ const version = activeVersionForDir(nameDir, opts.strictIo === true);
143
153
  if (!version) continue;
144
154
  const versionDir = path.join(nameDir, version);
145
155
  const manifest = parsePluginManifest(versionDir);
156
+ if (opts.strictIo === true && manifest.ok !== true) {
157
+ throw new Error(
158
+ `plugin manifest is not safely loadable at ${versionDir}: ${
159
+ manifest.errors?.join("; ") || "unknown manifest error"
160
+ }`,
161
+ );
162
+ }
146
163
  manifest.scope = scope;
147
164
  const name = manifest.metadata?.name || encoded;
148
165
  byName.set(name, {
@@ -151,6 +168,7 @@ export function discoverPlugins(opts = {}) {
151
168
  version: manifest.metadata?.version || version,
152
169
  root: versionDir,
153
170
  manifest,
171
+ enabled,
154
172
  });
155
173
  }
156
174
  }
@@ -188,8 +206,8 @@ export function discoverPlugins(opts = {}) {
188
206
 
189
207
  // ── internals ────────────────────────────────────────────────────────────
190
208
 
191
- function activeVersionForDir(nameDir) {
192
- if (!dirExists(nameDir)) return null;
209
+ function activeVersionForDir(nameDir, strictIo = false) {
210
+ if (!dirExists(nameDir, strictIo)) return null;
193
211
  const versions = _deps
194
212
  .readdirSync(nameDir, { withFileTypes: true })
195
213
  .filter((e) => e.isDirectory())
@@ -202,28 +220,31 @@ function activeVersionForDir(nameDir) {
202
220
  try {
203
221
  const pinned = _deps.readFileSync(activeFile, "utf8").trim();
204
222
  if (versions.includes(pinned)) return pinned;
205
- } catch {
223
+ } catch (error) {
224
+ if (strictIo) throw error;
206
225
  /* fall through */
207
226
  }
208
227
  }
209
228
  return versions[0];
210
229
  }
211
230
 
212
- function dirExists(dir) {
231
+ function dirExists(dir, strictIo = false) {
213
232
  try {
214
233
  return _deps.existsSync(dir) && _deps.statSync(dir).isDirectory();
215
- } catch {
234
+ } catch (error) {
235
+ if (strictIo) throw error;
216
236
  return false;
217
237
  }
218
238
  }
219
239
 
220
- function listDirs(dir) {
240
+ function listDirs(dir, strictIo = false) {
221
241
  try {
222
242
  return _deps
223
243
  .readdirSync(dir, { withFileTypes: true })
224
244
  .filter((e) => e.isDirectory())
225
245
  .map((e) => e.name);
226
- } catch {
246
+ } catch (error) {
247
+ if (strictIo) throw error;
227
248
  return [];
228
249
  }
229
250
  }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Extract a secret-free plugin identity from a tool result. Plugin bins expose
3
+ * an explicit `plugin_bin` record; extension-tier tools may expose a
4
+ * `toolAttribution.source` in the stable `plugin:<id>` / `plugin/<id>` form.
5
+ */
6
+ export function extractPluginUsageAttribution(result) {
7
+ const pluginBin =
8
+ result?.plugin_bin && typeof result.plugin_bin === "object"
9
+ ? result.plugin_bin
10
+ : null;
11
+ if (pluginBin?.plugin) {
12
+ const plugin = boundedIdentity(pluginBin.plugin, 256);
13
+ if (!plugin) return {};
14
+ return {
15
+ plugin,
16
+ pluginVersion: pluginBin.version
17
+ ? boundedIdentity(pluginBin.version, 128) || undefined
18
+ : undefined,
19
+ };
20
+ }
21
+ const attribution =
22
+ result?.toolAttribution && typeof result.toolAttribution === "object"
23
+ ? result.toolAttribution
24
+ : null;
25
+ const source =
26
+ typeof attribution?.source === "string" ? attribution.source : "";
27
+ const match = source.match(/^plugin(?::|\/)(.+)$/i);
28
+ const plugin = match ? boundedIdentity(match[1], 256) : null;
29
+ return plugin
30
+ ? {
31
+ plugin,
32
+ pluginVersion: attribution.version
33
+ ? boundedIdentity(attribution.version, 128) || undefined
34
+ : undefined,
35
+ }
36
+ : {};
37
+ }
38
+
39
+ function boundedIdentity(value, max) {
40
+ const clean = String(value ?? "")
41
+ .replace(/\p{Cc}/gu, "")
42
+ .trim();
43
+ return clean ? clean.slice(0, max) : null;
44
+ }