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
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
import { createHash, randomUUID as nodeRandomUUID } from "node:crypto";
|
|
2
|
+
import { isProxy } from "node:util/types";
|
|
3
|
+
|
|
4
|
+
export const MCP_CALL_LEDGER_SCHEMA_VERSION = 1;
|
|
5
|
+
|
|
6
|
+
export const MCP_CALL_LEDGER_PROTOCOL_LIMITS = Object.freeze({
|
|
7
|
+
identifier: 256,
|
|
8
|
+
ledgerId: 256,
|
|
9
|
+
errorName: 96,
|
|
10
|
+
errorCode: 128,
|
|
11
|
+
scopeCount: 64,
|
|
12
|
+
resourceScope: 1024,
|
|
13
|
+
networkScopeInput: 2048,
|
|
14
|
+
networkScope: 512,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const MCP_CALL_LEDGER_OUTPUT_KINDS = Object.freeze([
|
|
18
|
+
"array",
|
|
19
|
+
"bigint",
|
|
20
|
+
"boolean",
|
|
21
|
+
"buffer",
|
|
22
|
+
"function",
|
|
23
|
+
"null",
|
|
24
|
+
"number",
|
|
25
|
+
"object",
|
|
26
|
+
"string",
|
|
27
|
+
"symbol",
|
|
28
|
+
"undefined",
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
export const McpEffect = Object.freeze({
|
|
32
|
+
READ: "read",
|
|
33
|
+
UNKNOWN: "unknown",
|
|
34
|
+
WRITE: "write",
|
|
35
|
+
DESTRUCTIVE: "destructive",
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const McpCallStatus = Object.freeze({
|
|
39
|
+
STARTED: "started",
|
|
40
|
+
COMPLETED: "completed",
|
|
41
|
+
FAILED: "failed",
|
|
42
|
+
CANCELLED: "cancelled",
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export const LedgerFailureAction = Object.freeze({
|
|
46
|
+
FAIL_CLOSED: "fail-closed",
|
|
47
|
+
FAIL_OPEN: "fail-open",
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A missing prewrite is tolerable only for a host-classified read. Unknown
|
|
52
|
+
* effects stay fail-closed because an absent or incomplete MCP annotation is
|
|
53
|
+
* not proof that the tool is side-effect free. Write/destructive are hard-coded
|
|
54
|
+
* fail-closed and cannot be weakened through constructor options.
|
|
55
|
+
*/
|
|
56
|
+
export const DEFAULT_MCP_PREWRITE_FAILURE_POLICY = Object.freeze({
|
|
57
|
+
[McpEffect.READ]: LedgerFailureAction.FAIL_OPEN,
|
|
58
|
+
[McpEffect.UNKNOWN]: LedgerFailureAction.FAIL_CLOSED,
|
|
59
|
+
[McpEffect.WRITE]: LedgerFailureAction.FAIL_CLOSED,
|
|
60
|
+
[McpEffect.DESTRUCTIVE]: LedgerFailureAction.FAIL_CLOSED,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const EFFECTS = new Set(Object.values(McpEffect));
|
|
64
|
+
const TERMINAL_STATUSES = new Set([
|
|
65
|
+
McpCallStatus.COMPLETED,
|
|
66
|
+
McpCallStatus.FAILED,
|
|
67
|
+
McpCallStatus.CANCELLED,
|
|
68
|
+
]);
|
|
69
|
+
const FAILURE_ACTIONS = new Set(Object.values(LedgerFailureAction));
|
|
70
|
+
const MCP_WIRE_INPUT_SNAPSHOTS = new WeakSet();
|
|
71
|
+
|
|
72
|
+
export const MCP_WIRE_INPUT_INVALID_CODE = "CC_MCP_WIRE_INPUT_INVALID";
|
|
73
|
+
|
|
74
|
+
function invalidWireInput(reason) {
|
|
75
|
+
const error = new TypeError(
|
|
76
|
+
`MCP input must be unambiguous immutable JSON data (${reason})`,
|
|
77
|
+
);
|
|
78
|
+
error.code = MCP_WIRE_INPUT_INVALID_CODE;
|
|
79
|
+
return error;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function snapshotMcpJsonValue(value, stack) {
|
|
83
|
+
if (value === null) return null;
|
|
84
|
+
const type = typeof value;
|
|
85
|
+
if (type === "string" || type === "boolean") return value;
|
|
86
|
+
if (type === "number") {
|
|
87
|
+
if (!Number.isFinite(value)) throw invalidWireInput("non-finite-number");
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
if (type === "undefined") throw invalidWireInput("undefined");
|
|
91
|
+
if (type === "function") throw invalidWireInput("function");
|
|
92
|
+
if (type === "symbol") throw invalidWireInput("symbol");
|
|
93
|
+
if (type === "bigint") throw invalidWireInput("bigint");
|
|
94
|
+
if (type !== "object") throw invalidWireInput("unsupported-type");
|
|
95
|
+
|
|
96
|
+
if (isProxy(value)) throw invalidWireInput("proxy");
|
|
97
|
+
if (MCP_WIRE_INPUT_SNAPSHOTS.has(value)) return value;
|
|
98
|
+
if (stack.has(value)) throw invalidWireInput("circular-reference");
|
|
99
|
+
|
|
100
|
+
const prototype = Object.getPrototypeOf(value);
|
|
101
|
+
const isArray = Array.isArray(value);
|
|
102
|
+
if (
|
|
103
|
+
(!isArray && prototype !== Object.prototype && prototype !== null) ||
|
|
104
|
+
(isArray && prototype !== Array.prototype)
|
|
105
|
+
) {
|
|
106
|
+
throw invalidWireInput("non-plain-object");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
110
|
+
if (Object.getOwnPropertySymbols(value).length > 0) {
|
|
111
|
+
throw invalidWireInput("symbol-key");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
stack.add(value);
|
|
115
|
+
try {
|
|
116
|
+
if (isArray) {
|
|
117
|
+
const lengthDescriptor = descriptors.length;
|
|
118
|
+
const length = lengthDescriptor?.value;
|
|
119
|
+
if (!Number.isSafeInteger(length) || length < 0) {
|
|
120
|
+
throw invalidWireInput("array-length");
|
|
121
|
+
}
|
|
122
|
+
const keys = Object.keys(descriptors).filter((key) => key !== "length");
|
|
123
|
+
if (keys.length !== length) throw invalidWireInput("sparse-array");
|
|
124
|
+
const snapshot = new Array(length);
|
|
125
|
+
for (let index = 0; index < length; index += 1) {
|
|
126
|
+
const descriptor = descriptors[String(index)];
|
|
127
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
128
|
+
throw invalidWireInput("sparse-or-accessor-array");
|
|
129
|
+
}
|
|
130
|
+
snapshot[index] = snapshotMcpJsonValue(descriptor.value, stack);
|
|
131
|
+
}
|
|
132
|
+
MCP_WIRE_INPUT_SNAPSHOTS.add(snapshot);
|
|
133
|
+
return Object.freeze(snapshot);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const snapshot = {};
|
|
137
|
+
for (const key of Object.keys(descriptors).sort()) {
|
|
138
|
+
const descriptor = descriptors[key];
|
|
139
|
+
if (!("value" in descriptor)) throw invalidWireInput("accessor");
|
|
140
|
+
if (!descriptor.enumerable) throw invalidWireInput("hidden-property");
|
|
141
|
+
if (key === "toJSON") throw invalidWireInput("toJSON");
|
|
142
|
+
Object.defineProperty(snapshot, key, {
|
|
143
|
+
configurable: false,
|
|
144
|
+
enumerable: true,
|
|
145
|
+
writable: false,
|
|
146
|
+
value: snapshotMcpJsonValue(descriptor.value, stack),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
MCP_WIRE_INPUT_SNAPSHOTS.add(snapshot);
|
|
150
|
+
return Object.freeze(snapshot);
|
|
151
|
+
} finally {
|
|
152
|
+
stack.delete(value);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Capture the exact JSON-compatible value used for MCP admission, durable
|
|
158
|
+
* ledger identity and transport. Ambiguous JavaScript values are rejected
|
|
159
|
+
* before a network call instead of being hashed differently from their JSON
|
|
160
|
+
* wire representation.
|
|
161
|
+
*/
|
|
162
|
+
export function snapshotMcpJsonRpcInput(value) {
|
|
163
|
+
return snapshotMcpJsonValue(value, new Set());
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function isMcpJsonRpcInputSnapshot(value) {
|
|
167
|
+
return (
|
|
168
|
+
value === null ||
|
|
169
|
+
typeof value === "string" ||
|
|
170
|
+
typeof value === "boolean" ||
|
|
171
|
+
(typeof value === "number" && Number.isFinite(value)) ||
|
|
172
|
+
((typeof value === "object" || typeof value === "function") &&
|
|
173
|
+
value !== null &&
|
|
174
|
+
MCP_WIRE_INPUT_SNAPSHOTS.has(value))
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function canonicalStringify(value, stack = new Set()) {
|
|
179
|
+
if (value === null) return "null";
|
|
180
|
+
|
|
181
|
+
const type = typeof value;
|
|
182
|
+
if (type === "string" || type === "boolean") return JSON.stringify(value);
|
|
183
|
+
if (type === "number") {
|
|
184
|
+
return Number.isFinite(value)
|
|
185
|
+
? JSON.stringify(value)
|
|
186
|
+
: JSON.stringify(`[number:${String(value)}]`);
|
|
187
|
+
}
|
|
188
|
+
if (type === "bigint") return JSON.stringify(`[bigint:${String(value)}]`);
|
|
189
|
+
if (type === "undefined") return '"[undefined]"';
|
|
190
|
+
if (type === "function" || type === "symbol") {
|
|
191
|
+
return JSON.stringify(`[unsupported:${type}]`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (Buffer.isBuffer(value)) {
|
|
195
|
+
return JSON.stringify(`[buffer:${value.toString("base64")}]`);
|
|
196
|
+
}
|
|
197
|
+
if (ArrayBuffer.isView(value)) {
|
|
198
|
+
const bytes = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
199
|
+
return JSON.stringify(`[bytes:${bytes.toString("base64")}]`);
|
|
200
|
+
}
|
|
201
|
+
if (value instanceof Date) {
|
|
202
|
+
return JSON.stringify(`[date:${value.toISOString()}]`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (stack.has(value)) {
|
|
206
|
+
throw new TypeError("MCP ledger payload must not contain circular values");
|
|
207
|
+
}
|
|
208
|
+
stack.add(value);
|
|
209
|
+
try {
|
|
210
|
+
if (Array.isArray(value)) {
|
|
211
|
+
return `[${value.map((item) => canonicalStringify(item, stack)).join(",")}]`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const entries = Object.keys(value)
|
|
215
|
+
.sort()
|
|
216
|
+
.map(
|
|
217
|
+
(key) =>
|
|
218
|
+
`${JSON.stringify(key)}:${canonicalStringify(value[key], stack)}`,
|
|
219
|
+
);
|
|
220
|
+
return `{${entries.join(",")}}`;
|
|
221
|
+
} finally {
|
|
222
|
+
stack.delete(value);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function summarizeMcpPayload(value) {
|
|
227
|
+
const canonical = canonicalStringify(value);
|
|
228
|
+
return Object.freeze({
|
|
229
|
+
sha256: `sha256:${createHash("sha256").update(canonical).digest("hex")}`,
|
|
230
|
+
bytes: Buffer.byteLength(canonical, "utf8"),
|
|
231
|
+
kind:
|
|
232
|
+
value === null
|
|
233
|
+
? "null"
|
|
234
|
+
: Array.isArray(value)
|
|
235
|
+
? "array"
|
|
236
|
+
: Buffer.isBuffer(value)
|
|
237
|
+
? "buffer"
|
|
238
|
+
: typeof value,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Return a stable SHA-256 digest without retaining the source value. */
|
|
243
|
+
export function sha256PayloadDigest(value) {
|
|
244
|
+
return summarizeMcpPayload(value).sha256;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Content-free identity for one exact MCP invocation. The byte count is bound
|
|
249
|
+
* alongside the payload digest so every admission surface compares the same
|
|
250
|
+
* canonical input representation used by the durable ledger.
|
|
251
|
+
*/
|
|
252
|
+
export function computeMcpExactReplayDigest({
|
|
253
|
+
serverName,
|
|
254
|
+
toolName,
|
|
255
|
+
inputDigest,
|
|
256
|
+
inputBytes,
|
|
257
|
+
} = {}) {
|
|
258
|
+
return sha256PayloadDigest({
|
|
259
|
+
schemaVersion: 1,
|
|
260
|
+
serverName,
|
|
261
|
+
toolName,
|
|
262
|
+
inputDigest,
|
|
263
|
+
inputBytes,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function replaceProtocolControlCharacters(value) {
|
|
268
|
+
return [...value]
|
|
269
|
+
.map((character) => {
|
|
270
|
+
const code = character.charCodeAt(0);
|
|
271
|
+
return code <= 31 || (code >= 127 && code <= 159) ? " " : character;
|
|
272
|
+
})
|
|
273
|
+
.join("");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Canonicalize a bounded protocol label without retaining control bytes. */
|
|
277
|
+
export function normalizeMcpLedgerProtocolText(
|
|
278
|
+
value,
|
|
279
|
+
fallback = null,
|
|
280
|
+
maxLength = MCP_CALL_LEDGER_PROTOCOL_LIMITS.identifier,
|
|
281
|
+
) {
|
|
282
|
+
if (value === undefined || value === null || value === "") return fallback;
|
|
283
|
+
const text = replaceProtocolControlCharacters(String(value));
|
|
284
|
+
return text.trim().slice(0, maxLength).trim() || fallback;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function safeIdentifier(
|
|
288
|
+
value,
|
|
289
|
+
fallback = null,
|
|
290
|
+
maxLength = MCP_CALL_LEDGER_PROTOCOL_LIMITS.identifier,
|
|
291
|
+
) {
|
|
292
|
+
return normalizeMcpLedgerProtocolText(value, fallback, maxLength);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function nullableBoolean(value) {
|
|
296
|
+
return typeof value === "boolean" ? value : null;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Copy only the host-owned effect fields. Arbitrary server annotation payloads
|
|
301
|
+
* are deliberately not persisted here: the admission layer must first reduce
|
|
302
|
+
* them to this conservative contract.
|
|
303
|
+
*/
|
|
304
|
+
export function normalizeMcpEffectContract(contract = {}) {
|
|
305
|
+
if (typeof contract === "string") contract = { effect: contract };
|
|
306
|
+
const candidate = safeIdentifier(
|
|
307
|
+
contract.effect || contract.classification,
|
|
308
|
+
McpEffect.UNKNOWN,
|
|
309
|
+
32,
|
|
310
|
+
).toLowerCase();
|
|
311
|
+
let effect = EFFECTS.has(candidate) ? candidate : McpEffect.UNKNOWN;
|
|
312
|
+
const destructive =
|
|
313
|
+
effect === McpEffect.DESTRUCTIVE || contract.destructive === true;
|
|
314
|
+
const sideEffecting =
|
|
315
|
+
destructive ||
|
|
316
|
+
effect === McpEffect.WRITE ||
|
|
317
|
+
contract.sideEffecting === true;
|
|
318
|
+
if (destructive) effect = McpEffect.DESTRUCTIVE;
|
|
319
|
+
else if (sideEffecting && effect === McpEffect.READ) effect = McpEffect.WRITE;
|
|
320
|
+
|
|
321
|
+
return Object.freeze({
|
|
322
|
+
schemaVersion: MCP_CALL_LEDGER_SCHEMA_VERSION,
|
|
323
|
+
effect,
|
|
324
|
+
readOnly: effect === McpEffect.READ && !sideEffecting,
|
|
325
|
+
sideEffecting,
|
|
326
|
+
destructive,
|
|
327
|
+
idempotent: nullableBoolean(contract.idempotent),
|
|
328
|
+
openWorld: nullableBoolean(contract.openWorld),
|
|
329
|
+
trusted: contract.trusted === true,
|
|
330
|
+
source: safeIdentifier(
|
|
331
|
+
contract.source,
|
|
332
|
+
null,
|
|
333
|
+
MCP_CALL_LEDGER_PROTOCOL_LIMITS.identifier,
|
|
334
|
+
),
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function canonicalizeMcpResourceScopes(scopes) {
|
|
339
|
+
if (!Array.isArray(scopes)) return [];
|
|
340
|
+
const normalized = [];
|
|
341
|
+
const seen = new Set();
|
|
342
|
+
for (const scope of scopes) {
|
|
343
|
+
if (typeof scope !== "string") continue;
|
|
344
|
+
const value = safeIdentifier(
|
|
345
|
+
scope,
|
|
346
|
+
null,
|
|
347
|
+
MCP_CALL_LEDGER_PROTOCOL_LIMITS.resourceScope,
|
|
348
|
+
);
|
|
349
|
+
if (!value || seen.has(value)) continue;
|
|
350
|
+
seen.add(value);
|
|
351
|
+
normalized.push(value);
|
|
352
|
+
if (normalized.length >= MCP_CALL_LEDGER_PROTOCOL_LIMITS.scopeCount) break;
|
|
353
|
+
}
|
|
354
|
+
return normalized;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function normalizeNetworkScope(scope) {
|
|
358
|
+
if (typeof scope !== "string") return null;
|
|
359
|
+
const value = safeIdentifier(
|
|
360
|
+
scope,
|
|
361
|
+
null,
|
|
362
|
+
MCP_CALL_LEDGER_PROTOCOL_LIMITS.networkScopeInput,
|
|
363
|
+
);
|
|
364
|
+
if (!value) return null;
|
|
365
|
+
|
|
366
|
+
// Network ledger entries retain the authority target, never URL credentials,
|
|
367
|
+
// query strings, fragments, or path-carried request data.
|
|
368
|
+
try {
|
|
369
|
+
const url = new URL(value.includes("://") ? value : `https://${value}`);
|
|
370
|
+
if (!url.hostname) return null;
|
|
371
|
+
return safeIdentifier(
|
|
372
|
+
`${url.protocol}//${url.hostname.toLowerCase()}${url.port ? `:${url.port}` : ""}`,
|
|
373
|
+
null,
|
|
374
|
+
MCP_CALL_LEDGER_PROTOCOL_LIMITS.networkScope,
|
|
375
|
+
);
|
|
376
|
+
} catch {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function canonicalizeMcpNetworkScopes(scopes) {
|
|
382
|
+
if (!Array.isArray(scopes)) return [];
|
|
383
|
+
const normalized = [];
|
|
384
|
+
const seen = new Set();
|
|
385
|
+
for (const scope of scopes) {
|
|
386
|
+
const value = normalizeNetworkScope(scope);
|
|
387
|
+
if (!value || seen.has(value)) continue;
|
|
388
|
+
seen.add(value);
|
|
389
|
+
normalized.push(value);
|
|
390
|
+
if (normalized.length >= MCP_CALL_LEDGER_PROTOCOL_LIMITS.scopeCount) break;
|
|
391
|
+
}
|
|
392
|
+
return normalized;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function timestamp(now) {
|
|
396
|
+
const value = now();
|
|
397
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
398
|
+
if (Number.isNaN(date.getTime())) {
|
|
399
|
+
throw new TypeError("MCP ledger clock returned an invalid timestamp");
|
|
400
|
+
}
|
|
401
|
+
return date.toISOString();
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function cloneRecord(record) {
|
|
405
|
+
return JSON.parse(JSON.stringify(record));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function frozenSnapshot(record) {
|
|
409
|
+
const snapshot = cloneRecord(record);
|
|
410
|
+
for (const value of Object.values(snapshot)) {
|
|
411
|
+
if (value && typeof value === "object") Object.freeze(value);
|
|
412
|
+
}
|
|
413
|
+
return Object.freeze(snapshot);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function summarizeError(error) {
|
|
417
|
+
const message = String(error?.message || error || "unknown error");
|
|
418
|
+
return Object.freeze({
|
|
419
|
+
name: safeIdentifier(
|
|
420
|
+
error?.name,
|
|
421
|
+
"Error",
|
|
422
|
+
MCP_CALL_LEDGER_PROTOCOL_LIMITS.errorName,
|
|
423
|
+
),
|
|
424
|
+
code: safeIdentifier(
|
|
425
|
+
error?.code,
|
|
426
|
+
null,
|
|
427
|
+
MCP_CALL_LEDGER_PROTOCOL_LIMITS.errorCode,
|
|
428
|
+
),
|
|
429
|
+
messageDigest: sha256PayloadDigest(message),
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function normalizeStatus(outcome = {}) {
|
|
434
|
+
if (outcome.status) {
|
|
435
|
+
const status = String(outcome.status).toLowerCase();
|
|
436
|
+
if (!TERMINAL_STATUSES.has(status)) {
|
|
437
|
+
throw new TypeError(
|
|
438
|
+
`Invalid MCP ledger terminal status "${outcome.status}"`,
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
return status;
|
|
442
|
+
}
|
|
443
|
+
if (outcome.cancelled === true || outcome.error?.name === "AbortError") {
|
|
444
|
+
return McpCallStatus.CANCELLED;
|
|
445
|
+
}
|
|
446
|
+
return outcome.error ? McpCallStatus.FAILED : McpCallStatus.COMPLETED;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function resolveSinkWriter(sink) {
|
|
450
|
+
if (!sink) return null;
|
|
451
|
+
if (typeof sink === "function") return sink;
|
|
452
|
+
if (typeof sink.append === "function") return sink.append.bind(sink);
|
|
453
|
+
if (typeof sink.write === "function") return sink.write.bind(sink);
|
|
454
|
+
throw new TypeError(
|
|
455
|
+
"MCP call ledger sink must be a function, append(), or write()",
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export class McpCallLedgerPersistenceError extends Error {
|
|
460
|
+
constructor(message, details = {}) {
|
|
461
|
+
super(message, details.cause ? { cause: details.cause } : undefined);
|
|
462
|
+
this.name = "McpCallLedgerPersistenceError";
|
|
463
|
+
this.code = details.code || "CC_MCP_LEDGER_PERSIST_FAILED";
|
|
464
|
+
this.ledgerId = details.ledgerId || null;
|
|
465
|
+
this.effect = details.effect || McpEffect.UNKNOWN;
|
|
466
|
+
this.phase = details.phase || null;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Host-owned, append-friendly MCP call ledger.
|
|
472
|
+
*
|
|
473
|
+
* Typical call-site usage:
|
|
474
|
+
*
|
|
475
|
+
* const call = await ledger.begin({ ... });
|
|
476
|
+
* let outcome;
|
|
477
|
+
* try {
|
|
478
|
+
* const result = await mcpClient.callTool(...);
|
|
479
|
+
* outcome = { status: "completed", output: result };
|
|
480
|
+
* return result;
|
|
481
|
+
* } catch (error) {
|
|
482
|
+
* outcome = { error };
|
|
483
|
+
* throw error;
|
|
484
|
+
* } finally {
|
|
485
|
+
* await call.settle(outcome);
|
|
486
|
+
* }
|
|
487
|
+
*/
|
|
488
|
+
export class McpCallLedger {
|
|
489
|
+
constructor(options = {}) {
|
|
490
|
+
this._sink = resolveSinkWriter(options.sink || null);
|
|
491
|
+
this._now = options.now || (() => new Date());
|
|
492
|
+
this._randomUUID = options.randomUUID || nodeRandomUUID;
|
|
493
|
+
this._sequence = 0;
|
|
494
|
+
this._records = new Map();
|
|
495
|
+
|
|
496
|
+
const requested = options.prewriteFailurePolicy || {};
|
|
497
|
+
for (const [effect, action] of Object.entries(requested)) {
|
|
498
|
+
if (!EFFECTS.has(effect) || !FAILURE_ACTIONS.has(action)) {
|
|
499
|
+
throw new TypeError(
|
|
500
|
+
`Invalid MCP ledger prewrite policy: ${effect}=${action}`,
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
this.prewriteFailurePolicy = Object.freeze({
|
|
505
|
+
...DEFAULT_MCP_PREWRITE_FAILURE_POLICY,
|
|
506
|
+
[McpEffect.READ]:
|
|
507
|
+
requested[McpEffect.READ] ||
|
|
508
|
+
DEFAULT_MCP_PREWRITE_FAILURE_POLICY[McpEffect.READ],
|
|
509
|
+
[McpEffect.UNKNOWN]:
|
|
510
|
+
requested[McpEffect.UNKNOWN] ||
|
|
511
|
+
DEFAULT_MCP_PREWRITE_FAILURE_POLICY[McpEffect.UNKNOWN],
|
|
512
|
+
// Authority-bearing effects are never configurable to fail-open.
|
|
513
|
+
[McpEffect.WRITE]: LedgerFailureAction.FAIL_CLOSED,
|
|
514
|
+
[McpEffect.DESTRUCTIVE]: LedgerFailureAction.FAIL_CLOSED,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
async _persist(record, phase) {
|
|
519
|
+
if (!this._sink) return false;
|
|
520
|
+
await this._sink(frozenSnapshot(record), Object.freeze({ phase }));
|
|
521
|
+
return true;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
_nextLedgerId() {
|
|
525
|
+
this._sequence += 1;
|
|
526
|
+
const randomPart = safeIdentifier(this._randomUUID(), "uuid", 128).replace(
|
|
527
|
+
/[^a-zA-Z0-9_-]/g,
|
|
528
|
+
"-",
|
|
529
|
+
);
|
|
530
|
+
return `mcp-${randomPart}-${this._sequence.toString(36)}`;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
async begin(call = {}) {
|
|
534
|
+
const toolName = safeIdentifier(
|
|
535
|
+
call.toolName || call.tool,
|
|
536
|
+
null,
|
|
537
|
+
MCP_CALL_LEDGER_PROTOCOL_LIMITS.identifier,
|
|
538
|
+
);
|
|
539
|
+
const serverName = safeIdentifier(
|
|
540
|
+
call.serverName || call.server,
|
|
541
|
+
null,
|
|
542
|
+
MCP_CALL_LEDGER_PROTOCOL_LIMITS.identifier,
|
|
543
|
+
);
|
|
544
|
+
if (!toolName) throw new TypeError("MCP ledger begin requires toolName");
|
|
545
|
+
if (!serverName)
|
|
546
|
+
throw new TypeError("MCP ledger begin requires serverName");
|
|
547
|
+
|
|
548
|
+
const ledgerId = this._nextLedgerId();
|
|
549
|
+
const effectContract = normalizeMcpEffectContract(
|
|
550
|
+
call.effectContract || call.effect || {},
|
|
551
|
+
);
|
|
552
|
+
const inputSnapshot = snapshotMcpJsonRpcInput(
|
|
553
|
+
Object.prototype.hasOwnProperty.call(call, "input") ? call.input : {},
|
|
554
|
+
);
|
|
555
|
+
const input = summarizeMcpPayload(inputSnapshot);
|
|
556
|
+
const prewritePolicy =
|
|
557
|
+
this.prewriteFailurePolicy[effectContract.effect] ||
|
|
558
|
+
LedgerFailureAction.FAIL_CLOSED;
|
|
559
|
+
let record = {
|
|
560
|
+
schemaVersion: MCP_CALL_LEDGER_SCHEMA_VERSION,
|
|
561
|
+
ledgerId,
|
|
562
|
+
sessionId: safeIdentifier(
|
|
563
|
+
call.sessionId,
|
|
564
|
+
null,
|
|
565
|
+
MCP_CALL_LEDGER_PROTOCOL_LIMITS.identifier,
|
|
566
|
+
),
|
|
567
|
+
turnId: safeIdentifier(
|
|
568
|
+
call.turnId ?? call.turn,
|
|
569
|
+
null,
|
|
570
|
+
MCP_CALL_LEDGER_PROTOCOL_LIMITS.identifier,
|
|
571
|
+
),
|
|
572
|
+
toolName,
|
|
573
|
+
serverName,
|
|
574
|
+
inputDigest: input.sha256,
|
|
575
|
+
inputBytes: input.bytes,
|
|
576
|
+
effectContract,
|
|
577
|
+
resourceScopes: canonicalizeMcpResourceScopes(call.resourceScopes),
|
|
578
|
+
networkScopes: canonicalizeMcpNetworkScopes(call.networkScopes),
|
|
579
|
+
prewritePolicy,
|
|
580
|
+
prewritePersistence: this._sink ? "pending" : "memory-only",
|
|
581
|
+
status: McpCallStatus.STARTED,
|
|
582
|
+
startedAt: timestamp(this._now),
|
|
583
|
+
settledAt: null,
|
|
584
|
+
outputSummary: null,
|
|
585
|
+
outputDigest: null,
|
|
586
|
+
errorSummary: null,
|
|
587
|
+
};
|
|
588
|
+
this._records.set(ledgerId, record);
|
|
589
|
+
|
|
590
|
+
let prewritePersisted = false;
|
|
591
|
+
try {
|
|
592
|
+
prewritePersisted = await this._persist(record, "started");
|
|
593
|
+
if (prewritePersisted) {
|
|
594
|
+
record = { ...record, prewritePersistence: "persisted" };
|
|
595
|
+
this._records.set(ledgerId, record);
|
|
596
|
+
}
|
|
597
|
+
} catch (error) {
|
|
598
|
+
const persistenceError = summarizeError(error);
|
|
599
|
+
if (prewritePolicy === LedgerFailureAction.FAIL_CLOSED) {
|
|
600
|
+
record = {
|
|
601
|
+
...record,
|
|
602
|
+
status: McpCallStatus.FAILED,
|
|
603
|
+
settledAt: timestamp(this._now),
|
|
604
|
+
prewritePersistence: "failed-closed",
|
|
605
|
+
errorSummary: persistenceError,
|
|
606
|
+
};
|
|
607
|
+
this._records.set(ledgerId, record);
|
|
608
|
+
throw new McpCallLedgerPersistenceError(
|
|
609
|
+
"MCP call blocked because its ledger prewrite could not be persisted",
|
|
610
|
+
{
|
|
611
|
+
code: "CC_MCP_LEDGER_PREWRITE_FAILED",
|
|
612
|
+
ledgerId,
|
|
613
|
+
effect: effectContract.effect,
|
|
614
|
+
phase: "started",
|
|
615
|
+
cause: error,
|
|
616
|
+
},
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
record = {
|
|
621
|
+
...record,
|
|
622
|
+
prewritePersistence: "failed-open",
|
|
623
|
+
prewriteError: persistenceError,
|
|
624
|
+
};
|
|
625
|
+
this._records.set(ledgerId, record);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const ledger = this;
|
|
629
|
+
return Object.freeze({
|
|
630
|
+
ledgerId,
|
|
631
|
+
prewritePersisted,
|
|
632
|
+
prewritePolicy,
|
|
633
|
+
inputSnapshot,
|
|
634
|
+
record: frozenSnapshot(record),
|
|
635
|
+
settle(outcome = {}) {
|
|
636
|
+
return ledger.settle(ledgerId, outcome);
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
beginCall(call = {}) {
|
|
642
|
+
return this.begin(call);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
async settle(ticketOrLedgerId, outcome = {}) {
|
|
646
|
+
const ledgerId = safeIdentifier(
|
|
647
|
+
typeof ticketOrLedgerId === "string"
|
|
648
|
+
? ticketOrLedgerId
|
|
649
|
+
: ticketOrLedgerId?.ledgerId,
|
|
650
|
+
null,
|
|
651
|
+
MCP_CALL_LEDGER_PROTOCOL_LIMITS.ledgerId,
|
|
652
|
+
);
|
|
653
|
+
if (!ledgerId || !this._records.has(ledgerId)) {
|
|
654
|
+
throw new Error(`Unknown MCP ledger entry: ${ledgerId || "(missing)"}`);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const current = this._records.get(ledgerId);
|
|
658
|
+
const status = normalizeStatus(outcome);
|
|
659
|
+
if (TERMINAL_STATUSES.has(current.status)) {
|
|
660
|
+
if (current.status === status) return frozenSnapshot(current);
|
|
661
|
+
const error = new Error(
|
|
662
|
+
`MCP ledger entry ${ledgerId} is already settled as ${current.status}`,
|
|
663
|
+
);
|
|
664
|
+
error.code = "CC_MCP_LEDGER_ALREADY_SETTLED";
|
|
665
|
+
throw error;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const hasOutput =
|
|
669
|
+
Object.prototype.hasOwnProperty.call(outcome, "output") ||
|
|
670
|
+
Object.prototype.hasOwnProperty.call(outcome, "result");
|
|
671
|
+
const output = hasOutput
|
|
672
|
+
? summarizeMcpPayload(
|
|
673
|
+
Object.prototype.hasOwnProperty.call(outcome, "output")
|
|
674
|
+
? outcome.output
|
|
675
|
+
: outcome.result,
|
|
676
|
+
)
|
|
677
|
+
: null;
|
|
678
|
+
const errorSummary = outcome.error ? summarizeError(outcome.error) : null;
|
|
679
|
+
const settlementPersistence =
|
|
680
|
+
current.prewritePersistence === "failed-open"
|
|
681
|
+
? "skipped-no-prewrite"
|
|
682
|
+
: this._sink
|
|
683
|
+
? "pending"
|
|
684
|
+
: "memory-only";
|
|
685
|
+
let settled = {
|
|
686
|
+
...current,
|
|
687
|
+
status,
|
|
688
|
+
settledAt: timestamp(this._now),
|
|
689
|
+
outputSummary: output,
|
|
690
|
+
outputDigest: output?.sha256 || null,
|
|
691
|
+
errorSummary,
|
|
692
|
+
settlementPersistence,
|
|
693
|
+
};
|
|
694
|
+
this._records.set(ledgerId, settled);
|
|
695
|
+
|
|
696
|
+
// A failed-open call has no durable start. Appending a terminal record would
|
|
697
|
+
// create an orphan authority event that recovery must reject, so settlement
|
|
698
|
+
// remains an in-memory fact for this process only.
|
|
699
|
+
if (current.prewritePersistence === "failed-open") {
|
|
700
|
+
return frozenSnapshot(settled);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
try {
|
|
704
|
+
const persisted = await this._persist(settled, "settled");
|
|
705
|
+
if (persisted) {
|
|
706
|
+
settled = { ...settled, settlementPersistence: "persisted" };
|
|
707
|
+
this._records.set(ledgerId, settled);
|
|
708
|
+
}
|
|
709
|
+
} catch (error) {
|
|
710
|
+
settled = {
|
|
711
|
+
...settled,
|
|
712
|
+
settlementPersistence: "failed",
|
|
713
|
+
settlementError: summarizeError(error),
|
|
714
|
+
};
|
|
715
|
+
this._records.set(ledgerId, settled);
|
|
716
|
+
throw new McpCallLedgerPersistenceError(
|
|
717
|
+
"MCP call settled, but its ledger outcome could not be persisted",
|
|
718
|
+
{
|
|
719
|
+
code: "CC_MCP_LEDGER_SETTLE_FAILED",
|
|
720
|
+
ledgerId,
|
|
721
|
+
effect: current.effectContract.effect,
|
|
722
|
+
phase: "settled",
|
|
723
|
+
cause: error,
|
|
724
|
+
},
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
return frozenSnapshot(settled);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
settleCall(ticketOrLedgerId, outcome = {}) {
|
|
732
|
+
return this.settle(ticketOrLedgerId, outcome);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
get(ledgerId) {
|
|
736
|
+
const record = this._records.get(String(ledgerId || ""));
|
|
737
|
+
return record ? frozenSnapshot(record) : null;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
list() {
|
|
741
|
+
return [...this._records.values()].map(frozenSnapshot);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
export function createMcpCallLedger(options = {}) {
|
|
746
|
+
return new McpCallLedger(options);
|
|
747
|
+
}
|