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.
- package/bin/chainlesschain.js +14 -6
- package/package.json +4 -1
- package/scripts/command-lifecycle-report.mjs +94 -0
- package/src/assets/web-panel/assets/{AIOps-D3Gu1a5d.js → AIOps-DcQ0bWKy.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DrwAoKaz.js → ActionButton-GecwfFPt.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C7Q-gd_3.js → Analytics-CD6iSt9v.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DgBtae-n.js → AppLayout-CDEtuEYf.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-pQ6LcA-W.js → Artifacts-DS3jAjeW.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DK61tocB.js → Audit-CcSJA048.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BOKgDFRh.js → BackgroundAgents-DRo9Morb.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Bo9G4vTP.js → Backup-Vw5MrWft.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Do_dE9_v.js → BaseInput-DNHIu823.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-COVvZzOL.js → Chat-DvAsO8-b.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-9aDuObay.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-Z9MJMolt.js → Checkbox-CKCa7tvn.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BVzWKVbf.js → Codegen-CIFvZdaT.js} +1 -1
- package/src/assets/web-panel/assets/{Col-C8MnYcbT.js → Col-C79yz7bK.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DQxqw15l.js → Community-DVvZskLJ.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BAUmrh3y.js → Compact-oaew61hV.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-jPAkQ3zK.js → Compliance-DqA7wmRx.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BBQyxbh8.js → Cowork-B-qGadxa.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-W5koq8Z5.js → Cron-CUlCl4sS.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BQhnu5hS.js → Crosschain-CNOlr9DU.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DD7PlI18.js → DID-DJqFCT_3.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-w_ILuwKI.js → Dashboard-CznGpphf.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-qiDUAyAo.js → Dropdown-_cJw218E.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-PDeBcI5H.js → EmailListRenderer-Dr-1BZ9C.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-SuArscRM.js → FamilyGuardDashboard-Dxf4xtSk.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BCovZaUn.js → Federation-DOiG3i_1.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DjlFdVFk.js → FormItemContext-BzbMcFB9.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DoEZ7fcS.js → GenericCardRenderer-VbBrYttP.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CV0-6GPI.js → Git-CLG8E6ED.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B_SFN50w.js → Governance-LGJQfk_q.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-heI0Eo6k.js → Inference-DlQKx9lF.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cz5INS5p.js → KnowledgeGraph-DU8vEouV.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BSFxiHkg.js → Logs-FZIQOdln.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BJNWIrbw.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-0TWTpE7k.js → Marketplace-C_xd-zZI.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CV4-wBLm.js → McpTools-q-YktphS.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-x5bH5rw5.js → Memory-Cq9OhDin.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DaBmUu9G.js → MobileBridge-B03_R98T.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-DZrk-qmF.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-B_zKEBif.js → Mtc-BidCKW28.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BCfKD2yg.js → MtcAudit-r-iaMVM7.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DgYfj6c_.js → Multisig-CgTUFvff.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dz09lFUm.js → NLProgramming-B7NvzfAV.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cuj6ugBE.js → Notes-80CWNqJI.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DK2WXjhg.js → NotificationSettings-0H6xdiY3.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-D1IAOQoo.js → OrderTableRenderer-CKP4AFWy.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-9fqnPvTw.js → Organization-B8sxCV_h.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-n5xHkhow.js → Overflow-CW9JBBhQ.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-B5AUWwH-.js → P2P-bdSQV7oZ.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CoUDV6dy.js → PdhVaultBrowser-DQad42nW.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-DPRmQf_H.js → Permissions-CueexxTR.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-B37bsbEN.js → PersonalDataHub-fqxflhR8.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-CCAuPqu9.js → Pipeline-KFwwnIMC.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B81msZdv.js → Privacy-ClnD9buY.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B-Py2NbF.js → ProjectInit-CZEz6sm3.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CYsneiRB.js → ProjectSettings-B6NzpmOI.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Y975Mh6R.js → Projects-DYyuwPCs.js} +1 -1
- package/src/assets/web-panel/assets/Providers-C3tz_jdh.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-Bj_6gJ0l.js → QrScannerModal-BzuOBbqB.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BgpBCMPE.js → QuickAsk-CmNO9qSj.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DhUW3YUA.js → Recommend-VJNj3_NF.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DdeyfbkA.js → RemoteSession-CrbdbSBF.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-fxW421xd.js → Reputation-DTyMd45j.js} +1 -1
- package/src/assets/web-panel/assets/{Row-De73w1og.js → Row-DHrQ-Icz.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-3US_Q_lj.js → RssFeed-DqWxY9c5.js} +2 -2
- package/src/assets/web-panel/assets/{Search-DbZ_DU74.js → Search-ctF1Ks3J.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DoIU1vMF.js → Security-BrQz8uo5.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DB_RGqpc.js → Services-D4scVFar.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DnzLaayC.js → Skeleton-oUScaOjv.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-VzVXHENF.js → Skills-CL-y0eLt.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DMfvjCAI.js → Sla-DQExhwXj.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DQQOYWbF.js → SpeechSettings-iNp_I7kN.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Bmx3FG1P.js → SyncSettings-CTDlMo0A.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DOQyfb-2.js → Tasks-4kb8l3sc.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-D6hTRT7c.js → Templates-BXcL9oXW.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CTPQZAzs.js → Tenant-DLE8eS0D.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B7vII3oH.js → Terminal-DGpUZPPK.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BkiFJeXV.js → TimelineRenderer-C1pWXGPC.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BPPdQMNo.js → Tokens-BYfbfqhZ.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BxG8JAEI.js → Trigger-K9YfLOCw.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DHD7nMpM.js → Trust-Ctkq77Hy.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B4BJpU47.js → UkeySign-C6_CSe9J.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CRiGpFx2.js → VideoEditing-Be3rpOWs.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BSDSNiem.js → Wallet-D0UIplxk.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DLTUh5Zv.js → WebAuthn-CF9bM9I3.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-tW2MwjhD.js → WorkflowEditor-D0H1XVPY.js} +1 -1
- package/src/assets/web-panel/assets/{chat-SgsLQ3FC.js → chat-Cgzzbe0o.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BHjjLIKO.js → colors-BwNxEVS0.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-VceErdKq.js → compact-item-D60Z4Hhs.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BZ332MYl.js → createContext-MFk58XB0.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BJiw369i.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CBTjJOjn.js → hasIn-CRj2SvGO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaCCy6lq.js → index--s45_1cb.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMVvxIS8.js → index--tquQ2ZG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhXZEUAc.js → index-1T1rhokn.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQSmZGgs.js → index-B-M82_rx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BeiqkxJG.js → index-BE_el1Zs.js} +1 -1
- package/src/assets/web-panel/assets/{index-29wVzi-k.js → index-BhRkR7IO.js} +1 -1
- package/src/assets/web-panel/assets/{index-iYQNU9za.js → index-Bo0y96-K.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRWhLbWF.js → index-CJgu5JUK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPXFLYdW.js → index-CLWwdebO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cr4rjlW5.js → index-CNAK5QG_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaIS_TNX.js → index-CQjJjPRD.js} +1 -1
- package/src/assets/web-panel/assets/{index-1n7xjKDU.js → index-CTDbAfae.js} +1 -1
- package/src/assets/web-panel/assets/{index-BETEjuNr.js → index-CVI_9sRK.js} +1 -1
- package/src/assets/web-panel/assets/{index-By5jBtkV.js → index-CY14oMQK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgPvuIn2.js → index-CbiTJkZf.js} +1 -1
- package/src/assets/web-panel/assets/index-CiMouosn.js +1 -0
- package/src/assets/web-panel/assets/{index-CawpdGTW.js → index-CqWpxMqx.js} +1 -1
- package/src/assets/web-panel/assets/{index-_PHrIO4O.js → index-CvB8lYUR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOtvPdHt.js → index-D51XwtCY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrUgDQkf.js → index-D6HvK_1O.js} +1 -1
- package/src/assets/web-panel/assets/{index-BY1wexYG.js → index-D9jVIwLJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_B9rFQE.js → index-DA4tE98u.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5KAGxNo.js → index-DBwOtXw0.js} +1 -1
- package/src/assets/web-panel/assets/{index-BC1xuQgX.js → index-DKdNzTIb.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2u26AS3.js → index-DRySdmvN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DePOeMDM.js → index-DWzyTev7.js} +1 -1
- package/src/assets/web-panel/assets/index-DXcKlwy7.js +1 -0
- package/src/assets/web-panel/assets/{index-BTlc-Bve.js → index-DYjs7jAP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLc19alH.js → index-DbROzMBp.js} +1 -1
- package/src/assets/web-panel/assets/{index-fU7wNHDB.js → index-DhFtk5PR.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_SePDWj.js → index-Di2AbMLg.js} +1 -1
- package/src/assets/web-panel/assets/{index-vIXOpcjD.js → index-DvmocQsM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcUenmCX.js → index-Dw-o0zEF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUkVN794.js → index-DysPoP38.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtjLcCNU.js → index-LX9pJxm_.js} +1 -1
- package/src/assets/web-panel/assets/{index-B40dQn0G.js → index-OI_fXWx0.js} +3 -3
- package/src/assets/web-panel/assets/{index-FJ3zC9LK.js → index-eDTOX8Xj.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdefYBSw.js → index-gWKDsTzF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpumS98o.js → index-q_GDUVAA.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D1oZNz0D.js → initDefaultProps-C61kZqEd.js} +1 -1
- package/src/assets/web-panel/assets/{motion-C8k6doMO.js → motion-DZnT5U1L.js} +1 -1
- package/src/assets/web-panel/assets/{move-DJ4qSbYt.js → move-L8wzwbel.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CdpJD85j.js → mtc-parser-DLdYSejc.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B0V4slKA.js → omit-D53cbS59.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Di5NiW1F.js → pickAttrs-Bbc5HPtu.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B5OMHgEM.js → placementArrow-BkvmO860.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C6U9IWql.js → responsiveObserve-u2y1ryS1.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BMyTH3rN.js → slide-BBqj7svU.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C7d6BG7Z.js → statusUtils-GN0mqu2d.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CR50QnQy.js → styleChecker-BvwPKoBT.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DBEeoMb8.js → useFlexGapSupport-CCt8tnfY.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-3r099cjv.js → useFs-DXAxl2SV.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-C5IYNdTK.js → usePersonalDataHub-DihlR2dj.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DmEOLRHf.js → vnode-D7BOX2gi.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DnpUiVCB.js → zoom-CVbO-nL7.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +4 -4
- package/src/commands/agent.js +3 -3
- package/src/commands/background-session.js +145 -0
- package/src/commands/mcp.js +244 -7
- package/src/commands/serve.js +1 -1
- package/src/commands/session-show.js +129 -0
- package/src/commands/session.js +97 -145
- package/src/commands/skill.js +87 -50
- package/src/data/changelog.json +40 -1
- package/src/gateways/ws/session-protocol.js +14 -4
- package/src/gateways/ws/ws-agent-handler.js +39 -1
- package/src/harness/jsonl-session-store.js +1547 -127
- package/src/harness/mcp-client.js +2690 -409
- package/src/harness/session-index.js +80 -80
- package/src/harness/session-list-index.js +210 -7
- package/src/lazy-dispatch.js +76 -8
- package/src/lib/abort-utils.js +68 -0
- package/src/lib/agent-core.js +1 -59
- package/src/lib/background-interaction-journal.js +2 -2
- package/src/lib/chat-core.js +24 -6
- package/src/lib/command-lifecycle-report.js +516 -0
- package/src/lib/cowork-mcp-tools.js +47 -2
- package/src/lib/cowork-task-runner.js +11 -3
- package/src/lib/doctor-checkup.js +9 -7
- package/src/lib/learning/skill-improver.js +21 -2
- package/src/lib/mcp-call-ledger-store.js +116 -3
- package/src/lib/mcp-call-ledger.js +59 -14
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-stdio-environment.js +48 -0
- package/src/lib/mcp-stdio-executable-identity.js +1133 -0
- package/src/lib/mcp-stdio-execution-authority.js +363 -0
- package/src/lib/mcp-stdio-package-materialization.js +1424 -0
- package/src/lib/mcp-tool-metadata.js +436 -0
- package/src/lib/mcp-tool-result.js +367 -0
- package/src/lib/observability/index.js +71 -0
- package/src/lib/packer/native-update-state.js +979 -1
- package/src/lib/packer/pack-update-applier.js +1050 -125
- package/src/lib/packer/pkg-config-generator.js +1 -0
- package/src/lib/paths.js +53 -0
- package/src/lib/pr-link-ledger.js +8 -27
- package/src/lib/pr-link-store.js +26 -0
- package/src/lib/process-execution-broker/index.js +192 -35
- package/src/lib/process-execution-broker/platform-sandbox.js +291 -24
- package/src/lib/process-tree-termination.js +253 -0
- package/src/lib/recent-session.js +17 -17
- package/src/lib/secure-file-identity.js +47 -0
- package/src/lib/secure-fs.js +21 -2
- package/src/lib/session-anti-rollback-anchor.js +766 -0
- package/src/lib/session-host-lease.js +492 -0
- package/src/lib/session-host-snapshot.js +9 -5
- package/src/lib/session-persistence-failure.js +109 -0
- package/src/lib/session-picker.js +26 -3
- package/src/lib/session-projection.js +5 -1
- package/src/lib/session-search.js +25 -11
- package/src/lib/session-tail.js +31 -1
- package/src/lib/side-effect-ledger-store.js +2 -2
- package/src/lib/skill-budget.js +222 -0
- package/src/lib/skill-execution-authority.js +385 -0
- package/src/lib/skill-execution-identity.js +39 -2
- package/src/lib/skill-loader.js +322 -86
- package/src/lib/skill-mcp.js +43 -1
- package/src/lib/turn-binding-store.js +2 -2
- package/src/repl/agent-repl.js +483 -77
- package/src/repl/bg-dashboard.js +81 -28
- package/src/repl/chat-repl.js +77 -12
- package/src/runtime/agent-core.js +428 -113
- package/src/runtime/agent-runtime.js +26 -3
- package/src/runtime/cleanup-deadline.js +143 -0
- package/src/runtime/headless-runner.js +494 -149
- package/src/runtime/headless-stream.js +504 -75
- package/src/runtime/mcp-config.js +157 -10
- package/src/runtime/mcp-tool-search.js +0 -0
- package/src/runtime/output-backpressure.js +419 -0
- package/src/runtime/pipe-safety.js +56 -21
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BUUyJ5QO.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-1mNozZ8v.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-DqnoFPgR.js +0 -1
- package/src/assets/web-panel/assets/Providers-uO-10aP1.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DosMaL-k.js +0 -1
- package/src/assets/web-panel/assets/index-0O5ePnUq.js +0 -1
- package/src/assets/web-panel/assets/index-ChAzPigF.js +0 -1
- package/src/lib/skill-process-broker.js +0 -85
package/src/lib/skill-loader.js
CHANGED
|
@@ -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 =
|
|
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
|
|
470
|
-
this.
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
-
|
|
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
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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
|
-
|
|
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
|
|
817
|
+
let directory;
|
|
638
818
|
try {
|
|
639
|
-
|
|
819
|
+
directory = fs.opendirSync(dir);
|
|
640
820
|
} catch {
|
|
641
821
|
return; // Directory unreadable
|
|
642
822
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
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
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
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
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
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(
|
|
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
|
/**
|
package/src/lib/skill-mcp.js
CHANGED
|
@@ -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
|
-
|
|
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);
|