chainlesschain 0.162.197 → 0.162.200

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 (233) hide show
  1. package/bin/chainlesschain.js +14 -6
  2. package/package.json +4 -1
  3. package/scripts/command-lifecycle-report.mjs +94 -0
  4. package/src/assets/web-panel/assets/{AIOps-D3Gu1a5d.js → AIOps-DcQ0bWKy.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-DrwAoKaz.js → ActionButton-GecwfFPt.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-C7Q-gd_3.js → Analytics-CD6iSt9v.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-DgBtae-n.js → AppLayout-CDEtuEYf.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-pQ6LcA-W.js → Artifacts-DS3jAjeW.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-DK61tocB.js → Audit-CcSJA048.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-BOKgDFRh.js → BackgroundAgents-DRo9Morb.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-Bo9G4vTP.js → Backup-Vw5MrWft.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-Do_dE9_v.js → BaseInput-DNHIu823.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-COVvZzOL.js → Chat-DvAsO8-b.js} +4 -4
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-9aDuObay.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-Z9MJMolt.js → Checkbox-CKCa7tvn.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-BVzWKVbf.js → Codegen-CIFvZdaT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-C8MnYcbT.js → Col-C79yz7bK.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-DQxqw15l.js → Community-DVvZskLJ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-BAUmrh3y.js → Compact-oaew61hV.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-jPAkQ3zK.js → Compliance-DqA7wmRx.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-BBQyxbh8.js → Cowork-B-qGadxa.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-W5koq8Z5.js → Cron-CUlCl4sS.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BQhnu5hS.js → Crosschain-CNOlr9DU.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-DD7PlI18.js → DID-DJqFCT_3.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-w_ILuwKI.js → Dashboard-CznGpphf.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-qiDUAyAo.js → Dropdown-_cJw218E.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-PDeBcI5H.js → EmailListRenderer-Dr-1BZ9C.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-SuArscRM.js → FamilyGuardDashboard-Dxf4xtSk.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-BCovZaUn.js → Federation-DOiG3i_1.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-DjlFdVFk.js → FormItemContext-BzbMcFB9.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-DoEZ7fcS.js → GenericCardRenderer-VbBrYttP.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-CV0-6GPI.js → Git-CLG8E6ED.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-B_SFN50w.js → Governance-LGJQfk_q.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-heI0Eo6k.js → Inference-DlQKx9lF.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-Cz5INS5p.js → KnowledgeGraph-DU8vEouV.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BSFxiHkg.js → Logs-FZIQOdln.js} +2 -2
  37. package/src/assets/web-panel/assets/MarkdownRenderer-BJNWIrbw.js +1 -0
  38. package/src/assets/web-panel/assets/{Marketplace-0TWTpE7k.js → Marketplace-C_xd-zZI.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-CV4-wBLm.js → McpTools-q-YktphS.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-x5bH5rw5.js → Memory-Cq9OhDin.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-DaBmUu9G.js → MobileBridge-B03_R98T.js} +1 -1
  42. package/src/assets/web-panel/assets/MobileProjects-DZrk-qmF.js +1 -0
  43. package/src/assets/web-panel/assets/{Mtc-B_zKEBif.js → Mtc-BidCKW28.js} +5 -5
  44. package/src/assets/web-panel/assets/{MtcAudit-BCfKD2yg.js → MtcAudit-r-iaMVM7.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-DgYfj6c_.js → Multisig-CgTUFvff.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-Dz09lFUm.js → NLProgramming-B7NvzfAV.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-Cuj6ugBE.js → Notes-80CWNqJI.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-DK2WXjhg.js → NotificationSettings-0H6xdiY3.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-D1IAOQoo.js → OrderTableRenderer-CKP4AFWy.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-9fqnPvTw.js → Organization-B8sxCV_h.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-n5xHkhow.js → Overflow-CW9JBBhQ.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-B5AUWwH-.js → P2P-bdSQV7oZ.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-CoUDV6dy.js → PdhVaultBrowser-DQad42nW.js} +4 -4
  54. package/src/assets/web-panel/assets/{Permissions-DPRmQf_H.js → Permissions-CueexxTR.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-B37bsbEN.js → PersonalDataHub-fqxflhR8.js} +6 -6
  56. package/src/assets/web-panel/assets/{Pipeline-CCAuPqu9.js → Pipeline-KFwwnIMC.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-B81msZdv.js → Privacy-ClnD9buY.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-B-Py2NbF.js → ProjectInit-CZEz6sm3.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-CYsneiRB.js → ProjectSettings-B6NzpmOI.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-Y975Mh6R.js → Projects-DYyuwPCs.js} +1 -1
  61. package/src/assets/web-panel/assets/Providers-C3tz_jdh.js +1 -0
  62. package/src/assets/web-panel/assets/{QrScannerModal-Bj_6gJ0l.js → QrScannerModal-BzuOBbqB.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-BgpBCMPE.js → QuickAsk-CmNO9qSj.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-DhUW3YUA.js → Recommend-VJNj3_NF.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-DdeyfbkA.js → RemoteSession-CrbdbSBF.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-fxW421xd.js → Reputation-DTyMd45j.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-De73w1og.js → Row-DHrQ-Icz.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-3US_Q_lj.js → RssFeed-DqWxY9c5.js} +2 -2
  69. package/src/assets/web-panel/assets/{Search-DbZ_DU74.js → Search-ctF1Ks3J.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-DoIU1vMF.js → Security-BrQz8uo5.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-DB_RGqpc.js → Services-D4scVFar.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-DnzLaayC.js → Skeleton-oUScaOjv.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-VzVXHENF.js → Skills-CL-y0eLt.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-DMfvjCAI.js → Sla-DQExhwXj.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-DQQOYWbF.js → SpeechSettings-iNp_I7kN.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-Bmx3FG1P.js → SyncSettings-CTDlMo0A.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-DOQyfb-2.js → Tasks-4kb8l3sc.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-D6hTRT7c.js → Templates-BXcL9oXW.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-CTPQZAzs.js → Tenant-DLE8eS0D.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-B7vII3oH.js → Terminal-DGpUZPPK.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-BkiFJeXV.js → TimelineRenderer-C1pWXGPC.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-BPPdQMNo.js → Tokens-BYfbfqhZ.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BxG8JAEI.js → Trigger-K9YfLOCw.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-DHD7nMpM.js → Trust-Ctkq77Hy.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-B4BJpU47.js → UkeySign-C6_CSe9J.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-CRiGpFx2.js → VideoEditing-Be3rpOWs.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-BSDSNiem.js → Wallet-D0UIplxk.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-DLTUh5Zv.js → WebAuthn-CF9bM9I3.js} +5 -5
  89. package/src/assets/web-panel/assets/{WorkflowEditor-tW2MwjhD.js → WorkflowEditor-D0H1XVPY.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-SgsLQ3FC.js → chat-Cgzzbe0o.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-BHjjLIKO.js → colors-BwNxEVS0.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-VceErdKq.js → compact-item-D60Z4Hhs.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-BZ332MYl.js → createContext-MFk58XB0.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BJiw369i.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-CBTjJOjn.js → hasIn-CRj2SvGO.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DaCCy6lq.js → index--s45_1cb.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CMVvxIS8.js → index--tquQ2ZG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BhXZEUAc.js → index-1T1rhokn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BQSmZGgs.js → index-B-M82_rx.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BeiqkxJG.js → index-BE_el1Zs.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-29wVzi-k.js → index-BhRkR7IO.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-iYQNU9za.js → index-Bo0y96-K.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BRWhLbWF.js → index-CJgu5JUK.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BPXFLYdW.js → index-CLWwdebO.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Cr4rjlW5.js → index-CNAK5QG_.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CaIS_TNX.js → index-CQjJjPRD.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-1n7xjKDU.js → index-CTDbAfae.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BETEjuNr.js → index-CVI_9sRK.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-By5jBtkV.js → index-CY14oMQK.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DgPvuIn2.js → index-CbiTJkZf.js} +1 -1
  111. package/src/assets/web-panel/assets/index-CiMouosn.js +1 -0
  112. package/src/assets/web-panel/assets/{index-CawpdGTW.js → index-CqWpxMqx.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-_PHrIO4O.js → index-CvB8lYUR.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BOtvPdHt.js → index-D51XwtCY.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CrUgDQkf.js → index-D6HvK_1O.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BY1wexYG.js → index-D9jVIwLJ.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C_B9rFQE.js → index-DA4tE98u.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-B5KAGxNo.js → index-DBwOtXw0.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BC1xuQgX.js → index-DKdNzTIb.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-B2u26AS3.js → index-DRySdmvN.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DePOeMDM.js → index-DWzyTev7.js} +1 -1
  122. package/src/assets/web-panel/assets/index-DXcKlwy7.js +1 -0
  123. package/src/assets/web-panel/assets/{index-BTlc-Bve.js → index-DYjs7jAP.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CLc19alH.js → index-DbROzMBp.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-fU7wNHDB.js → index-DhFtk5PR.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D_SePDWj.js → index-Di2AbMLg.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-vIXOpcjD.js → index-DvmocQsM.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BcUenmCX.js → index-Dw-o0zEF.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DUkVN794.js → index-DysPoP38.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CtjLcCNU.js → index-LX9pJxm_.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B40dQn0G.js → index-OI_fXWx0.js} +3 -3
  132. package/src/assets/web-panel/assets/{index-FJ3zC9LK.js → index-eDTOX8Xj.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DdefYBSw.js → index-gWKDsTzF.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-CpumS98o.js → index-q_GDUVAA.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-D1oZNz0D.js → initDefaultProps-C61kZqEd.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-C8k6doMO.js → motion-DZnT5U1L.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-DJ4qSbYt.js → move-L8wzwbel.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-CdpJD85j.js → mtc-parser-DLdYSejc.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-B0V4slKA.js → omit-D53cbS59.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-Di5NiW1F.js → pickAttrs-Bbc5HPtu.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-B5OMHgEM.js → placementArrow-BkvmO860.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-C6U9IWql.js → responsiveObserve-u2y1ryS1.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-BMyTH3rN.js → slide-BBqj7svU.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-C7d6BG7Z.js → statusUtils-GN0mqu2d.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-CR50QnQy.js → styleChecker-BvwPKoBT.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-DBEeoMb8.js → useFlexGapSupport-CCt8tnfY.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-3r099cjv.js → useFs-DXAxl2SV.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-C5IYNdTK.js → usePersonalDataHub-DihlR2dj.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-DmEOLRHf.js → vnode-D7BOX2gi.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-DnpUiVCB.js → zoom-CVbO-nL7.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +4 -4
  153. package/src/commands/agent.js +3 -3
  154. package/src/commands/background-session.js +145 -0
  155. package/src/commands/mcp.js +244 -7
  156. package/src/commands/serve.js +1 -1
  157. package/src/commands/session-show.js +129 -0
  158. package/src/commands/session.js +97 -145
  159. package/src/commands/skill.js +87 -50
  160. package/src/data/changelog.json +40 -1
  161. package/src/gateways/ws/session-protocol.js +14 -4
  162. package/src/gateways/ws/ws-agent-handler.js +39 -1
  163. package/src/harness/jsonl-session-store.js +1547 -127
  164. package/src/harness/mcp-client.js +2690 -409
  165. package/src/harness/session-index.js +80 -80
  166. package/src/harness/session-list-index.js +210 -7
  167. package/src/lazy-dispatch.js +76 -8
  168. package/src/lib/abort-utils.js +68 -0
  169. package/src/lib/agent-core.js +1 -59
  170. package/src/lib/background-interaction-journal.js +2 -2
  171. package/src/lib/chat-core.js +24 -6
  172. package/src/lib/command-lifecycle-report.js +516 -0
  173. package/src/lib/cowork-mcp-tools.js +47 -2
  174. package/src/lib/cowork-task-runner.js +11 -3
  175. package/src/lib/doctor-checkup.js +9 -7
  176. package/src/lib/learning/skill-improver.js +21 -2
  177. package/src/lib/mcp-call-ledger-store.js +116 -3
  178. package/src/lib/mcp-call-ledger.js +59 -14
  179. package/src/lib/mcp-client.js +1 -0
  180. package/src/lib/mcp-stdio-environment.js +48 -0
  181. package/src/lib/mcp-stdio-executable-identity.js +1133 -0
  182. package/src/lib/mcp-stdio-execution-authority.js +363 -0
  183. package/src/lib/mcp-stdio-package-materialization.js +1424 -0
  184. package/src/lib/mcp-tool-metadata.js +436 -0
  185. package/src/lib/mcp-tool-result.js +367 -0
  186. package/src/lib/observability/index.js +71 -0
  187. package/src/lib/packer/native-update-state.js +979 -1
  188. package/src/lib/packer/pack-update-applier.js +1050 -125
  189. package/src/lib/packer/pkg-config-generator.js +1 -0
  190. package/src/lib/paths.js +53 -0
  191. package/src/lib/pr-link-ledger.js +8 -27
  192. package/src/lib/pr-link-store.js +26 -0
  193. package/src/lib/process-execution-broker/index.js +192 -35
  194. package/src/lib/process-execution-broker/platform-sandbox.js +291 -24
  195. package/src/lib/process-tree-termination.js +253 -0
  196. package/src/lib/recent-session.js +17 -17
  197. package/src/lib/secure-file-identity.js +47 -0
  198. package/src/lib/secure-fs.js +21 -2
  199. package/src/lib/session-anti-rollback-anchor.js +766 -0
  200. package/src/lib/session-host-lease.js +492 -0
  201. package/src/lib/session-host-snapshot.js +9 -5
  202. package/src/lib/session-persistence-failure.js +109 -0
  203. package/src/lib/session-picker.js +26 -3
  204. package/src/lib/session-projection.js +5 -1
  205. package/src/lib/session-search.js +25 -11
  206. package/src/lib/session-tail.js +31 -1
  207. package/src/lib/side-effect-ledger-store.js +2 -2
  208. package/src/lib/skill-budget.js +222 -0
  209. package/src/lib/skill-execution-authority.js +385 -0
  210. package/src/lib/skill-execution-identity.js +39 -2
  211. package/src/lib/skill-loader.js +322 -86
  212. package/src/lib/skill-mcp.js +43 -1
  213. package/src/lib/turn-binding-store.js +2 -2
  214. package/src/repl/agent-repl.js +483 -77
  215. package/src/repl/bg-dashboard.js +81 -28
  216. package/src/repl/chat-repl.js +77 -12
  217. package/src/runtime/agent-core.js +428 -113
  218. package/src/runtime/agent-runtime.js +26 -3
  219. package/src/runtime/cleanup-deadline.js +143 -0
  220. package/src/runtime/headless-runner.js +494 -149
  221. package/src/runtime/headless-stream.js +504 -75
  222. package/src/runtime/mcp-config.js +157 -10
  223. package/src/runtime/mcp-tool-search.js +0 -0
  224. package/src/runtime/output-backpressure.js +419 -0
  225. package/src/runtime/pipe-safety.js +56 -21
  226. package/src/assets/web-panel/assets/ChatBubbleRenderer-BUUyJ5QO.js +0 -1
  227. package/src/assets/web-panel/assets/MarkdownRenderer-1mNozZ8v.js +0 -1
  228. package/src/assets/web-panel/assets/MobileProjects-DqnoFPgR.js +0 -1
  229. package/src/assets/web-panel/assets/Providers-uO-10aP1.js +0 -1
  230. package/src/assets/web-panel/assets/devWarning-DosMaL-k.js +0 -1
  231. package/src/assets/web-panel/assets/index-0O5ePnUq.js +0 -1
  232. package/src/assets/web-panel/assets/index-ChAzPigF.js +0 -1
  233. package/src/lib/skill-process-broker.js +0 -85
@@ -32,6 +32,23 @@ import { normalizeSkillPaths } from "./skill-path-scope.js";
32
32
  import { discoverPluginSkillLayers } from "./plugin-runtime/skills.js";
33
33
  import settingsLoader from "./settings-loader.cjs";
34
34
  import contextSourceLedger from "./context-source-ledger.js";
35
+ import {
36
+ createAbortError,
37
+ isAbortError,
38
+ raceWithAbort,
39
+ throwIfAborted,
40
+ } from "./abort-utils.js";
41
+ import {
42
+ admitSkillPrompt,
43
+ assertSkillFileSize,
44
+ createSkillDiscoveryBudget,
45
+ debitSkillDiscoveryBudget,
46
+ debitSkillDiscoveryEntry,
47
+ debitSkillPromptBudget,
48
+ HOST_SKILL_LIMITS,
49
+ isSkillBudgetError,
50
+ resolveSkillLimits,
51
+ } from "./skill-budget.js";
35
52
  import {
36
53
  assertControlledSkillExecution,
37
54
  captureSkillExecutionSnapshot,
@@ -43,9 +60,20 @@ import {
43
60
  skillReauthorizationError,
44
61
  skillTrustRequiredError,
45
62
  } from "./skill-execution-identity.js";
63
+ import { getSkillExecutionAuthority } from "./skill-execution-authority.js";
46
64
 
47
65
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
48
66
 
67
+ /**
68
+ * Revoke every Skill execution authorization sharing this CLI state home.
69
+ * The durable generation is advanced before success is returned. Existing
70
+ * local leases are aborted synchronously; other local hosts observe it through
71
+ * their active-lease poller and at every authority-sensitive transition.
72
+ */
73
+ export function revokeSkillExecutionAuthorizations(options = {}) {
74
+ return getSkillExecutionAuthority().revoke(options);
75
+ }
76
+
49
77
  /**
50
78
  * Whether the bundled (built-in) skill layer should be hidden — Claude-Code
51
79
  * 2.1.169 `disableBundledSkills` parity. Precedence: explicit option >
@@ -108,7 +136,7 @@ export const LAYER_NAMES = [
108
136
 
109
137
  /** Max directory depth to descend when scanning a skill layer for nested
110
138
  * `<group>/<skill>/SKILL.md` layouts (infinite-recursion / deep-tree guard). */
111
- export const MAX_SKILL_NEST_DEPTH = 5;
139
+ export const MAX_SKILL_NEST_DEPTH = HOST_SKILL_LIMITS.maxSkillNestDepth;
112
140
 
113
141
  // Frontmatter keys (camelCased) that are semantically strings and are consumed
114
142
  // with string methods; they must never be number/boolean-coerced. `version` is
@@ -235,7 +263,12 @@ export function parseSkillMd(content) {
235
263
  * body is deliberately not retained during discovery; it is materialized by
236
264
  * CLISkillLoader.materializeSkill() only when a persona or run_skill needs it.
237
265
  */
238
- function readSkillFrontmatter(skillMd, maxBytes = 256 * 1024) {
266
+ function readSkillFrontmatter(skillMd, maxBytes = 256 * 1024, limits = {}) {
267
+ const before = fs.lstatSync(skillMd);
268
+ if (before.isSymbolicLink() || !before.isFile()) {
269
+ throw new TypeError("SKILL.md must be a regular, non-symlink file");
270
+ }
271
+ assertSkillFileSize("SKILL.md", before.size, limits);
239
272
  const fd = fs.openSync(skillMd, "r");
240
273
  const chunks = [];
241
274
  let total = 0;
@@ -341,6 +374,8 @@ export class CLISkillLoader {
341
374
  constructor({
342
375
  contextLedger = contextSourceLedger,
343
376
  reauthorizeSkill = null,
377
+ executionAuthority = null,
378
+ limits = {},
344
379
  // Only package-owned, immutable-at-install sources are trusted by
345
380
  // default. The historical "managed" layer is actually the user-writable
346
381
  // <userData>/skills directory (and `skill add --global` writes there), so
@@ -354,8 +389,22 @@ export class CLISkillLoader {
354
389
  this._bodyCache = new Map();
355
390
  this._contextLedger = contextLedger;
356
391
  this._reauthorizeSkill = reauthorizeSkill;
392
+ this._executionAuthority =
393
+ executionAuthority || getSkillExecutionAuthority();
394
+ if (
395
+ typeof this._executionAuthority.readGeneration !== "function" ||
396
+ typeof this._executionAuthority.acquireLease !== "function" ||
397
+ typeof this._executionAuthority.assertGeneration !== "function" ||
398
+ typeof this._executionAuthority.revoke !== "function"
399
+ ) {
400
+ throw new TypeError(
401
+ "executionAuthority must implement the Skill authority API",
402
+ );
403
+ }
404
+ this._limits = resolveSkillLimits(limits);
357
405
  this._trustedExecutionSources = new Set(trustedExecutionSources);
358
406
  this._authorizedExecutionDigests = new Map();
407
+ this._executionAuthorityGeneration = null;
359
408
  this._cacheStats = {
360
409
  descriptorScans: 0,
361
410
  descriptorCacheHits: 0,
@@ -366,6 +415,100 @@ export class CLISkillLoader {
366
415
  };
367
416
  }
368
417
 
418
+ getLimits() {
419
+ return this._limits;
420
+ }
421
+
422
+ _syncExecutionAuthorityGeneration() {
423
+ const authorityGeneration = this._executionAuthority.readGeneration();
424
+ if (this._executionAuthorityGeneration !== authorityGeneration) {
425
+ this._authorizedExecutionDigests.clear();
426
+ this._executionAuthorityGeneration = authorityGeneration;
427
+ }
428
+ return this._executionAuthorityGeneration;
429
+ }
430
+
431
+ _assertExecutionAuthorityGeneration(expectedGeneration) {
432
+ this._executionAuthority.assertGeneration(expectedGeneration);
433
+ this._syncExecutionAuthorityGeneration();
434
+ }
435
+
436
+ /**
437
+ * Acquire a durable-generation lease for one authorization or controlled
438
+ * execution. Same-home revocation aborts the returned signal and fences any
439
+ * later transition under an old digest decision.
440
+ */
441
+ acquireSkillExecution(skill, context = {}) {
442
+ if (!skill || typeof skill !== "object") {
443
+ throw new TypeError("A discovered skill descriptor is required");
444
+ }
445
+ throwIfAborted(context.signal, "Skill execution was interrupted");
446
+ this._syncExecutionAuthorityGeneration();
447
+ const authorityLease = this._executionAuthority.acquireLease({
448
+ skillId: String(skill.id || skill.dirName || "unknown"),
449
+ });
450
+ const generation = authorityLease.generation;
451
+ let released = false;
452
+ let parentAbortListener = null;
453
+ const release = () => {
454
+ if (released) return;
455
+ released = true;
456
+ authorityLease.release();
457
+ if (parentAbortListener) {
458
+ try {
459
+ context.signal?.removeEventListener?.("abort", parentAbortListener);
460
+ } catch {
461
+ // Lease retirement must remain idempotent even for a malformed host
462
+ // signal implementation.
463
+ }
464
+ }
465
+ };
466
+ const assertActive = () => {
467
+ throwIfAborted(authorityLease.signal, "Skill execution was interrupted");
468
+ this._assertExecutionAuthorityGeneration(generation);
469
+ };
470
+
471
+ if (context.signal?.addEventListener) {
472
+ parentAbortListener = () => {
473
+ authorityLease.abort(
474
+ context.signal.reason ||
475
+ createAbortError("Skill execution was interrupted"),
476
+ );
477
+ };
478
+ try {
479
+ context.signal.addEventListener("abort", parentAbortListener, {
480
+ once: true,
481
+ });
482
+ } catch (error) {
483
+ release();
484
+ throw error;
485
+ }
486
+ // Close the same pre-check/listener-registration race handled by
487
+ // raceWithAbort for Promise consumers.
488
+ if (context.signal.aborted) parentAbortListener();
489
+ }
490
+ try {
491
+ assertActive();
492
+ } catch (error) {
493
+ release();
494
+ throw error;
495
+ }
496
+ return Object.freeze({
497
+ generation: String(generation),
498
+ signal: authorityLease.signal,
499
+ assertActive,
500
+ release,
501
+ });
502
+ }
503
+
504
+ revokeExecutionAuthorizations(options = {}) {
505
+ this._syncExecutionAuthorityGeneration();
506
+ const clearedLocalAuthorizations = this._authorizedExecutionDigests.size;
507
+ const result = this._executionAuthority.revoke(options);
508
+ this._syncExecutionAuthorityGeneration();
509
+ return Object.freeze({ ...result, clearedLocalAuthorizations });
510
+ }
511
+
369
512
  _skillReauthorizationRequest(
370
513
  skill,
371
514
  snapshot,
@@ -393,9 +536,11 @@ export class CLISkillLoader {
393
536
  discoveredDigest: skill.executionIdentity?.contentDigest || null,
394
537
  currentDigest: snapshot.contentDigest,
395
538
  componentDigests: Object.freeze({ ...snapshot.componentDigests }),
539
+ executionAuthorityGeneration: String(this._executionAuthorityGeneration),
396
540
  loadedBecause: context.loadedBecause || "explicit",
397
541
  sessionId: context.sessionId || null,
398
542
  turnId: context.turnId || null,
543
+ signal: context.signal || null,
399
544
  });
400
545
  }
401
546
 
@@ -421,10 +566,16 @@ export class CLISkillLoader {
421
566
  * materializeSkillForExecution after the await, closing the prompt-time race.
422
567
  */
423
568
  async _authorizeSkillExecutionDigest(skill, context = {}) {
569
+ throwIfAborted(
570
+ context.signal,
571
+ "Skill execution authorization was interrupted",
572
+ );
573
+ const authorizationGeneration = this._syncExecutionAuthorityGeneration();
424
574
  const snapshot = captureSkillExecutionSnapshot({
425
575
  skillDir: skill.skillDir,
426
576
  skillId: skill.id,
427
577
  source: skill.source,
578
+ limits: this._limits,
428
579
  });
429
580
  const discovered = skill.executionIdentity;
430
581
  if (
@@ -448,6 +599,7 @@ export class CLISkillLoader {
448
599
  isSkillExecutionContext(context) &&
449
600
  skill.hasHandler === true &&
450
601
  !this._trustedExecutionSources.has(String(skill.source || ""));
602
+ this._assertExecutionAuthorityGeneration(authorizationGeneration);
451
603
  const previouslyAuthorizedDigest = this._authorizedExecutionDigests.get(
452
604
  snapshot.identityDigest,
453
605
  );
@@ -466,17 +618,28 @@ export class CLISkillLoader {
466
618
 
467
619
  let decision;
468
620
  try {
469
- decision = await this._reauthorizeSkill(
470
- this._skillReauthorizationRequest(skill, snapshot, context, {
471
- firstUseAuthorization,
472
- contentChanged,
473
- previouslyAuthorizedDigest,
474
- }),
621
+ decision = await raceWithAbort(
622
+ this._reauthorizeSkill(
623
+ this._skillReauthorizationRequest(skill, snapshot, context, {
624
+ firstUseAuthorization,
625
+ contentChanged,
626
+ previouslyAuthorizedDigest,
627
+ }),
628
+ ),
629
+ context.signal,
630
+ "Skill execution authorization was interrupted",
475
631
  );
476
632
  } catch (cause) {
633
+ if (context.signal?.aborted || isAbortError(cause)) throw cause;
477
634
  throw skillReauthorizationError(skill, cause);
478
635
  }
479
- if (decision !== true && decision?.authorized !== true) {
636
+ const authorized = decision === true || decision?.authorized === true;
637
+ throwIfAborted(
638
+ context.signal,
639
+ "Skill execution authorization was interrupted",
640
+ );
641
+ this._assertExecutionAuthorityGeneration(authorizationGeneration);
642
+ if (!authorized) {
480
643
  this._throwSkillAuthorizationDenial(skill, snapshot, {
481
644
  firstUseAuthorization,
482
645
  contentChanged,
@@ -490,11 +653,27 @@ export class CLISkillLoader {
490
653
  }
491
654
 
492
655
  async materializeSkillForExecution(skill, context = {}) {
493
- const executionContext = { ...context, forExecution: true };
494
- await this._authorizeSkillExecutionDigest(skill, executionContext);
495
- // Re-capture and re-check after a potentially human-blocking async prompt.
496
- // materializeSkill recognizes only the exact digest authorized above.
497
- return this.materializeSkill(skill, executionContext);
656
+ const executionLease = this.acquireSkillExecution(skill, {
657
+ signal: context.signal,
658
+ });
659
+ const executionContext = {
660
+ ...context,
661
+ forExecution: true,
662
+ signal: executionLease.signal,
663
+ };
664
+ try {
665
+ executionLease.assertActive();
666
+ await this._authorizeSkillExecutionDigest(skill, executionContext);
667
+ executionLease.assertActive();
668
+ // Re-capture and re-check after a potentially human-blocking async
669
+ // prompt. materializeSkill recognizes only the exact digest authorized
670
+ // above.
671
+ const materialized = this.materializeSkill(skill, executionContext);
672
+ executionLease.assertActive();
673
+ return materialized;
674
+ } finally {
675
+ executionLease.release();
676
+ }
498
677
  }
499
678
 
500
679
  /**
@@ -619,9 +798,10 @@ export class CLISkillLoader {
619
798
  * @param {string} layer - Layer name for source tracking
620
799
  * @returns {object[]} Array of skill metadata
621
800
  */
622
- _loadFromDir(dir, layer) {
801
+ _loadFromDir(dir, layer, discoveryBudget = null) {
623
802
  const skills = [];
624
- this._collectSkills(dir, layer, skills, 0);
803
+ const budget = discoveryBudget || createSkillDiscoveryBudget(this._limits);
804
+ this._collectSkills(dir, layer, skills, 0, budget);
625
805
  return skills;
626
806
  }
627
807
 
@@ -632,79 +812,102 @@ export class CLISkillLoader {
632
812
  * descended into, so nested layouts like `.claude/skills/<group>/<skill>/`
633
813
  * load too (Claude-Code 2.1.178 nested-skills parity). Depth-capped.
634
814
  */
635
- _collectSkills(dir, layer, out, depth) {
815
+ _collectSkills(dir, layer, out, depth, discoveryBudget) {
636
816
  if (!dir || !fs.existsSync(dir)) return;
637
- let entries;
817
+ let directory;
638
818
  try {
639
- entries = fs.readdirSync(dir, { withFileTypes: true });
819
+ directory = fs.opendirSync(dir);
640
820
  } catch {
641
821
  return; // Directory unreadable
642
822
  }
643
- for (const entry of entries) {
644
- if (!entry.isDirectory()) continue;
645
- const skillDir = path.join(dir, entry.name);
646
- const skillMd = path.join(skillDir, "SKILL.md");
647
- if (!fs.existsSync(skillMd)) {
648
- // Grouping folder → descend (bounded) to find nested skills.
649
- if (depth < MAX_SKILL_NEST_DEPTH) {
650
- this._collectSkills(skillDir, layer, out, depth + 1);
823
+ try {
824
+ let entry;
825
+ while ((entry = directory.readSync()) !== null) {
826
+ debitSkillDiscoveryEntry(discoveryBudget);
827
+ if (!entry.isDirectory()) continue;
828
+ const skillDir = path.join(dir, entry.name);
829
+ const skillMd = path.join(skillDir, "SKILL.md");
830
+ if (!fs.existsSync(skillMd)) {
831
+ // Grouping folder → descend (bounded) to find nested skills.
832
+ if (depth < this._limits.maxSkillNestDepth) {
833
+ this._collectSkills(
834
+ skillDir,
835
+ layer,
836
+ out,
837
+ depth + 1,
838
+ discoveryBudget,
839
+ );
840
+ }
841
+ continue;
651
842
  }
652
- continue;
653
- }
654
- try {
655
- const data = readSkillFrontmatter(skillMd);
656
- const stat = fs.statSync(skillMd);
657
- const id = data.name || entry.name;
658
- const isolation = data.isolation === true;
659
- const snapshot = captureSkillExecutionSnapshot({
660
- skillDir,
661
- skillId: id,
662
- source: layer,
663
- });
664
- const hasHandler = snapshot.handlerPresent;
665
-
666
- out.push({
667
- id,
668
- displayName: data.displayName || entry.name,
669
- description: data.description || "",
670
- version: data.version || "1.0.0",
671
- category: data.category || "uncategorized",
672
- activation: data.activation || "manual",
673
- tags: data.tags || [],
674
- userInvocable: data.userInvocable !== false,
675
- handler: data.handler || null,
676
- capabilities: data.capabilities || [],
677
- os: data.os || [],
678
- // CLI pack extended fields
679
- executionMode: data.executionMode || null,
680
- cliDomain: data.cliDomain || null,
681
- cliVersionHash: data.cliVersionHash || null,
682
- // Optional per-directory path-scope: when set, the skill is only
683
- // surfaced/runnable under a matching subtree (large-monorepo lazy
684
- // context). null (the default) = applies everywhere.
685
- paths: normalizeSkillPaths(data),
686
- dirName: entry.name,
687
- hasHandler,
688
- isolation,
689
- executionIdentity: executionIdentityMetadata(snapshot),
690
- executionAuthority: describeSkillExecutionAuthority({
843
+ try {
844
+ const data = readSkillFrontmatter(
845
+ skillMd,
846
+ Math.min(256 * 1024, this._limits.maxSkillFileBytes),
847
+ this._limits,
848
+ );
849
+ const stat = fs.statSync(skillMd);
850
+ const id = data.name || entry.name;
851
+ const isolation = data.isolation === true;
852
+ const snapshot = captureSkillExecutionSnapshot({
853
+ skillDir,
854
+ skillId: id,
855
+ source: layer,
856
+ limits: this._limits,
857
+ });
858
+ debitSkillDiscoveryBudget(
859
+ discoveryBudget,
860
+ snapshot.componentFileCount,
861
+ snapshot.componentBytes,
862
+ );
863
+ const hasHandler = snapshot.handlerPresent;
864
+
865
+ out.push({
866
+ id,
867
+ displayName: data.displayName || entry.name,
868
+ description: data.description || "",
869
+ version: data.version || "1.0.0",
870
+ category: data.category || "uncategorized",
871
+ activation: data.activation || "manual",
872
+ tags: data.tags || [],
873
+ userInvocable: data.userInvocable !== false,
874
+ handler: data.handler || null,
875
+ capabilities: data.capabilities || [],
876
+ os: data.os || [],
877
+ // CLI pack extended fields
878
+ executionMode: data.executionMode || null,
879
+ cliDomain: data.cliDomain || null,
880
+ cliVersionHash: data.cliVersionHash || null,
881
+ // Optional per-directory path-scope: when set, the skill is only
882
+ // surfaced/runnable under a matching subtree (large-monorepo lazy
883
+ // context). null (the default) = applies everywhere.
884
+ paths: normalizeSkillPaths(data),
885
+ dirName: entry.name,
691
886
  hasHandler,
692
887
  isolation,
693
- }),
694
- // Two-tier loading: discovery retains only the descriptor. The body
695
- // and embedded MCP declarations are populated on first use.
696
- body: null,
697
- bodyLoaded: false,
698
- mcpServers: [],
699
- source: layer,
700
- skillDir: snapshot.rootRealPath,
701
- skillMdPath: path.join(snapshot.rootRealPath, "SKILL.md"),
702
- skillFileBytes: snapshot.skillFileBytes,
703
- skillFileMtimeMs: stat.mtimeMs,
704
- });
705
- } catch {
706
- // Skip malformed skill files
888
+ executionIdentity: executionIdentityMetadata(snapshot),
889
+ executionAuthority: describeSkillExecutionAuthority({
890
+ hasHandler,
891
+ isolation,
892
+ }),
893
+ // Two-tier loading: discovery retains only the descriptor. The body
894
+ // and embedded MCP declarations are populated on first use.
895
+ body: null,
896
+ bodyLoaded: false,
897
+ mcpServers: [],
898
+ source: layer,
899
+ skillDir: snapshot.rootRealPath,
900
+ skillMdPath: path.join(snapshot.rootRealPath, "SKILL.md"),
901
+ skillFileBytes: snapshot.skillFileBytes,
902
+ skillFileMtimeMs: stat.mtimeMs,
903
+ });
904
+ } catch (error) {
905
+ if (isSkillBudgetError(error)) throw error;
906
+ // Skip malformed skill files
907
+ }
707
908
  }
909
+ } finally {
910
+ directory.closeSync();
708
911
  }
709
912
  }
710
913
 
@@ -715,6 +918,11 @@ export class CLISkillLoader {
715
918
  * size/mtime metadata, and provenance records never include source content.
716
919
  */
717
920
  materializeSkill(skill, context = {}) {
921
+ throwIfAborted(
922
+ context.signal,
923
+ "Skill execution materialization was interrupted",
924
+ );
925
+ const authorizationGeneration = this._syncExecutionAuthorityGeneration();
718
926
  if (!skill || !skill.skillMdPath) {
719
927
  throw new TypeError("A discovered skill descriptor is required");
720
928
  }
@@ -723,6 +931,7 @@ export class CLISkillLoader {
723
931
  skillDir: skill.skillDir,
724
932
  skillId: skill.id,
725
933
  source: skill.source,
934
+ limits: this._limits,
726
935
  });
727
936
  const discovered = skill.executionIdentity;
728
937
  if (
@@ -745,6 +954,7 @@ export class CLISkillLoader {
745
954
  isSkillExecutionContext(context) &&
746
955
  skill.hasHandler === true &&
747
956
  !this._trustedExecutionSources.has(String(skill.source || ""));
957
+ this._assertExecutionAuthorityGeneration(authorizationGeneration);
748
958
  const previouslyAuthorizedDigest = this._authorizedExecutionDigests.get(
749
959
  snapshot.identityDigest,
750
960
  );
@@ -772,6 +982,7 @@ export class CLISkillLoader {
772
982
  }),
773
983
  );
774
984
  } catch (cause) {
985
+ if (context.signal?.aborted || isAbortError(cause)) throw cause;
775
986
  throw skillReauthorizationError(skill, cause);
776
987
  }
777
988
  if (decision && typeof decision.then === "function") {
@@ -794,6 +1005,11 @@ export class CLISkillLoader {
794
1005
  snapshot.contentDigest,
795
1006
  );
796
1007
  }
1008
+ throwIfAborted(
1009
+ context.signal,
1010
+ "Skill execution authorization was interrupted",
1011
+ );
1012
+ this._assertExecutionAuthorityGeneration(authorizationGeneration);
797
1013
  if (executionRequiresTrust) {
798
1014
  this._authorizedExecutionDigests.set(
799
1015
  snapshot.identityDigest,
@@ -805,6 +1021,7 @@ export class CLISkillLoader {
805
1021
  skill.hasHandler === true &&
806
1022
  this._trustedExecutionSources.has(String(skill.source || ""))
807
1023
  ) {
1024
+ this._assertExecutionAuthorityGeneration(authorizationGeneration);
808
1025
  this._authorizedExecutionDigests.set(
809
1026
  snapshot.identityDigest,
810
1027
  snapshot.contentDigest,
@@ -816,6 +1033,7 @@ export class CLISkillLoader {
816
1033
  if (String(runtimeId) !== String(skill.id)) {
817
1034
  throw skillIdentityChangedError(skill);
818
1035
  }
1036
+ const promptMeasurement = admitSkillPrompt(parsed.body, this._limits);
819
1037
  const runtimeIsolation = parsed.data.isolation === true;
820
1038
  skill.handler = parsed.data.handler || null;
821
1039
  skill.capabilities = parsed.data.capabilities || [];
@@ -844,6 +1062,8 @@ export class CLISkillLoader {
844
1062
  mcpServers: parseSkillMcpServers(body),
845
1063
  rawChars: body.length,
846
1064
  estimatedTokens: estimateSkillTokens(body),
1065
+ promptBytes: promptMeasurement.bytes,
1066
+ promptTokenUpperBound: promptMeasurement.tokenUpperBound,
847
1067
  };
848
1068
  this._bodyCache.set(skill.skillMdPath, cached);
849
1069
  this._cacheStats.bodyCacheMisses += 1;
@@ -1040,6 +1260,7 @@ export class CLISkillLoader {
1040
1260
  }
1041
1261
  const layers = this.getLayerPaths();
1042
1262
  const skillMap = new Map();
1263
+ const discoveryBudget = createSkillDiscoveryBudget(this._limits);
1043
1264
  // Claude-Code `disableBundledSkills`: hide the built-in layer so only
1044
1265
  // user/workspace/Claude-Code-portable skills load.
1045
1266
  const dropBundled = bundledSkillsDisabled(options);
@@ -1049,7 +1270,7 @@ export class CLISkillLoader {
1049
1270
  if (!exists) continue;
1050
1271
  if (dropBundled && (layer === "bundled" || layer === "cli-bundled"))
1051
1272
  continue;
1052
- const skills = this._loadFromDir(layerPath, layer);
1273
+ const skills = this._loadFromDir(layerPath, layer, discoveryBudget);
1053
1274
  for (const skill of skills) {
1054
1275
  skillMap.set(skill.id, skill);
1055
1276
  }
@@ -1071,11 +1292,16 @@ export class CLISkillLoader {
1071
1292
  if (allSkillsDisabled(options)) return [];
1072
1293
  const dropBundled = bundledSkillsDisabled(options);
1073
1294
  const out = [];
1295
+ const discoveryBudget = createSkillDiscoveryBudget(this._limits);
1074
1296
  for (const { layer, path: layerPath, exists } of this.getLayerPaths()) {
1075
1297
  if (!exists) continue;
1076
1298
  if (dropBundled && (layer === "bundled" || layer === "cli-bundled"))
1077
1299
  continue;
1078
- for (const skill of this._loadFromDir(layerPath, layer)) {
1300
+ for (const skill of this._loadFromDir(
1301
+ layerPath,
1302
+ layer,
1303
+ discoveryBudget,
1304
+ )) {
1079
1305
  out.push({
1080
1306
  id: skill.id,
1081
1307
  layer: skill.source || layer,
@@ -1103,15 +1329,25 @@ export class CLISkillLoader {
1103
1329
  * @returns {object[]} skills with category "persona" and activation "auto"
1104
1330
  */
1105
1331
  getAutoActivatedPersonas(context = {}) {
1332
+ const promptBudget = {
1333
+ limits: this._limits,
1334
+ bytes: 0,
1335
+ tokens: 0,
1336
+ };
1106
1337
  return this.getResolvedSkills()
1107
1338
  .filter((s) => s.category === "persona" && s.activation === "auto")
1108
- .map((skill) =>
1109
- this.materializeSkill(skill, {
1339
+ .map((skill) => {
1340
+ const materialized = this.materializeSkill(skill, {
1110
1341
  ...context,
1111
1342
  loadedBecause: context.loadedBecause || "persona_auto",
1112
1343
  bodyIncluded: true,
1113
- }),
1114
- );
1344
+ });
1345
+ debitSkillPromptBudget(
1346
+ promptBudget,
1347
+ admitSkillPrompt(materialized.body, this._limits),
1348
+ );
1349
+ return materialized;
1350
+ });
1115
1351
  }
1116
1352
 
1117
1353
  /**
@@ -27,6 +27,11 @@
27
27
  * Pure functions only (except mount/unmount which take an MCPClient dep).
28
28
  */
29
29
 
30
+ import {
31
+ MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED_CODE,
32
+ issueMcpStdioExecutionAuthority,
33
+ } from "./mcp-stdio-execution-authority.js";
34
+
30
35
  /**
31
36
  * Parse MCP server declarations from a SKILL.md body.
32
37
  * Returns an empty array if no `mcp-servers` code block is present.
@@ -103,6 +108,7 @@ export function validateMcpServerConfig(entry) {
103
108
  * @param {object} skill - Skill metadata with `mcpServers` array
104
109
  * @param {object} [opts]
105
110
  * @param {(msg: string, err?: Error) => void} [opts.onWarn] - Warning hook
111
+ * @param {(request: object) => boolean|Promise<boolean>} [opts.approveLocalCodeExecution]
106
112
  * @returns {Promise<{ mounted: string[], skipped: Array<{ name: string, error: string }> }>}
107
113
  */
108
114
  export async function mountSkillMcpServers(mcpClient, skill, opts = {}) {
@@ -130,8 +136,44 @@ export async function mountSkillMcpServers(mcpClient, skill, opts = {}) {
130
136
  }
131
137
  continue;
132
138
  }
139
+ let approved = false;
140
+ try {
141
+ approved =
142
+ typeof opts.approveLocalCodeExecution === "function" &&
143
+ (await opts.approveLocalCodeExecution({
144
+ skillId: skill?.id || skill?.name || null,
145
+ serverName: normalized.name,
146
+ command: normalized.command,
147
+ args: [...normalized.args],
148
+ cwd: normalized.cwd || null,
149
+ envKeys: Object.keys(normalized.env || {}).sort(),
150
+ })) === true;
151
+ } catch (error) {
152
+ const message = error?.message || String(error);
153
+ skipped.push({ name: normalized.name, error: message });
154
+ opts.onWarn?.(
155
+ `[skill-mcp] Local-code approval failed for "${normalized.name}": ${message}`,
156
+ error,
157
+ );
158
+ continue;
159
+ }
160
+ if (!approved) {
161
+ const message = `${MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED_CODE}: skill MCP server requires explicit local-code approval`;
162
+ skipped.push({ name: normalized.name, error: message });
163
+ opts.onWarn?.(
164
+ `[skill-mcp] Skipped "${normalized.name}" for skill "${skill?.id || skill?.name}": ${message}`,
165
+ );
166
+ continue;
167
+ }
133
168
  try {
134
- await mcpClient.connect(normalized.name, normalized);
169
+ const config = { ...normalized };
170
+ config.mcpStdioExecutionAuthority = issueMcpStdioExecutionAuthority({
171
+ serverName: normalized.name,
172
+ config,
173
+ approvalKind: "explicit-config",
174
+ approvalSource: `skill:${skill?.id || skill?.name || "unknown"}:${normalized.name}`,
175
+ });
176
+ await mcpClient.connect(normalized.name, config);
135
177
  mounted.push(normalized.name);
136
178
  } catch (err) {
137
179
  const message = err?.message || String(err);