chainlesschain 0.162.188 → 0.162.193
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/README.md +61 -11
- package/bin/chainlesschain.js +7 -52
- package/completions/_cc +209 -0
- package/completions/cc.bash +18 -0
- package/completions/cc.fish +13 -0
- package/completions/cc.ps1 +226 -0
- package/package.json +10 -2
- package/src/assets/web-panel/assets/{AIOps-Brr1LVcE.js → AIOps-DBLkaTje.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DQU74DKG.js → ActionButton-BAy-Qebo.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CYp9178y.js → Analytics-Dfb1Mlau.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DW4SCYsW.js → AppLayout-BbFkN8m0.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CYlD_ed1.js → Artifacts-kM4-cEEH.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CWl1rZOi.js → Audit-BSQfYWf3.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BOR-LPsa.js → BackgroundAgents-CWDbI_fG.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DLpG7j1X.js → Backup-BxLPbq_Q.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-B35K9wdr.js → BaseInput-DuIbdrp6.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CwUJb9gB.js → Chat-NEmjGahU.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-1v6aMS1m.js → ChatBubbleRenderer-DfJEo-5_.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-pi3-tPNW.js → Checkbox-CaoeG3Ns.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-FyNF35eL.js → Codegen-BFf_g3xY.js} +1 -1
- package/src/assets/web-panel/assets/{Col-tGt_JDEr.js → Col-CNOd8mdQ.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CC7LonQX.js → Community-BhpDqz-i.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BGXrHHjE.js → Compact-CQ42T7Z6.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DSW0TBZ8.js → Compliance-DI5PcLMN.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Bhk7rbvE.js → Cowork-wYYZe0wR.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DxyiQ2NZ.js → Cron-Dlq03DD_.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-B2ZkxHok.js → Crosschain-BzqS8PP3.js} +1 -1
- package/src/assets/web-panel/assets/{DID-yOJV99eJ.js → DID-It55Yem7.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CKsttz7D.js → Dashboard-9HkH6oxn.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CVFldXSL.js → Dropdown-00e2YWRB.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Bufv638J.js → EmailListRenderer-By-ugRDL.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-4ncjoIjL.js → FamilyGuardDashboard-C396yu5m.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DsknEDoX.js → Federation-BME5sbZY.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Cqe5jWXA.js → FormItemContext-B4TEZCr-.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-NAJaFKxc.js → GenericCardRenderer-_9CX9ieY.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CszKQpnl.js → Git-BrS6WVSr.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BXGodXi6.js → Governance-BMdCWnKF.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CkoYuG2c.js → Inference-Bcp3vzcA.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DFlEr_T0.js → KnowledgeGraph-DlLuqGY5.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CVaENddA.js → Logs-D-W_6x09.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-DElGPxCZ.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-CDlLT0fw.js → Marketplace-Dzn2guvI.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DdwOyszN.js → McpTools-uPict-KL.js} +6 -6
- package/src/assets/web-panel/assets/{Memory-DKQnkKuG.js → Memory-BCw43kOb.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DGseqlzJ.js → MobileBridge-DmV8Wv7F.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-COBbEOlj.js → MobileProjects-C-HaCmSM.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-O61Vwyli.js → Mtc-C6jxJ34Y.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Dw2SyasS.js → MtcAudit-Za8ExwhQ.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-hSu_l8kf.js → Multisig-KKDbuEgb.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CX1Epurr.js → NLProgramming-Dj97zbCk.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-R9xX8o8_.js → Notes-DBXvMraU.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-jtSNLCuK.js → NotificationSettings-DOxITLYl.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-7DVjZwMM.js +1 -0
- package/src/assets/web-panel/assets/{Organization-Pu2WO-U3.js → Organization-D1i73mjb.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BjnqUyFp.js → Overflow-kW6Q3rvg.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-jjkvz44c.js → P2P-C-v_8p2v.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-tUbbY64W.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-D4yDpivd.js → Permissions-DkHxEZ58.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-0qsL6iH1.js → PersonalDataHub-DWuMPGrK.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-Cx7Ybrk2.js → Pipeline-30IYXWWm.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-PbfR_M_V.js → Privacy-DZNQ4RrH.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CyV6Gsub.js → ProjectInit-De2MCElf.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C7KZ2Wdk.js → ProjectSettings-5uHk9wsC.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DQDjO-yK.js → Projects-XjDfrFLb.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DyFVBBMX.js → Providers-Bgwj6gcE.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CzB1BC9_.js → QrScannerModal-gKql9umv.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Bk636mC1.js → QuickAsk-BWvmwkh7.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DoWxHIT3.js → Recommend-Uok4hZPd.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-oOVIv9wq.js → RemoteSession-DLJzTk9a.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-BucoReGG.js → Reputation-ZVwIPMSO.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DzmOdJU7.js → Row-CN04HxWR.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-C1tK7KGU.js → RssFeed-BdSrEG4g.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CSnInOqk.js → Search-5PwVd0IR.js} +1 -1
- package/src/assets/web-panel/assets/{Security-dlejr15l.js → Security-DdBo4zjN.js} +3 -3
- package/src/assets/web-panel/assets/{Services-BioPZuNh.js → Services-D3letU7M.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DWMibwpC.js → Skeleton--Jb83f1v.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BAAnx1B2.js → Skills-Bg-CulHX.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-jWNp4hNO.js → Sla-7QOa7S-X.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-y5kqKha-.js → SpeechSettings-DQwppjRw.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-B9GNsc3e.js → SyncSettings-dNPQGL-s.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BDBYKRen.js → Tasks-Ggs-Z48q.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DRiE5eaN.js → Templates-78TYHgRO.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BGlAvExF.js → Tenant-xHwwv9vO.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B8uzrbNY.js → Terminal-BJXSJfm4.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Df48iveY.js → TimelineRenderer-fLbjRhJw.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CcmHndN6.js → Tokens-oMSBSBmB.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DPkWxmQ2.js → Trigger-CfnY_wX_.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DTb5a5AV.js → Trust-AdyTgYKO.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CBmg5vch.js → UkeySign-C5K_81dS.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B0mZrz4O.js → VideoEditing-vcGVDqki.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Bxs7_mpd.js → Wallet-hhOc3hlY.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-C8x3cMxU.js → WebAuthn-CVloC11s.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-DPrNznrP.js → WorkflowEditor-CYeE32R5.js} +1 -1
- package/src/assets/web-panel/assets/{chat-D3bYWu7S.js → chat-CNROR5bN.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DY-PwMu4.js → colors-Cjx-cnwU.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-LDk10Kw6.js → compact-item-DOIfelvD.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C-2MZnFq.js → createContext-DfA7UZ8u.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BQIBjcnB.js → hasIn-BMSCzcfM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQVb2dWp.js → index-5-HXgOX5.js} +1 -1
- package/src/assets/web-panel/assets/{index-BdXnYMCZ.js → index-AWg-0M1S.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cx6D_dx2.js → index-B4iYzpkL.js} +1 -1
- package/src/assets/web-panel/assets/{index-_HRO_cIu.js → index-B5Udeb_6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlRYqt3S.js → index-B7gf77CJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BrnRO9yE.js → index-BEMwBBHk.js} +1 -1
- package/src/assets/web-panel/assets/{index-VCh5zNJo.js → index-BM1y_9hM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRI3ngNg.js → index-BUonmKSE.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3GNvExG.js → index-BbQ2eQ8H.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSvo_bp0.js → index-BemJ2u_E.js} +1 -1
- package/src/assets/web-panel/assets/{index-L6qixyLc.js → index-BolaOQxx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BI4KgcYC.js → index-BxOk9vkG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CY4KnaO9.js → index-By61ojUx.js} +1 -1
- package/src/assets/web-panel/assets/{index-L3smcoQX.js → index-C2K9SNVX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BX2b9Orc.js → index-C3L5gdiD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cz4b6Z_7.js → index-C7xn0Nub.js} +1 -1
- package/src/assets/web-panel/assets/{index-COG61sRv.js → index-CAnvhwg6.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7FC8cP2.js → index-CEKMcOyU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BN-xdSWZ.js → index-CNC5onm4.js} +1 -1
- package/src/assets/web-panel/assets/{index-DY2FIrmz.js → index-CVL27kXC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiTvBAr-.js → index-CVm1_i3t.js} +1 -1
- package/src/assets/web-panel/assets/{index-DhRt9cYJ.js → index-CdYBye8B.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5pgtZnY.js → index-ClNMrqEY.js} +1 -1
- package/src/assets/web-panel/assets/{index-7xKu7Fns.js → index-Cs6l7uRu.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4K1weKc.js → index-CshQbilz.js} +1 -1
- package/src/assets/web-panel/assets/{index-k7C_oDCO.js → index-Cyweh0Hy.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3Oq-DLL.js → index-DBpWXFed.js} +3 -3
- package/src/assets/web-panel/assets/{index-DYyLJPlF.js → index-DHtpHZAQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dh-tkpKQ.js → index-DLmn0BoK.js} +1 -1
- package/src/assets/web-panel/assets/{index-8JC9BJa1.js → index-DPAWBrMf.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dd2Km97U.js → index-DjEsxO5h.js} +1 -1
- package/src/assets/web-panel/assets/{index-KFZO_lcx.js → index-FBc_CJBd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTIZpPtG.js → index-Icdoqmzt.js} +1 -1
- package/src/assets/web-panel/assets/index-Q2IajL54.js +1 -0
- package/src/assets/web-panel/assets/index-bRkHgOMc.js +1 -0
- package/src/assets/web-panel/assets/{index-lmm9p-jm.js → index-dCd8cSUq.js} +1 -1
- package/src/assets/web-panel/assets/{index-HasPt4-h.js → index-m2vG9gG_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaslYch6.js → index-mTpHoEzr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbR6ROOa.js → index-ti_lnTWl.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-zQPSHf2C.js → initDefaultProps-Bu8k7H1Y.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CKCStGVk.js → motion-s3iVK41k.js} +1 -1
- package/src/assets/web-panel/assets/{move-BufVDGYF.js → move-B6B-E5Jz.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DcaFxy5D.js → mtc-parser-BiYgTidA.js} +1 -1
- package/src/assets/web-panel/assets/{omit-EScpkGdo.js → omit-pU1RdUDA.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-72hG_qfT.js → pickAttrs-Cdoq_SeM.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CMFWiH20.js → placementArrow-DIuvAn2K.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DKQHS41p.js → responsiveObserve-8w16YR2D.js} +1 -1
- package/src/assets/web-panel/assets/{slide-QVAFHTIQ.js → slide-VZ01cZe3.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DIz38e2G.js → statusUtils-lh8YMN8Z.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-zK5cyLby.js → styleChecker-CmDyreec.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DhZngk62.js → useFlexGapSupport-hiJWMU5y.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BvV2cvkg.js → useFs-Thnu-Iaj.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CRmxb9l3.js → usePersonalDataHub-LBdOxjwY.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CgQIbobP.js → vnode-Cg6lFY_X.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-D4XrrO0R.js → zoom-DVfsR1YO.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/cli-help.js +181 -0
- package/src/command-help-index.json +181 -0
- package/src/command-manifest.json +1107 -175
- package/src/command-surface-policy.js +197 -0
- package/src/commands/agent.js +98 -33
- package/src/commands/artifacts.js +400 -2
- package/src/commands/background-session.js +41 -3
- package/src/commands/checkpoint-managed.js +1059 -0
- package/src/commands/checkpoint-restore-recovery.js +1476 -0
- package/src/commands/checkpoint.js +950 -22
- package/src/commands/compact.js +101 -25
- package/src/commands/config.js +506 -20
- package/src/commands/doctor.js +4 -1
- package/src/commands/hub.js +1 -1
- package/src/commands/mcp.js +688 -24
- package/src/commands/pack.js +16 -2
- package/src/commands/plugin.js +20 -12
- package/src/commands/session-mcp-recovery.js +218 -0
- package/src/commands/session.js +470 -110
- package/src/commands/setup.js +36 -10
- package/src/commands/skill.js +181 -110
- package/src/commands/status.js +21 -5
- package/src/commands/team-distributed.js +3324 -0
- package/src/commands/team.js +1130 -74
- package/src/commands/update.js +68 -6
- package/src/constants.js +11 -0
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/action-protocol.js +10 -0
- package/src/gateways/ws/session-protocol.js +555 -27
- package/src/gateways/ws/ws-agent-handler.js +541 -14
- package/src/gateways/ws/ws-session-gateway.js +129 -22
- package/src/gateways/ws/ws-session-state.js +431 -0
- package/src/harness/background-task-manager.js +233 -36
- package/src/harness/jsonl-session-store.js +2551 -167
- package/src/harness/mcp-client.js +802 -61
- package/src/harness/prompt-compressor.js +227 -27
- package/src/harness/session-index.js +89 -38
- package/src/harness/session-list-index.js +254 -0
- package/src/harness/structured-handoff.js +410 -0
- package/src/lazy-dispatch.js +747 -84
- package/src/lib/__tests__/logger.test.js +50 -2
- package/src/lib/advisor-runtime.js +1047 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/agent-sandbox.js +120 -26
- package/src/lib/agent-session-export.js +20 -0
- package/src/lib/agent-team/task-lease.js +271 -3
- package/src/lib/agent-team/team-adjudication.js +1501 -0
- package/src/lib/agent-team/team-budget.js +4 -1
- package/src/lib/agent-team/team-control-store.js +582 -0
- package/src/lib/agent-team/team-distributed-queue.js +5246 -0
- package/src/lib/agent-team/team-process-checkpoint.js +211 -0
- package/src/lib/agent-team/team-runner.js +543 -24
- package/src/lib/agent-team/team-worktree.js +1403 -95
- package/src/lib/artifact-store.js +131 -8
- package/src/lib/background-agent-supervisor.js +185 -13
- package/src/lib/background-interaction-journal.js +37 -9
- package/src/lib/background-launch-profile.js +1058 -0
- package/src/lib/checkpoint-restore-already-completed-controller.js +902 -0
- package/src/lib/checkpoint-restore-orchestrator.js +987 -0
- package/src/lib/checkpoint-restore-partial-rollback-controller.js +1953 -0
- package/src/lib/checkpoint-restore-recovery-controller.js +689 -0
- package/src/lib/checkpoint-restore-recovery.js +884 -0
- package/src/lib/checkpoint-restore-retention.js +126 -0
- package/src/lib/checkpoint-restore-saga.js +5079 -0
- package/src/lib/checkpoint-restore-session-recovery.js +1178 -0
- package/src/lib/checkpoint-restore-workspace-target-verifier.js +270 -0
- package/src/lib/checkpoint-store.js +1688 -97
- package/src/lib/checkpoint-timeline-authority.js +522 -0
- package/src/lib/checkpoint-timeline.js +642 -0
- package/src/lib/config-keys.js +18 -119
- package/src/lib/config-manager.js +446 -27
- package/src/lib/config-redaction.js +57 -0
- package/src/lib/config-schema.js +853 -0
- package/src/lib/cowork-mcp-tools.js +5 -0
- package/src/lib/cowork-task-runner.js +458 -63
- package/src/lib/delivery-coordinator.js +1234 -0
- package/src/lib/delivery-evidence.js +450 -0
- package/src/lib/doctor-checkup.js +199 -14
- package/src/lib/executable-path.js +34 -0
- package/src/lib/fatal-handler.js +16 -0
- package/src/lib/file-checkpoint.js +4909 -186
- package/src/lib/file-lines.js +125 -0
- package/src/lib/headless-config-command.js +16 -5
- package/src/lib/hook-runner.cjs +139 -38
- package/src/lib/hooks-v2-workspace-context.js +10 -0
- package/src/lib/impacted-gate-selector.js +424 -0
- package/src/lib/interaction-adapter.js +62 -0
- package/src/lib/jsonl-session-store.js +1 -35
- package/src/lib/logger.js +66 -14
- package/src/lib/managed-checkpoint-render.js +60 -0
- package/src/lib/managed-tool-checkpoint.js +194 -0
- package/src/lib/mcp-call-ledger-store.js +1069 -0
- package/src/lib/mcp-call-ledger.js +747 -0
- package/src/lib/mcp-client.js +5 -0
- package/src/lib/mcp-conflict-scheduler.js +468 -0
- package/src/lib/mcp-effect-contract.js +102 -0
- package/src/lib/mcp-headers-helper-trust.js +145 -0
- package/src/lib/mcp-headers-helper.js +616 -0
- package/src/lib/mcp-host-recovery-runtime.js +131 -0
- package/src/lib/mcp-ledger-recovery-admission.js +1065 -0
- package/src/lib/mcp-recovery-adjudication.js +443 -0
- package/src/lib/output-context.js +86 -0
- package/src/lib/packer/file-identity.js +170 -0
- package/src/lib/packer/index.js +3 -0
- package/src/lib/packer/native-update-state.js +277 -0
- package/src/lib/packer/pack-update-applier.js +1983 -136
- package/src/lib/packer/pack-update-checker.js +34 -0
- package/src/lib/packer/pack-update-downloader.js +875 -86
- package/src/lib/packer/pack-update-signature.js +86 -0
- package/src/lib/packer/pkg-config-generator.js +6 -0
- package/src/lib/paths.js +65 -10
- package/src/lib/plan-mode.js +831 -14
- package/src/lib/plan-persistence.js +540 -0
- package/src/lib/plugin-runtime/hooks.js +227 -69
- package/src/lib/plugin-runtime/plugin-options.js +189 -44
- package/src/lib/pr-automation-policy.js +146 -8
- package/src/lib/process-execution-broker/credential-agent.js +7 -0
- package/src/lib/process-execution-broker/index.js +491 -13
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
- package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
- package/src/lib/process-execution-broker/workspace-transaction.js +4459 -0
- package/src/lib/project-instructions.js +165 -29
- package/src/lib/project-mcp-trust.js +67 -0
- package/src/lib/public-ide-capability-manifest.js +272 -0
- package/src/lib/public-ide-capability-validation.js +351 -0
- package/src/lib/secret-store.js +267 -43
- package/src/lib/secure-file-identity.js +433 -0
- package/src/lib/secure-fs.js +1089 -0
- package/src/lib/security-audit.js +58 -0
- package/src/lib/service-manager.js +34 -13
- package/src/lib/session-budget-runtime.js +1452 -0
- package/src/lib/session-host-snapshot.js +388 -0
- package/src/lib/session-message-provenance.js +429 -0
- package/src/lib/session-projection.js +804 -0
- package/src/lib/session-resource-budget.js +1495 -0
- package/src/lib/settings-hooks.cjs +138 -22
- package/src/lib/settings-loader.cjs +25 -2
- package/src/lib/side-effect-ledger-store.js +45 -23
- package/src/lib/skill-execution-identity.js +217 -0
- package/src/lib/skill-loader.js +299 -18
- package/src/lib/sub-agent-context.js +236 -28
- package/src/lib/sub-agent-registry.js +5 -0
- package/src/lib/subagent-contract.js +94 -0
- package/src/lib/todo-manager.js +185 -18
- package/src/lib/todo-persistence.js +417 -0
- package/src/lib/turn-binding-store.js +41 -23
- package/src/lib/turn-binding.js +160 -2
- package/src/lib/with-file-lock.js +165 -24
- package/src/lib/ws-chat-handler.js +21 -0
- package/src/program-base.js +18 -0
- package/src/repl/advisor-command.js +103 -0
- package/src/repl/agent-repl.js +2175 -146
- package/src/repl/btw-command.js +93 -10
- package/src/repl/clipboard-image.js +119 -0
- package/src/repl/config-summary.js +64 -108
- package/src/repl/prompt-editor.js +233 -0
- package/src/repl/prompt-interactions.js +344 -0
- package/src/repl/prompt-stash.js +354 -0
- package/src/repl/prompt-suggestions.js +312 -0
- package/src/repl/repl-keybindings.js +227 -0
- package/src/repl/session-recap.js +217 -0
- package/src/repl/slash-command-registry.js +22 -0
- package/src/repl/terminal-layout.js +88 -0
- package/src/runtime/agent-core.js +1630 -447
- package/src/runtime/coding-agent-contract-shared.cjs +13 -1
- package/src/runtime/coding-agent-managed-tool-policy.cjs +7 -2
- package/src/runtime/diagnostics.js +32 -13
- package/src/runtime/headless-runner.js +310 -118
- package/src/runtime/headless-stream.js +356 -95
- package/src/runtime/mcp-config.js +148 -14
- package/src/runtime/policies/agent-policy.js +14 -0
- package/src/runtime/status-diagnostics-lite.js +137 -0
- package/src/assets/web-panel/assets/MarkdownRenderer-7iWGwZ-r.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-BU2Yqq5H.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-69ca_WeR.js +0 -7
- package/src/assets/web-panel/assets/devWarning-B4dXIJqD.js +0 -1
- package/src/assets/web-panel/assets/index-C-C7DaG6.js +0 -1
- package/src/assets/web-panel/assets/index-DkfWzWtd.js +0 -1
package/src/lib/mcp-client.js
CHANGED
|
@@ -13,9 +13,14 @@ export {
|
|
|
13
13
|
MCPClient,
|
|
14
14
|
MCPServerConfig,
|
|
15
15
|
inferTransport,
|
|
16
|
+
isWebSocketTransport,
|
|
17
|
+
MCP_CONFIG_SCOPES,
|
|
18
|
+
normalizeMcpConfigScope,
|
|
16
19
|
normalizeMcpElicitationRequest,
|
|
17
20
|
isHttpTransport,
|
|
21
|
+
redactMcpUrl,
|
|
18
22
|
isLikelyConnectionError,
|
|
23
|
+
isMcpAuthenticationError,
|
|
19
24
|
isTransientMcpError,
|
|
20
25
|
_deps,
|
|
21
26
|
} from "../harness/mcp-client.js";
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { McpEffect, normalizeMcpEffectContract } from "./mcp-call-ledger.js";
|
|
3
|
+
|
|
4
|
+
export const MCP_CONFLICT_SCHEDULER_SCHEMA_VERSION = 1;
|
|
5
|
+
|
|
6
|
+
export const McpConflictReason = Object.freeze({
|
|
7
|
+
NONE: "none",
|
|
8
|
+
SAME_SCOPE_MAY_WRITE: "same-scope-may-write",
|
|
9
|
+
MUTATING_EFFECT_SERIALIZED: "mutating-effect-serialized",
|
|
10
|
+
UNKNOWN_EFFECT_SERIALIZED: "unknown-effect-serialized",
|
|
11
|
+
UNTRUSTED_CONTRACT_SERIALIZED: "untrusted-contract-serialized",
|
|
12
|
+
OPEN_WORLD_SERIALIZED: "open-world-serialized",
|
|
13
|
+
MISSING_SCOPE_SERIALIZED: "missing-scope-serialized",
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const HOST_EFFECT_CONTRACTS = new WeakSet();
|
|
17
|
+
const NORMALIZED_CONCURRENCY_REQUESTS = new WeakSet();
|
|
18
|
+
|
|
19
|
+
function stableHash(parts) {
|
|
20
|
+
const hash = createHash("sha256");
|
|
21
|
+
for (const part of parts) {
|
|
22
|
+
const value = String(part);
|
|
23
|
+
hash.update(String(Buffer.byteLength(value, "utf8")));
|
|
24
|
+
hash.update("\0");
|
|
25
|
+
hash.update(value);
|
|
26
|
+
hash.update("\0");
|
|
27
|
+
}
|
|
28
|
+
return hash.digest("hex");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Brand a contract at the host boundary. A plain object from a model or MCP
|
|
33
|
+
* server cannot self-declare itself safe for parallel execution, even if it
|
|
34
|
+
* copies every visible field from this object.
|
|
35
|
+
*/
|
|
36
|
+
export function createHostOwnedMcpEffectContract(contract = {}) {
|
|
37
|
+
const normalized = normalizeMcpEffectContract(contract);
|
|
38
|
+
const hostContract = Object.freeze({
|
|
39
|
+
...normalized,
|
|
40
|
+
authority: "host",
|
|
41
|
+
});
|
|
42
|
+
HOST_EFFECT_CONTRACTS.add(hostContract);
|
|
43
|
+
return hostContract;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function isHostOwnedMcpEffectContract(contract) {
|
|
47
|
+
return Boolean(
|
|
48
|
+
contract &&
|
|
49
|
+
typeof contract === "object" &&
|
|
50
|
+
HOST_EFFECT_CONTRACTS.has(contract),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function scopeValue(scope) {
|
|
55
|
+
if (typeof scope === "string") return scope;
|
|
56
|
+
if (!scope || typeof scope !== "object") return "";
|
|
57
|
+
return scope.uri || scope.path || scope.id || scope.name || "";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeResourceScope(scope) {
|
|
61
|
+
let value = String(scopeValue(scope) || "").trim();
|
|
62
|
+
if (!value) return null;
|
|
63
|
+
value = value.replace(/\\/g, "/");
|
|
64
|
+
if (/^[A-Za-z]:\//.test(value)) {
|
|
65
|
+
value = `${value[0].toLowerCase()}${value.slice(1)}`;
|
|
66
|
+
}
|
|
67
|
+
return `resource:${value}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function normalizeNetworkScope(scope) {
|
|
71
|
+
const value = String(scopeValue(scope) || "").trim();
|
|
72
|
+
if (!value) return null;
|
|
73
|
+
try {
|
|
74
|
+
const url = new URL(value.includes("://") ? value : `https://${value}`);
|
|
75
|
+
if (!url.hostname) return null;
|
|
76
|
+
const authority = `${url.hostname.toLowerCase()}${url.port ? `:${url.port}` : ""}`;
|
|
77
|
+
return `network:${url.protocol}//${authority}`;
|
|
78
|
+
} catch {
|
|
79
|
+
const authority = value
|
|
80
|
+
.replace(/^.*@/, "")
|
|
81
|
+
.split(/[/?#]/, 1)[0]
|
|
82
|
+
.toLowerCase();
|
|
83
|
+
return authority ? `network:${authority}` : null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function normalizeScopes(scopes, normalize) {
|
|
88
|
+
if (!Array.isArray(scopes)) return [];
|
|
89
|
+
return [...new Set(scopes.map(normalize).filter(Boolean))].sort();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function serialReason(descriptor) {
|
|
93
|
+
if (!descriptor.hostOwned || descriptor.effectContract.trusted !== true) {
|
|
94
|
+
return McpConflictReason.UNTRUSTED_CONTRACT_SERIALIZED;
|
|
95
|
+
}
|
|
96
|
+
if (descriptor.effectContract.effect === McpEffect.UNKNOWN) {
|
|
97
|
+
return McpConflictReason.UNKNOWN_EFFECT_SERIALIZED;
|
|
98
|
+
}
|
|
99
|
+
if (
|
|
100
|
+
descriptor.effectContract.effect === McpEffect.WRITE ||
|
|
101
|
+
descriptor.effectContract.effect === McpEffect.DESTRUCTIVE ||
|
|
102
|
+
descriptor.effectContract.sideEffecting === true
|
|
103
|
+
) {
|
|
104
|
+
return McpConflictReason.MUTATING_EFFECT_SERIALIZED;
|
|
105
|
+
}
|
|
106
|
+
if (descriptor.effectContract.openWorld !== false) {
|
|
107
|
+
return McpConflictReason.OPEN_WORLD_SERIALIZED;
|
|
108
|
+
}
|
|
109
|
+
if (descriptor.scopeKeys.length === 0) {
|
|
110
|
+
return McpConflictReason.MISSING_SCOPE_SERIALIZED;
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Normalize one scheduler request without retaining arbitrary input payloads. */
|
|
116
|
+
export function normalizeMcpConcurrencyRequest(request = {}, options = {}) {
|
|
117
|
+
const rawContract = request.effectContract || {};
|
|
118
|
+
const isHostOwned =
|
|
119
|
+
options.isHostOwnedContract || isHostOwnedMcpEffectContract;
|
|
120
|
+
const effectContract = normalizeMcpEffectContract(rawContract);
|
|
121
|
+
const resourceScopeKeys = normalizeScopes(
|
|
122
|
+
request.resourceScopes,
|
|
123
|
+
normalizeResourceScope,
|
|
124
|
+
);
|
|
125
|
+
const networkScopeKeys = normalizeScopes(
|
|
126
|
+
request.networkScopes,
|
|
127
|
+
normalizeNetworkScope,
|
|
128
|
+
);
|
|
129
|
+
const scopeKeys = [...resourceScopeKeys, ...networkScopeKeys].sort();
|
|
130
|
+
const descriptor = {
|
|
131
|
+
schemaVersion: MCP_CONFLICT_SCHEDULER_SCHEMA_VERSION,
|
|
132
|
+
hostOwned: isHostOwned(rawContract) === true,
|
|
133
|
+
effectContract,
|
|
134
|
+
resourceScopeKeys,
|
|
135
|
+
networkScopeKeys,
|
|
136
|
+
scopeKeys,
|
|
137
|
+
};
|
|
138
|
+
descriptor.serialReason = serialReason(descriptor);
|
|
139
|
+
descriptor.parallelReadEligible = descriptor.serialReason === null;
|
|
140
|
+
descriptor.stableKey = `mcp-scope:${stableHash([
|
|
141
|
+
descriptor.hostOwned,
|
|
142
|
+
effectContract.effect,
|
|
143
|
+
effectContract.readOnly,
|
|
144
|
+
effectContract.sideEffecting,
|
|
145
|
+
effectContract.destructive,
|
|
146
|
+
effectContract.idempotent,
|
|
147
|
+
effectContract.openWorld,
|
|
148
|
+
effectContract.trusted,
|
|
149
|
+
...scopeKeys,
|
|
150
|
+
])}`;
|
|
151
|
+
|
|
152
|
+
Object.freeze(resourceScopeKeys);
|
|
153
|
+
Object.freeze(networkScopeKeys);
|
|
154
|
+
Object.freeze(scopeKeys);
|
|
155
|
+
Object.freeze(descriptor);
|
|
156
|
+
NORMALIZED_CONCURRENCY_REQUESTS.add(descriptor);
|
|
157
|
+
return descriptor;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function isNormalizedMcpConcurrencyRequest(value) {
|
|
161
|
+
return Boolean(
|
|
162
|
+
value &&
|
|
163
|
+
typeof value === "object" &&
|
|
164
|
+
NORMALIZED_CONCURRENCY_REQUESTS.has(value),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function sharedScopes(left, right) {
|
|
169
|
+
const rightScopes = new Set(right.scopeKeys);
|
|
170
|
+
return left.scopeKeys.filter((scope) => rightScopes.has(scope));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function mayWrite(descriptor) {
|
|
174
|
+
return (
|
|
175
|
+
descriptor.effectContract.effect !== McpEffect.READ ||
|
|
176
|
+
descriptor.effectContract.sideEffecting === true ||
|
|
177
|
+
descriptor.effectContract.destructive === true
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function reasonText(code, details = {}) {
|
|
182
|
+
switch (code) {
|
|
183
|
+
case McpConflictReason.SAME_SCOPE_MAY_WRITE:
|
|
184
|
+
return `Calls share ${details.sharedScopes.join(", ")} and at least one may write.`;
|
|
185
|
+
case McpConflictReason.MUTATING_EFFECT_SERIALIZED:
|
|
186
|
+
return "A write or destructive MCP effect is conservatively serialized.";
|
|
187
|
+
case McpConflictReason.UNKNOWN_EFFECT_SERIALIZED:
|
|
188
|
+
return "An unknown MCP effect is conservatively serialized.";
|
|
189
|
+
case McpConflictReason.UNTRUSTED_CONTRACT_SERIALIZED:
|
|
190
|
+
return "A non-host-owned or untrusted effect contract cannot authorize parallel execution.";
|
|
191
|
+
case McpConflictReason.OPEN_WORLD_SERIALIZED:
|
|
192
|
+
return "Open-world status was not explicitly ruled out by the host contract.";
|
|
193
|
+
case McpConflictReason.MISSING_SCOPE_SERIALIZED:
|
|
194
|
+
return "A scoped parallel read requires at least one resource or network scope.";
|
|
195
|
+
default:
|
|
196
|
+
return "Both calls are trusted, scoped, closed-world reads.";
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Explain whether two calls may overlap. Any non-read descriptor is a global
|
|
202
|
+
* serial barrier; shared-scope writes receive a more specific diagnostic.
|
|
203
|
+
*/
|
|
204
|
+
export function explainMcpConcurrency(leftRequest, rightRequest, options = {}) {
|
|
205
|
+
const left = isNormalizedMcpConcurrencyRequest(leftRequest)
|
|
206
|
+
? leftRequest
|
|
207
|
+
: normalizeMcpConcurrencyRequest(leftRequest, options);
|
|
208
|
+
const right = isNormalizedMcpConcurrencyRequest(rightRequest)
|
|
209
|
+
? rightRequest
|
|
210
|
+
: normalizeMcpConcurrencyRequest(rightRequest, options);
|
|
211
|
+
const overlap = sharedScopes(left, right);
|
|
212
|
+
let reasonCode = McpConflictReason.NONE;
|
|
213
|
+
|
|
214
|
+
if (overlap.length > 0 && (mayWrite(left) || mayWrite(right))) {
|
|
215
|
+
reasonCode = McpConflictReason.SAME_SCOPE_MAY_WRITE;
|
|
216
|
+
} else if (!left.parallelReadEligible) {
|
|
217
|
+
reasonCode = left.serialReason;
|
|
218
|
+
} else if (!right.parallelReadEligible) {
|
|
219
|
+
reasonCode = right.serialReason;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const conflict = reasonCode !== McpConflictReason.NONE;
|
|
223
|
+
const pairKeys = [left.stableKey, right.stableKey].sort();
|
|
224
|
+
return Object.freeze({
|
|
225
|
+
conflict,
|
|
226
|
+
canRunInParallel: !conflict,
|
|
227
|
+
reasonCode,
|
|
228
|
+
reason: reasonText(reasonCode, { sharedScopes: overlap }),
|
|
229
|
+
stableKey: `mcp-conflict:${stableHash(pairKeys)}`,
|
|
230
|
+
leftKey: left.stableKey,
|
|
231
|
+
rightKey: right.stableKey,
|
|
232
|
+
sharedScopes: Object.freeze(overlap),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export class McpSchedulerAbortError extends Error {
|
|
237
|
+
constructor(message = "MCP scheduler request aborted", details = {}) {
|
|
238
|
+
super(message, details.cause ? { cause: details.cause } : undefined);
|
|
239
|
+
this.name = "AbortError";
|
|
240
|
+
this.code = "CC_MCP_SCHEDULER_ABORTED";
|
|
241
|
+
this.requestId = details.requestId || null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function positiveInteger(value, fallback, name) {
|
|
246
|
+
if (value === undefined || value === null) return fallback;
|
|
247
|
+
const number = Number(value);
|
|
248
|
+
if (!Number.isInteger(number) || number < 1) {
|
|
249
|
+
throw new TypeError(`${name} must be a positive integer`);
|
|
250
|
+
}
|
|
251
|
+
return number;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Bounded FIFO conflict scheduler. Later reads never bypass an earlier queued
|
|
256
|
+
* write/unknown barrier, preventing a continuous read stream from starving it.
|
|
257
|
+
*/
|
|
258
|
+
export class McpConflictScheduler {
|
|
259
|
+
constructor(options = {}) {
|
|
260
|
+
this.maxActive = positiveInteger(options.maxActive, 16, "maxActive");
|
|
261
|
+
this.maxQueue = positiveInteger(options.maxQueue, 128, "maxQueue");
|
|
262
|
+
this._now = options.now || (() => new Date());
|
|
263
|
+
this._isHostOwnedContract =
|
|
264
|
+
options.isHostOwnedContract || isHostOwnedMcpEffectContract;
|
|
265
|
+
this._queue = [];
|
|
266
|
+
this._active = new Map();
|
|
267
|
+
this._sequence = 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
_timestamp() {
|
|
271
|
+
const value = this._now();
|
|
272
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
273
|
+
if (Number.isNaN(date.getTime())) {
|
|
274
|
+
throw new TypeError("MCP scheduler clock returned an invalid timestamp");
|
|
275
|
+
}
|
|
276
|
+
return date.toISOString();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
_nextRequestId() {
|
|
280
|
+
this._sequence += 1;
|
|
281
|
+
return `mcp-lock-${this._sequence.toString(36)}`;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
acquire(request = {}, options = {}) {
|
|
285
|
+
const requestId = this._nextRequestId();
|
|
286
|
+
const signal = options.signal || null;
|
|
287
|
+
if (signal?.aborted) {
|
|
288
|
+
return Promise.reject(
|
|
289
|
+
new McpSchedulerAbortError(
|
|
290
|
+
"MCP scheduler request was already aborted",
|
|
291
|
+
{
|
|
292
|
+
requestId,
|
|
293
|
+
cause: signal.reason,
|
|
294
|
+
},
|
|
295
|
+
),
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
if (this._queue.length >= this.maxQueue) {
|
|
299
|
+
const error = new Error(
|
|
300
|
+
`MCP scheduler queue is full (${this.maxQueue} waiting requests)`,
|
|
301
|
+
);
|
|
302
|
+
error.code = "CC_MCP_SCHEDULER_QUEUE_FULL";
|
|
303
|
+
error.requestId = requestId;
|
|
304
|
+
return Promise.reject(error);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const descriptor = normalizeMcpConcurrencyRequest(request, {
|
|
308
|
+
isHostOwnedContract: this._isHostOwnedContract,
|
|
309
|
+
});
|
|
310
|
+
return new Promise((resolve, reject) => {
|
|
311
|
+
const controller = new AbortController();
|
|
312
|
+
const entry = {
|
|
313
|
+
requestId,
|
|
314
|
+
descriptor,
|
|
315
|
+
controller,
|
|
316
|
+
resolve,
|
|
317
|
+
reject,
|
|
318
|
+
status: "queued",
|
|
319
|
+
queuedAt: this._timestamp(),
|
|
320
|
+
acquiredAt: null,
|
|
321
|
+
externalSignal: signal,
|
|
322
|
+
externalAbort: null,
|
|
323
|
+
};
|
|
324
|
+
if (signal) {
|
|
325
|
+
entry.externalAbort = () => {
|
|
326
|
+
this.abort(requestId, signal.reason);
|
|
327
|
+
};
|
|
328
|
+
signal.addEventListener("abort", entry.externalAbort, { once: true });
|
|
329
|
+
}
|
|
330
|
+
this._queue.push(entry);
|
|
331
|
+
this._drain();
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
_conflictsWithActive(descriptor) {
|
|
336
|
+
for (const active of this._active.values()) {
|
|
337
|
+
const explanation = explainMcpConcurrency(descriptor, active.descriptor, {
|
|
338
|
+
isHostOwnedContract: this._isHostOwnedContract,
|
|
339
|
+
});
|
|
340
|
+
if (explanation.conflict) return explanation;
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
_drain() {
|
|
346
|
+
while (this._queue.length > 0 && this._active.size < this.maxActive) {
|
|
347
|
+
// Strict head-of-line ordering is intentional: a queued writer/unknown
|
|
348
|
+
// barrier cannot be bypassed indefinitely by later compatible reads.
|
|
349
|
+
const entry = this._queue[0];
|
|
350
|
+
if (this._conflictsWithActive(entry.descriptor)) return;
|
|
351
|
+
this._queue.shift();
|
|
352
|
+
entry.status = "active";
|
|
353
|
+
entry.acquiredAt = this._timestamp();
|
|
354
|
+
this._active.set(entry.requestId, entry);
|
|
355
|
+
entry.resolve(this._createLease(entry));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
_createLease(entry) {
|
|
360
|
+
const scheduler = this;
|
|
361
|
+
return Object.freeze({
|
|
362
|
+
requestId: entry.requestId,
|
|
363
|
+
stableKey: entry.descriptor.stableKey,
|
|
364
|
+
descriptor: entry.descriptor,
|
|
365
|
+
acquiredAt: entry.acquiredAt,
|
|
366
|
+
signal: entry.controller.signal,
|
|
367
|
+
release() {
|
|
368
|
+
return scheduler.release(entry.requestId);
|
|
369
|
+
},
|
|
370
|
+
abort(reason) {
|
|
371
|
+
return scheduler.abort(entry.requestId, reason);
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
_detachExternalAbort(entry) {
|
|
377
|
+
if (entry?.externalSignal && entry.externalAbort) {
|
|
378
|
+
entry.externalSignal.removeEventListener("abort", entry.externalAbort);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
release(leaseOrRequestId) {
|
|
383
|
+
const requestId =
|
|
384
|
+
typeof leaseOrRequestId === "string"
|
|
385
|
+
? leaseOrRequestId
|
|
386
|
+
: leaseOrRequestId?.requestId;
|
|
387
|
+
const entry = this._active.get(String(requestId || ""));
|
|
388
|
+
if (!entry) return false;
|
|
389
|
+
this._active.delete(entry.requestId);
|
|
390
|
+
entry.status = "released";
|
|
391
|
+
this._detachExternalAbort(entry);
|
|
392
|
+
this._drain();
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
abort(leaseOrRequestId, reason = undefined) {
|
|
397
|
+
const requestId =
|
|
398
|
+
typeof leaseOrRequestId === "string"
|
|
399
|
+
? leaseOrRequestId
|
|
400
|
+
: leaseOrRequestId?.requestId;
|
|
401
|
+
const key = String(requestId || "");
|
|
402
|
+
const queuedIndex = this._queue.findIndex(
|
|
403
|
+
(entry) => entry.requestId === key,
|
|
404
|
+
);
|
|
405
|
+
if (queuedIndex >= 0) {
|
|
406
|
+
const [entry] = this._queue.splice(queuedIndex, 1);
|
|
407
|
+
entry.status = "aborted";
|
|
408
|
+
this._detachExternalAbort(entry);
|
|
409
|
+
entry.controller.abort(reason);
|
|
410
|
+
entry.reject(
|
|
411
|
+
new McpSchedulerAbortError("Queued MCP scheduler request aborted", {
|
|
412
|
+
requestId: entry.requestId,
|
|
413
|
+
cause: reason,
|
|
414
|
+
}),
|
|
415
|
+
);
|
|
416
|
+
this._drain();
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const active = this._active.get(key);
|
|
421
|
+
if (!active) return false;
|
|
422
|
+
if (active.controller.signal.aborted) return false;
|
|
423
|
+
// AbortSignal delivery is advisory. Keep the conflict lock active until
|
|
424
|
+
// the caller observes cancellation and releases in its finally block; an
|
|
425
|
+
// immediate unlock could overlap a new writer with work still winding down.
|
|
426
|
+
active.controller.abort(reason);
|
|
427
|
+
active.status = "aborting";
|
|
428
|
+
this._detachExternalAbort(active);
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
explain(left, right) {
|
|
433
|
+
return explainMcpConcurrency(left, right, {
|
|
434
|
+
isHostOwnedContract: this._isHostOwnedContract,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
snapshot() {
|
|
439
|
+
return Object.freeze({
|
|
440
|
+
active: this._active.size,
|
|
441
|
+
queued: this._queue.length,
|
|
442
|
+
maxActive: this.maxActive,
|
|
443
|
+
maxQueue: this.maxQueue,
|
|
444
|
+
activeRequests: Object.freeze(
|
|
445
|
+
[...this._active.values()].map((entry) =>
|
|
446
|
+
Object.freeze({
|
|
447
|
+
requestId: entry.requestId,
|
|
448
|
+
stableKey: entry.descriptor.stableKey,
|
|
449
|
+
acquiredAt: entry.acquiredAt,
|
|
450
|
+
}),
|
|
451
|
+
),
|
|
452
|
+
),
|
|
453
|
+
queuedRequests: Object.freeze(
|
|
454
|
+
this._queue.map((entry) =>
|
|
455
|
+
Object.freeze({
|
|
456
|
+
requestId: entry.requestId,
|
|
457
|
+
stableKey: entry.descriptor.stableKey,
|
|
458
|
+
queuedAt: entry.queuedAt,
|
|
459
|
+
}),
|
|
460
|
+
),
|
|
461
|
+
),
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export function createMcpConflictScheduler(options = {}) {
|
|
467
|
+
return new McpConflictScheduler(options);
|
|
468
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP effect contracts.
|
|
3
|
+
*
|
|
4
|
+
* MCP annotations are supplied by the server and remain hints. This module
|
|
5
|
+
* preserves them verbatim in a normalized shape and classifies only the
|
|
6
|
+
* server-declared effect. Source identity trust is recorded separately, but it
|
|
7
|
+
* never turns the declaration into a host authorization. Plan-mode authority
|
|
8
|
+
* requires a distinct host-owned per-tool policy that is intentionally not
|
|
9
|
+
* minted by this module.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const MCP_EFFECT_CONTRACT_VERSION = 1;
|
|
13
|
+
|
|
14
|
+
function explicitBoolean(...values) {
|
|
15
|
+
for (const value of values) {
|
|
16
|
+
if (typeof value === "boolean") return value;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function normalizeMcpToolAnnotations(tool = {}) {
|
|
22
|
+
const annotations =
|
|
23
|
+
tool?.annotations && typeof tool.annotations === "object"
|
|
24
|
+
? tool.annotations
|
|
25
|
+
: {};
|
|
26
|
+
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
readOnlyHint: explicitBoolean(
|
|
29
|
+
annotations.readOnlyHint,
|
|
30
|
+
annotations.read_only_hint,
|
|
31
|
+
tool.readOnlyHint,
|
|
32
|
+
tool.read_only_hint,
|
|
33
|
+
tool.isReadOnly,
|
|
34
|
+
tool.is_read_only,
|
|
35
|
+
),
|
|
36
|
+
destructiveHint: explicitBoolean(
|
|
37
|
+
annotations.destructiveHint,
|
|
38
|
+
annotations.destructive_hint,
|
|
39
|
+
tool.destructiveHint,
|
|
40
|
+
tool.destructive_hint,
|
|
41
|
+
),
|
|
42
|
+
idempotentHint: explicitBoolean(
|
|
43
|
+
annotations.idempotentHint,
|
|
44
|
+
annotations.idempotent_hint,
|
|
45
|
+
tool.idempotentHint,
|
|
46
|
+
tool.idempotent_hint,
|
|
47
|
+
),
|
|
48
|
+
openWorldHint: explicitBoolean(
|
|
49
|
+
annotations.openWorldHint,
|
|
50
|
+
annotations.open_world_hint,
|
|
51
|
+
tool.openWorldHint,
|
|
52
|
+
tool.open_world_hint,
|
|
53
|
+
),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function classifyMcpDeclaredEffect(annotations = {}) {
|
|
58
|
+
if (annotations.destructiveHint === true) return "destructive";
|
|
59
|
+
if (
|
|
60
|
+
annotations.readOnlyHint === true &&
|
|
61
|
+
annotations.destructiveHint !== true
|
|
62
|
+
) {
|
|
63
|
+
return "read";
|
|
64
|
+
}
|
|
65
|
+
if (annotations.readOnlyHint === false) return "write";
|
|
66
|
+
return "unknown";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function riskForEffect(effect, annotations) {
|
|
70
|
+
if (effect === "destructive") return "high";
|
|
71
|
+
if (effect === "read" && annotations.openWorldHint !== true) return "low";
|
|
72
|
+
return "medium";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function buildMcpEffectContract(
|
|
76
|
+
tool,
|
|
77
|
+
{ sourceTrusted = false, provenance = "untrusted-mcp-server" } = {},
|
|
78
|
+
) {
|
|
79
|
+
const annotations = normalizeMcpToolAnnotations(tool);
|
|
80
|
+
const declaredEffect = classifyMcpDeclaredEffect(annotations);
|
|
81
|
+
return Object.freeze({
|
|
82
|
+
version: MCP_EFFECT_CONTRACT_VERSION,
|
|
83
|
+
declaredEffect,
|
|
84
|
+
authorizedEffect: null,
|
|
85
|
+
riskLevel: riskForEffect(declaredEffect, annotations),
|
|
86
|
+
sourceTrusted: sourceTrusted === true,
|
|
87
|
+
provenance: String(provenance || "untrusted-mcp-server"),
|
|
88
|
+
annotations,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function mcpEffectDescriptorFields(tool, options = {}) {
|
|
93
|
+
const effectContract = buildMcpEffectContract(tool, options);
|
|
94
|
+
return {
|
|
95
|
+
// Presentation/scheduling hint only. Security gates must not consume this
|
|
96
|
+
// without an independent host authorization.
|
|
97
|
+
isReadOnly: effectContract.declaredEffect === "read",
|
|
98
|
+
riskLevel: effectContract.riskLevel,
|
|
99
|
+
mcpAnnotations: effectContract.annotations,
|
|
100
|
+
effectContract,
|
|
101
|
+
};
|
|
102
|
+
}
|