chainlesschain 0.162.162 → 0.162.164

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 (237) hide show
  1. package/package.json +3 -1
  2. package/src/assets/web-panel/assets/{AIOps-ClI2ALgv.js → AIOps-BqRkaHGb.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BDjRcJaK.js → ActionButton-DWO7OZLC.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DikSRsaT.js → Analytics-C5oaVezM.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-9mLhMFmz.js → AppLayout-CsUESDoZ.js} +3 -3
  6. package/src/assets/web-panel/assets/{Artifacts-BSr88BrH.js → Artifacts-Dragqu5k.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CdvyimOF.js → Audit-CQsaHctY.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-D3_hCedh.js → BackgroundAgents-DdG1jNSM.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-Cyvv9RHf.js → Backup-DN6sjK4z.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-BZM14kt8.js → BaseInput-Cxm_ZTCl.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-DrQSokVz.js → Chat-DVXGmA56.js} +6 -6
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-HwJR-C6B.js → ChatBubbleRenderer-BdaCL_iE.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-bFLYeWMV.js → Checkbox-DEc4vD_A.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-7mN7xJXT.js → Codegen-CKiTcv36.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DAB5ZWm7.js → Col-Br9UUNN0.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-B_1TymaK.js → Community-NeUH_70T.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-Bq1uSWLJ.js → Compact-BqR-z2Df.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CyiE1jEe.js → Compliance-DcWB_SgZ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Cpk0D7vm.js → Cowork-BfhgcudF.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-d_Lv3rdB.js → Cron-LaU29LgD.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-DMqhHjMR.js → Crosschain-B2ZB9uwW.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-Br-oPuBw.js → DID-BuHl38A2.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-CfLW_RaP.js → Dashboard-m627yqvZ.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-DZnTOExV.js → Dropdown-C7bxF8F5.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-DHIUHDv1.js → EmailListRenderer-DPLPQzLp.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bw3t3myu.js → FamilyGuardDashboard-cOiH6ike.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-D9cpQPAY.js → Federation-Bvkzg88z.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-Jdczr9Mr.js → FormItemContext-BTsiA7h_.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BT8ZMpC5.js → GenericCardRenderer-BGnVE5qu.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-DjARip2b.js → Git-DS_j6r25.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-BgWbSRcj.js → Governance-CKRG3Rsl.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CfouJciM.js → Inference-DW8INoNQ.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-50l7mrAr.js → KnowledgeGraph-BhEYeFhC.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-rq50H84B.js → Logs-wqi0uxuR.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-DUPIEdQH.js → MarkdownRenderer-DS8dE8ty.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-BYgPDmHU.js → Marketplace-CMlTuPLT.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-5DVgcFdx.js → McpTools-CSx4m5JJ.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-B70LPk7f.js → Memory-vnQCUy-_.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-De8ssA5Y.js → MobileBridge-BV2NA742.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-Bw0k8e1B.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-RJsOTsIA.js → Mtc-CzK_FPB1.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-Crh-6BEK.js → MtcAudit-Cb7G9oL3.js} +5 -5
  43. package/src/assets/web-panel/assets/{Multisig-wuoR5l3x.js → Multisig-qJxlUyzr.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-U2Mji4fZ.js → NLProgramming-DqaBJ7Te.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CdTZAyDH.js → Notes-CwhTJkRi.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-C-FucWE7.js → NotificationSettings-YDs4Fal4.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DCYFTeaI.js → OrderTableRenderer-CvEALpq7.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-an3OF85L.js → Organization-CEmQm9n8.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-C-1vmYFe.js → Overflow-zby_P_LA.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-Cwh47xKg.js → P2P-DsmDNFNT.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Dm38GT0k.js → PdhVaultBrowser-FWwFlZGF.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-DS2Y8awD.js → Permissions-DuzQ6me5.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-BQNziA0I.js → PersonalDataHub-DZbeN613.js} +3 -3
  54. package/src/assets/web-panel/assets/{Pipeline-DF85L1ue.js → Pipeline-CoNSVAUS.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-BuLornF-.js → Privacy-OYJEAaMC.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-C3Y31IwX.js → ProjectInit-CWIapV--.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-BCPiI8aD.js → ProjectSettings-CX8xMxZn.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-BZ6s4k6g.js → Projects-BLCN8YyY.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-D3Wp022X.js → Providers-BV_sxp75.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-CUJfQkH4.js → QrScannerModal-BTnRrHRp.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-UAc0EO8l.js → QuickAsk-D1KZg018.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-BaxKupcs.js → Recommend-COm5cuNg.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C75NNnXZ.js → RemoteSession-0saWEkYI.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-BVXynZAk.js → Reputation-Dr7iKLS2.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-C5MRZ0bi.js → Row-C1vpPBcr.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-DAKkEYka.js → RssFeed-CxlWrtNj.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DvWv9YOA.js → Search-BT2V8dPN.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-B5peafW1.js → Security-bm9V3c5k.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-UQmvPEaG.js → Services-CocH1yyX.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-C3bhWpuF.js → Skeleton-DHItlMdd.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-DfkKf9_H.js → Skills-C59sLc4w.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-DqMSjlBM.js → Sla-Crj4BMc8.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-C-WbxWUX.js → SpeechSettings-DX4KFceY.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CYuHZ4N2.js → SyncSettings-BD_iwQLr.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-CyeAyB9d.js → Tasks-BmCTzYsH.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-BTCeQnpq.js → Templates-BGUaxZYl.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-BifQuA9Y.js → Tenant-BS0tJx2O.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-Bx_9Bocf.js → Terminal--tHSAsld.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-BX0RFCOm.js → TimelineRenderer-D9-MFxS4.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-CGHrCmUS.js → Tokens-CiCB485f.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DlqaY4SO.js → Trigger-SmCFuW_l.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DfOmj-ay.js → Trust-DkK3Rqe0.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-B72jH5fE.js → UkeySign-CDM4LGdB.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-BT-63fqv.js → VideoEditing-IDB7DJXt.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-Dsul3sHr.js → Wallet-Bikj5cmt.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-Izkwwdgy.js → WebAuthn-C9abWlAw.js} +2 -2
  87. package/src/assets/web-panel/assets/{WorkflowEditor-D3FHSiH1.js → WorkflowEditor-DrbRKVky.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-CmVz5z3U.js → chat-CccCSDQf.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-CuLgyJBp.js → colors-CXObXLhb.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-Ml1YkXpw.js → compact-item-B5kfYAxC.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-BvjJ48bs.js → createContext-DmiPBfip.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-CvKrADNa.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DVC0157C.js → hasIn-Q6lYteAc.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-C229ABfy.js → index-25Wlqps4.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-Ck_52dz_.js → index-33U29UNV.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-QXzlT9KD.js → index-4oXBDczR.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-4SBta3d7.js → index-BGYXvMc5.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-B0S8NzO6.js → index-BJCCa0s_.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DJyN28Et.js → index-BJJ5DhKI.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DC_OI9H2.js → index-BLOzViJ6.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-C_XuRgGn.js → index-BRrbUMoG.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-wvb_FtaV.js → index-BUFpT17Q.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-KMqhcF7l.js → index-BkRDiqGc.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-vG4XSJ1d.js → index-BsfvW6my.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DYXHFr4r.js → index-By-kfPED.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DuGszaWY.js → index-BzFTcQsc.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CXkJH-lK.js → index-C2FXdboq.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Dt5KMjwJ.js → index-C5XOlTHN.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-4wcgvPoF.js → index-CLhBDS2w.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-C_H_S-DY.js → index-CMq7UeoT.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BydjdZnl.js → index-CVMTWNKl.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BjHBpRYZ.js → index-Caghi-yL.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Bzr0rB5T.js → index-CdB6FuSH.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BbPtzB8M.js → index-CoK8-kWQ.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BUt8kTIB.js → index-CsO3GUAY.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-C_vRgTl9.js → index-CwZBa20Z.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DukDVXCv.js → index-D-kASucw.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BbepULSc.js → index-D8eBw7vW.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BfuKP4qe.js → index-D9wgifk4.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BiC6dQZG.js → index-DAYCTc07.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CmFNvNLZ.js → index-DF6Q8l2h.js} +1 -1
  122. package/src/assets/web-panel/assets/index-DTdFUWc_.js +1 -0
  123. package/src/assets/web-panel/assets/{index-KjgKdZe-.js → index-DgTLApS9.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BpFJHiTv.js → index-DgxVJ8pg.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Cw0Rjy87.js → index-DlJvIqoR.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CFYVQMvu.js → index-Dw4eHfsG.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CudmwNBV.js → index-Rh-ytwow.js} +3 -3
  128. package/src/assets/web-panel/assets/{index-DDGaOWBV.js → index-StAEt2pq.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-D3lD5qap.js → index-bzfw951k.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BzG-x9p4.js → index-fSUMbMvv.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B_EvG_GW.js → index-tFHy5Wol.js} +1 -1
  132. package/src/assets/web-panel/assets/index-zQ0M_kz-.js +1 -0
  133. package/src/assets/web-panel/assets/{initDefaultProps-BDgd2YZ8.js → initDefaultProps-BNq7X72N.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-ErtrXdYe.js → motion-DOf9viTc.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-_GPPNCgs.js → move-CRHTcW4x.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-DRLL-8V2.js → mtc-parser-Cyoozgm2.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-p86sl023.js → omit-6-TjFNru.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-BTu7cMLH.js → pickAttrs-DlUmO1zi.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-BEKz6Isr.js → placementArrow-CFOL8mIR.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-Ck42c9MP.js → responsiveObserve-BtaFK5zd.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-1CEvuPsB.js → slide-BIbc2bur.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BDfdx1GJ.js → statusUtils-BXX9d6-w.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-B4K46m5w.js → styleChecker-BGv10Bpn.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-DoIVpJNq.js → useFlexGapSupport-bKnKT8tw.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DdAWi-ln.js → useFs-FY4IL4jn.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-iUu6k8dx.js → usePersonalDataHub-Bwl9TEgu.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-DZz4R6Tw.js → vnode-BM3OHxAT.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-DXlf9sA3.js → zoom-D1fbFiHB.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agenda.js +320 -32
  151. package/src/commands/agent.js +105 -11
  152. package/src/commands/context.js +125 -14
  153. package/src/commands/doctor.js +85 -1
  154. package/src/commands/hook.js +181 -7
  155. package/src/commands/plugin.js +456 -1
  156. package/src/commands/review.js +363 -2
  157. package/src/commands/session.js +191 -0
  158. package/src/commands/team.js +19 -1
  159. package/src/data/changelog.json +10 -4
  160. package/src/gateways/ws/remote-session-protocol.js +39 -0
  161. package/src/gateways/ws/session-protocol.js +55 -2
  162. package/src/gateways/ws/ws-agent-handler.js +50 -0
  163. package/src/harness/jsonl-session-store.js +60 -0
  164. package/src/harness/worktree-isolator.js +140 -5
  165. package/src/lib/agent-schedule-store.js +318 -10
  166. package/src/lib/agent-team/team-worktree.js +37 -2
  167. package/src/lib/agent-tool-admission.js +125 -0
  168. package/src/lib/app-preview.js +360 -0
  169. package/src/lib/async-hook-supervisor.cjs +54 -0
  170. package/src/lib/capability-manifest.js +319 -0
  171. package/src/lib/capability-negotiation.js +23 -12
  172. package/src/lib/channels/channel-manager.js +54 -2
  173. package/src/lib/cli-context-engineering.js +9 -2
  174. package/src/lib/context-breakdown.js +116 -0
  175. package/src/lib/diagnostic-bundle.js +230 -0
  176. package/src/lib/doctor-checkup.js +275 -4
  177. package/src/lib/execution-location.js +286 -0
  178. package/src/lib/headless-manifest.js +13 -40
  179. package/src/lib/hook-event-log.cjs +193 -0
  180. package/src/lib/hook-stats-store.cjs +197 -0
  181. package/src/lib/idempotency.js +109 -0
  182. package/src/lib/interaction-adapter.js +34 -4
  183. package/src/lib/json-schema-output.js +46 -12
  184. package/src/lib/json-schema-validate.js +143 -3
  185. package/src/lib/jsonl-session-store.js +1 -0
  186. package/src/lib/lsp/__tests__/lsp-manager.test.js +58 -0
  187. package/src/lib/lsp/lsp-manager.js +56 -0
  188. package/src/lib/monitor-event.js +290 -0
  189. package/src/lib/operation-fingerprint.js +217 -0
  190. package/src/lib/plugin-runtime/bin.js +37 -0
  191. package/src/lib/plugin-runtime/capabilities.js +19 -0
  192. package/src/lib/plugin-runtime/capability-consent.js +203 -0
  193. package/src/lib/plugin-runtime/hooks.js +37 -0
  194. package/src/lib/plugin-runtime/lsp.js +38 -0
  195. package/src/lib/plugin-runtime/mcp.js +40 -0
  196. package/src/lib/plugin-runtime/monitors.js +37 -0
  197. package/src/lib/plugin-runtime/plugin-options.js +151 -0
  198. package/src/lib/plugin-runtime/policy.js +62 -0
  199. package/src/lib/plugin-runtime/scopes.js +20 -4
  200. package/src/lib/pr-automation-policy.js +224 -0
  201. package/src/lib/project-instructions.js +128 -0
  202. package/src/lib/remote-approval-bridge.js +66 -13
  203. package/src/lib/repl-goal.js +132 -0
  204. package/src/lib/repl-rewind.js +153 -0
  205. package/src/lib/review-comment-anchor.js +241 -0
  206. package/src/lib/review-pipeline.js +46 -7
  207. package/src/lib/runtime-checkup.js +221 -0
  208. package/src/lib/schedule-planner.js +17 -1
  209. package/src/lib/secret-scan.js +409 -0
  210. package/src/lib/session-branch.js +0 -0
  211. package/src/lib/settings-hook-events.cjs +13 -0
  212. package/src/lib/settings-loader.cjs +42 -0
  213. package/src/lib/side-effect-ledger-store.js +88 -0
  214. package/src/lib/side-effect-ledger.js +401 -0
  215. package/src/lib/skill-loader.js +32 -0
  216. package/src/lib/skill-path-scope.js +63 -0
  217. package/src/lib/sub-agent-context.js +110 -9
  218. package/src/lib/sub-agent-registry.js +19 -0
  219. package/src/lib/subagent-inheritance.js +112 -0
  220. package/src/lib/telemetry/span-recorder.js +17 -4
  221. package/src/lib/terminal-context.js +195 -0
  222. package/src/lib/turn-binding-store.js +119 -0
  223. package/src/lib/unattended-action-policy.js +182 -0
  224. package/src/lib/worktree-cleanup-safety.js +138 -0
  225. package/src/lib/worktree-isolator.js +8 -0
  226. package/src/lib/worktree-sparse.js +131 -0
  227. package/src/repl/agent-repl.js +280 -30
  228. package/src/repl/permission-tier.js +28 -0
  229. package/src/runtime/agent-core.js +565 -21
  230. package/src/runtime/coding-agent-contract-shared.cjs +60 -1
  231. package/src/runtime/diagnostics.js +60 -0
  232. package/src/runtime/headless-runner.js +393 -1
  233. package/src/runtime/headless-stream.js +60 -1
  234. package/src/assets/web-panel/assets/MobileProjects-D10VISps.js +0 -1
  235. package/src/assets/web-panel/assets/devWarning-DhiGrbsh.js +0 -1
  236. package/src/assets/web-panel/assets/index-B7a1TrU6.js +0 -1
  237. package/src/assets/web-panel/assets/index-C2B1KRja.js +0 -1
@@ -19,6 +19,33 @@
19
19
  import path from "path";
20
20
  import { discoverPlugins } from "./scopes.js";
21
21
  import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
22
+ import { componentCapabilityDenial } from "./capabilities.js";
23
+
24
+ // One-time stderr notice when a plugin's bin dir is refused at the COMPONENT
25
+ // level because the plugin opted into the capability model but did not declare
26
+ // the `process` capability its executables need. Distinct from the trust gate:
27
+ // these plugins ARE trusted, but their bin component is denied.
28
+ const _capabilityDenied = new Set();
29
+ function warnBinCapabilityDeniedOnce(entries) {
30
+ if (!entries || entries.length === 0) return;
31
+ if (_capabilityDenied.has("bin-capability")) return;
32
+ _capabilityDenied.add("bin-capability");
33
+ const list = entries.map((e) => `${e.name} (${e.reason})`).join("; ");
34
+ try {
35
+ process.stderr.write(
36
+ `[plugins] refused bin dir(s) from plugin(s) that declared a permissions ` +
37
+ `block but did not declare the 'process' capability: ${list}\n` +
38
+ ` add 'process' to the plugin's permissions block to enable them.\n`,
39
+ );
40
+ } catch {
41
+ /* stderr notice is best-effort */
42
+ }
43
+ }
44
+
45
+ /** Test hook: reset the one-time capability-denied warning guard. */
46
+ export function _resetBinWarnings() {
47
+ _capabilityDenied.clear();
48
+ }
22
49
 
23
50
  /**
24
51
  * Collect trusted, installed plugins' `bin/` directories.
@@ -40,10 +67,19 @@ export function collectPluginBinDirs(opts = {}) {
40
67
  "bin",
41
68
  );
42
69
  const out = [];
70
+ const denied = [];
43
71
  for (const p of trusted) {
44
72
  if (!p.manifest || p.manifest.ok !== true) continue;
45
73
  const bins = p.manifest.components?.bin;
46
74
  if (!Array.isArray(bins) || bins.length === 0) continue;
75
+ // Component-level capability gate: a plugin that declared a permissions
76
+ // block but did not declare the `process` capability its executables need
77
+ // gets its bin dir refused here (mirrors the MCP collector's denial).
78
+ const denial = componentCapabilityDenial(p.manifest, ["process"]);
79
+ if (denial) {
80
+ denied.push({ name: p.name, reason: denial.reason });
81
+ continue;
82
+ }
47
83
  // Every bin entry's absPath is a file inside the plugin's bin dir; collect
48
84
  // the unique parent directories (usually just `<root>/bin`).
49
85
  const dirs = new Set(bins.map((b) => path.dirname(b.absPath)));
@@ -56,6 +92,7 @@ export function collectPluginBinDirs(opts = {}) {
56
92
  });
57
93
  }
58
94
  }
95
+ warnBinCapabilityDeniedOnce(denied);
59
96
  return out;
60
97
  }
61
98
 
@@ -220,6 +220,25 @@ export function auditDeclaredCapabilities(manifest) {
220
220
  return findings;
221
221
  }
222
222
 
223
+ /**
224
+ * Component-level capability denial. A plugin that opted into the capability
225
+ * model (declared a `permissions` block, so `capabilitiesDeclared === true`) but
226
+ * ships a component whose required capability it did NOT declare should have that
227
+ * single component refused — not just flagged by the whole-plugin consent notice.
228
+ * `capabilityNames` is the set of capability names whose absence denies this
229
+ * component (e.g. `["process"]` for a hook/bin/lsp, `["monitor"]` for a monitor,
230
+ * `["mcp","network"]` for an MCP server). Legacy plugins (no permissions block)
231
+ * are unrestricted by design and never denied. Returns `{reason}` or null.
232
+ */
233
+ export function componentCapabilityDenial(manifest, capabilityNames) {
234
+ if (!manifest || manifest.capabilitiesDeclared !== true) return null;
235
+ const wanted = new Set(capabilityNames || []);
236
+ const finding = auditDeclaredCapabilities(manifest).find((f) =>
237
+ wanted.has(f.capability),
238
+ );
239
+ return finding ? { reason: finding.reason } : null;
240
+ }
241
+
223
242
  // ─── options schema ─────────────────────────────────────────────────────────
224
243
 
225
244
  export const OPTION_TYPES = Object.freeze([
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Plugin capability CONSENT (Phase 3 gap: capability declaration + re-consent).
3
+ *
4
+ * Complements version trust ([[trust.js]]): trust pins the exact CODE a plugin
5
+ * ships (re-consent on version bump); this pins the CAPABILITY SET the plugin is
6
+ * allowed to request (process / network / filesystem / mcp / monitor /
7
+ * credential). The two are orthogonal — a plugin can be trusted-to-run yet still
8
+ * WIDEN what it wants (add `network:*`, a new credential), which must re-prompt.
9
+ *
10
+ * Consent is keyed by the SET, not the version: once the user consents, later
11
+ * versions that keep the same (or a narrower) capability set stay consented; any
12
+ * WIDENING (a new grant) revokes consent until re-granted. This mirrors
13
+ * `consentRequiredForUpgrade` in [[capabilities.js]] exactly.
14
+ *
15
+ * Recorded in the USER data dir (never in the repo — a checked-in project file
16
+ * must not be able to pre-grant itself capabilities), keyed `<scope>:<name>` →
17
+ * { version, capabilities, consentedAt }. Pure over injected IO so unit tests
18
+ * never touch the real user-data dir.
19
+ */
20
+
21
+ import fs from "fs";
22
+ import path from "path";
23
+ import { getElectronUserDataDir } from "../paths.js";
24
+ import {
25
+ normalizeCapabilities,
26
+ diffCapabilities,
27
+ describeCapabilities,
28
+ } from "./capabilities.js";
29
+
30
+ export const _deps = {
31
+ existsSync: fs.existsSync,
32
+ readFileSync: fs.readFileSync,
33
+ writeFileSync: fs.writeFileSync,
34
+ mkdirSync: fs.mkdirSync,
35
+ now: () => new Date().toISOString(),
36
+ // Injectable so unit tests never touch the real user-data dir.
37
+ storePath: () =>
38
+ path.join(getElectronUserDataDir(), "plugin-capability-consent.json"),
39
+ };
40
+
41
+ function consentKey(scope, name) {
42
+ return `${scope}:${name}`;
43
+ }
44
+
45
+ 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");
60
+ }
61
+
62
+ /**
63
+ * Coerce a capability set to the canonical shape diffCapabilities needs.
64
+ * `normalizeCapabilities` is NOT idempotent — it re-parses `filesystem`/
65
+ * `credential` through `toList`, so feeding it an already-normalized set (e.g.
66
+ * one read back from the store) corrupts those lists. Detect an already-
67
+ * canonical set and pass it through unchanged; only normalize raw input.
68
+ */
69
+ function canonicalCaps(caps) {
70
+ if (
71
+ caps &&
72
+ typeof caps === "object" &&
73
+ caps.network &&
74
+ typeof caps.network === "object" &&
75
+ "domains" in caps.network &&
76
+ caps.filesystem &&
77
+ "roots" in caps.filesystem &&
78
+ caps.credential &&
79
+ "names" in caps.credential
80
+ ) {
81
+ return caps;
82
+ }
83
+ return normalizeCapabilities(caps);
84
+ }
85
+
86
+ /** True when a capability set grants nothing (all-deny) — nothing to consent. */
87
+ export function capabilitiesAreEmpty(caps) {
88
+ return !diffCapabilities(null, canonicalCaps(caps)).widened;
89
+ }
90
+
91
+ /**
92
+ * PURE consent status: given a plugin's DECLARED capabilities and its stored
93
+ * consent entry, decide whether consent covers them. Re-consent is required on
94
+ * a first request or any WIDENING (new grant) — not on a version bump alone
95
+ * (trust handles versions).
96
+ *
97
+ * @param {object} declaredCaps raw or normalized capabilities
98
+ * @param {object|null} entry stored `{ version, capabilities }` (or null)
99
+ * @returns {{consented:boolean, reason:string, added:string[]}}
100
+ */
101
+ export function capabilityConsentStatus(declaredCaps, entry) {
102
+ const declared = canonicalCaps(declaredCaps);
103
+ if (!diffCapabilities(null, declared).widened) {
104
+ return { consented: true, reason: "no capabilities declared", added: [] };
105
+ }
106
+ if (!entry) {
107
+ return {
108
+ consented: false,
109
+ reason: "capabilities never consented",
110
+ added: diffCapabilities(null, declared).added,
111
+ };
112
+ }
113
+ // Coerce the stored set to canonical too (raw hand-written entry, or an
114
+ // already-normalized one read from the store) before diffing.
115
+ const diff = diffCapabilities(canonicalCaps(entry.capabilities), declared);
116
+ if (diff.widened) {
117
+ return {
118
+ consented: false,
119
+ reason: "capabilities widened since last consent",
120
+ added: diff.added,
121
+ };
122
+ }
123
+ return {
124
+ consented: true,
125
+ reason: "consent covers declared capabilities",
126
+ added: [],
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Decide how an install/upgrade command should handle capability consent at the
132
+ * point of installation (pure — no I/O). `notice` is the shape returned by the
133
+ * command layer's capability resolver: `{ consented, added, ... }` or null when
134
+ * the plugin declares no capabilities.
135
+ *
136
+ * Returns one of:
137
+ * - "advisory" — nothing to consent (no caps, or already consented), OR the
138
+ * caller is non-interactive and did not pass an explicit grant flag: print
139
+ * the notice and require a later `cc plugin consent --grant`.
140
+ * - "grant" — an explicit grant flag was given: record consent immediately
141
+ * (scriptable / CI path).
142
+ * - "prompt" — consent is required and the session is interactive: ask the
143
+ * user, then grant on confirmation.
144
+ *
145
+ * Precedence: an explicit grant flag always wins over an interactive prompt, so
146
+ * `--grant-capabilities` is honored even under a TTY without a second question.
147
+ */
148
+ export function resolveConsentAction(
149
+ notice,
150
+ { grant = false, interactive = false } = {},
151
+ ) {
152
+ if (!notice || notice.consented) return "advisory";
153
+ if (grant) return "grant";
154
+ if (interactive) return "prompt";
155
+ return "advisory";
156
+ }
157
+
158
+ /** Load the store and report whether one plugin's declared caps are consented. */
159
+ export function isPluginCapabilityConsented(plugin, declaredCaps) {
160
+ if (!plugin || !plugin.name) return false;
161
+ const entry = loadConsentStore()[consentKey(plugin.scope, plugin.name)];
162
+ return capabilityConsentStatus(declaredCaps, entry).consented;
163
+ }
164
+
165
+ /** Record consent for a plugin's currently-declared capability set. */
166
+ export function consentPluginCapabilities(
167
+ name,
168
+ { scope = "project", version, capabilities } = {},
169
+ ) {
170
+ 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 };
179
+ }
180
+
181
+ /** Revoke capability consent for a plugin at a scope. */
182
+ 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 };
189
+ }
190
+
191
+ /** All consent entries (for `cc plugin consent --list`). */
192
+ export function listCapabilityConsent() {
193
+ return Object.entries(loadConsentStore()).map(([key, v]) => {
194
+ const idx = key.indexOf(":");
195
+ return {
196
+ scope: key.slice(0, idx),
197
+ name: key.slice(idx + 1),
198
+ version: v?.version || null,
199
+ consentedAt: v?.consentedAt || null,
200
+ capabilities: describeCapabilities(v?.capabilities),
201
+ };
202
+ });
203
+ }
@@ -17,9 +17,36 @@
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
21
 
21
22
  export const _deps = { readFileSync: fs.readFileSync };
22
23
 
24
+ // One-time stderr notice when a plugin's hooks are refused at the COMPONENT
25
+ // level because the plugin opted into the capability model but did not declare
26
+ // the `process` capability its shell hooks need. Distinct from the trust gate:
27
+ // these plugins ARE trusted, but their hook component is denied.
28
+ const _capabilityDenied = new Set();
29
+ function warnHookCapabilityDeniedOnce(entries) {
30
+ if (!entries || entries.length === 0) return;
31
+ if (_capabilityDenied.has("hook-capability")) return;
32
+ _capabilityDenied.add("hook-capability");
33
+ const list = entries.map((e) => `${e.name} (${e.reason})`).join("; ");
34
+ try {
35
+ process.stderr.write(
36
+ `[plugins] refused hook(s) from plugin(s) that declared a permissions ` +
37
+ `block but did not declare the 'process' capability their hooks need: ${list}\n` +
38
+ ` add 'process' to the plugin's permissions block to enable them.\n`,
39
+ );
40
+ } catch {
41
+ /* stderr notice is best-effort */
42
+ }
43
+ }
44
+
45
+ /** Test hook: reset the one-time capability-denied warning guard. */
46
+ export function _resetHookWarnings() {
47
+ _capabilityDenied.clear();
48
+ }
49
+
23
50
  /** Accept either `{ hooks: {Event:[...]} }` (plugin wrap) or `{Event:[...]}`. */
24
51
  function normalizeHookMap(parsed) {
25
52
  if (
@@ -57,10 +84,19 @@ export function collectPluginHooks(opts = {}) {
57
84
  "hooks",
58
85
  );
59
86
  const merged = {};
87
+ const denied = [];
60
88
  for (const p of trusted) {
61
89
  if (!p.manifest || p.manifest.ok !== true) continue;
62
90
  const h = p.manifest.components?.hooks;
63
91
  if (!h) continue;
92
+ // Component-level capability gate: a plugin that declared a permissions
93
+ // block but under-declared the `process` capability its shell hooks need
94
+ // gets its hooks refused here (mirrors the MCP collector's denial).
95
+ const denial = componentCapabilityDenial(p.manifest, ["process"]);
96
+ if (denial) {
97
+ denied.push({ name: p.name, reason: denial.reason });
98
+ continue;
99
+ }
64
100
  let parsed;
65
101
  if (h.absPath) {
66
102
  try {
@@ -89,6 +125,7 @@ export function collectPluginHooks(opts = {}) {
89
125
  merged[event] = (merged[event] || []).concat(entries);
90
126
  }
91
127
  }
128
+ warnHookCapabilityDeniedOnce(denied);
92
129
  return merged;
93
130
  }
94
131
 
@@ -16,9 +16,31 @@ import path from "path";
16
16
  import { discoverPlugins } from "./scopes.js";
17
17
  import { registerLanguageServer } from "../lsp/lsp-server-registry.js";
18
18
  import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
19
+ import { componentCapabilityDenial } from "./capabilities.js";
19
20
 
20
21
  const _loadedRoots = new Set();
21
22
 
23
+ // One-time stderr notice when a plugin's LSP servers are refused at the
24
+ // COMPONENT level because the plugin opted into the capability model but did not
25
+ // declare the `process` capability the servers need. Distinct from the trust
26
+ // gate: these plugins ARE trusted, but their LSP component is denied.
27
+ const _capabilityDenied = new Set();
28
+ function warnLspCapabilityDeniedOnce(entries) {
29
+ if (!entries || entries.length === 0) return;
30
+ if (_capabilityDenied.has("lsp-capability")) return;
31
+ _capabilityDenied.add("lsp-capability");
32
+ const list = entries.map((e) => `${e.name} (${e.reason})`).join("; ");
33
+ try {
34
+ process.stderr.write(
35
+ `[plugins] refused language server(s) from plugin(s) that declared a ` +
36
+ `permissions block but did not declare the 'process' capability: ${list}\n` +
37
+ ` add 'process' to the plugin's permissions block to enable them.\n`,
38
+ );
39
+ } catch {
40
+ /* stderr notice is best-effort */
41
+ }
42
+ }
43
+
22
44
  /**
23
45
  * Register every installed plugin's LSP servers for `cwd` (once per root).
24
46
  * Never throws — a broken plugin manifest must not break code navigation.
@@ -49,8 +71,18 @@ export function ensurePluginLspServers(opts = {}) {
49
71
  "language servers",
50
72
  );
51
73
 
74
+ const denied = [];
52
75
  for (const p of trusted) {
53
76
  const servers = p.manifest?.components?.lsp || [];
77
+ if (servers.length === 0) continue;
78
+ // Component-level capability gate: a plugin that declared a permissions
79
+ // block but did not declare the `process` capability its language servers
80
+ // need gets them refused here (mirrors the MCP collector's denial).
81
+ const denial = componentCapabilityDenial(p.manifest, ["process"]);
82
+ if (denial) {
83
+ denied.push({ name: p.name, reason: denial.reason });
84
+ continue;
85
+ }
54
86
  for (const s of servers) {
55
87
  try {
56
88
  registerLanguageServer({
@@ -66,6 +98,7 @@ export function ensurePluginLspServers(opts = {}) {
66
98
  }
67
99
  }
68
100
  }
101
+ warnLspCapabilityDeniedOnce(denied);
69
102
  return { registered };
70
103
  }
71
104
 
@@ -73,3 +106,8 @@ export function ensurePluginLspServers(opts = {}) {
73
106
  export function _resetPluginLspLoadState() {
74
107
  _loadedRoots.clear();
75
108
  }
109
+
110
+ /** Test hook: reset the one-time capability-denied warning guard. */
111
+ export function _resetLspWarnings() {
112
+ _capabilityDenied.clear();
113
+ }
@@ -17,9 +17,38 @@
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
21
 
21
22
  export const _deps = { readFileSync: fs.readFileSync };
22
23
 
24
+ // One-time stderr notice when a plugin's MCP server is refused at the COMPONENT
25
+ // level because the plugin opted into the capability model but did not declare
26
+ // the `mcp` capability its server needs. Distinct from the trust gate: these
27
+ // plugins ARE trusted, but their MCP component is denied. (Network is enforced
28
+ // separately at connection time by the egress/network policy, so a local stdio
29
+ // server is not denied for lacking a `network` declaration.)
30
+ const _capabilityDenied = new Set();
31
+ function warnMcpCapabilityDeniedOnce(entries) {
32
+ if (!entries || entries.length === 0) return;
33
+ if (_capabilityDenied.has("mcp-capability")) return;
34
+ _capabilityDenied.add("mcp-capability");
35
+ const list = entries.map((e) => `${e.name} (${e.reason})`).join("; ");
36
+ try {
37
+ 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`,
41
+ );
42
+ } catch {
43
+ /* stderr notice is best-effort */
44
+ }
45
+ }
46
+
47
+ /** Test hook: reset the one-time capability-denied warning guard. */
48
+ export function _resetMcpWarnings() {
49
+ _capabilityDenied.clear();
50
+ }
51
+
23
52
  /** Accept `{ mcpServers: {...} }`, `{ servers: {...} }`, or a bare map. */
24
53
  function normalizeServerMap(parsed) {
25
54
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
@@ -52,10 +81,20 @@ export function collectPluginMcpServers(opts = {}) {
52
81
  );
53
82
  const servers = {};
54
83
  const sources = [];
84
+ const denied = [];
55
85
  for (const p of trusted) {
56
86
  if (!p.manifest || p.manifest.ok !== true) continue;
57
87
  const m = p.manifest.components?.mcp;
58
88
  if (!m) continue;
89
+ // Component-level capability gate: a plugin that declared a permissions
90
+ // block but under-declared the `mcp` capability its server needs gets its
91
+ // MCP component refused here (mirrors the hooks/monitors/bin/lsp denial).
92
+ // Legacy plugins (no permissions block) are unrestricted and never denied.
93
+ const denial = componentCapabilityDenial(p.manifest, ["mcp"]);
94
+ if (denial) {
95
+ denied.push({ name: p.name, reason: denial.reason });
96
+ continue;
97
+ }
59
98
  let parsed = null;
60
99
  if (m.absPath) {
61
100
  try {
@@ -85,5 +124,6 @@ export function collectPluginMcpServers(opts = {}) {
85
124
  }
86
125
  if (added > 0 && m.absPath) sources.push(m.absPath);
87
126
  }
127
+ warnMcpCapabilityDeniedOnce(denied);
88
128
  return { servers, sources };
89
129
  }
@@ -21,11 +21,38 @@
21
21
  import fs from "fs";
22
22
  import { discoverPlugins } from "./scopes.js";
23
23
  import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
24
+ import { componentCapabilityDenial } from "./capabilities.js";
24
25
 
25
26
  export const _deps = { readFileSync: fs.readFileSync };
26
27
 
27
28
  const VALID_MODES = new Set(["interval", "longRunning"]);
28
29
 
30
+ // One-time stderr notice when a plugin's monitors are refused at the COMPONENT
31
+ // level because the plugin opted into the capability model but did not declare
32
+ // the `monitor` capability. Distinct from the trust gate: these plugins ARE
33
+ // trusted, but their monitor component is denied.
34
+ const _capabilityDenied = new Set();
35
+ function warnMonitorCapabilityDeniedOnce(entries) {
36
+ if (!entries || entries.length === 0) return;
37
+ if (_capabilityDenied.has("monitor-capability")) return;
38
+ _capabilityDenied.add("monitor-capability");
39
+ const list = entries.map((e) => `${e.name} (${e.reason})`).join("; ");
40
+ try {
41
+ process.stderr.write(
42
+ `[plugins] refused monitor(s) from plugin(s) that declared a permissions ` +
43
+ `block but did not declare the 'monitor' capability: ${list}\n` +
44
+ ` add 'monitor' to the plugin's permissions block to enable them.\n`,
45
+ );
46
+ } catch {
47
+ /* stderr notice is best-effort */
48
+ }
49
+ }
50
+
51
+ /** Test hook: reset the one-time capability-denied warning guard. */
52
+ export function _resetMonitorWarnings() {
53
+ _capabilityDenied.clear();
54
+ }
55
+
29
56
  /** Normalize one raw monitor entry into a validated descriptor, or null. */
30
57
  function normalizeMonitor(plugin, raw) {
31
58
  if (!raw || typeof raw !== "object") return null;
@@ -76,10 +103,19 @@ export function collectPluginMonitors(opts = {}) {
76
103
  "monitors",
77
104
  );
78
105
  const out = [];
106
+ const denied = [];
79
107
  for (const p of trusted) {
80
108
  if (!p.manifest || p.manifest.ok !== true) continue;
81
109
  const m = p.manifest.components?.monitors;
82
110
  if (!m) continue;
111
+ // Component-level capability gate: a plugin that declared a permissions
112
+ // block but did not declare the `monitor` capability gets its monitors
113
+ // refused here (mirrors the MCP collector's denial).
114
+ const denial = componentCapabilityDenial(p.manifest, ["monitor"]);
115
+ if (denial) {
116
+ denied.push({ name: p.name, reason: denial.reason });
117
+ continue;
118
+ }
83
119
  let parsed;
84
120
  if (m.absPath) {
85
121
  try {
@@ -112,5 +148,6 @@ export function collectPluginMonitors(opts = {}) {
112
148
  if (desc) out.push(desc);
113
149
  }
114
150
  }
151
+ warnMonitorCapabilityDeniedOnce(denied);
115
152
  return out;
116
153
  }