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,689 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conservative production recovery for durable checkpoint restores.
|
|
3
|
+
*
|
|
4
|
+
* This first control-plane slice intentionally supports only two operations:
|
|
5
|
+
*
|
|
6
|
+
* - abort before a session intent or workspace mutation can have been issued;
|
|
7
|
+
* - release an exact terminal operation's retained workspace lock and archive
|
|
8
|
+
* its saga.
|
|
9
|
+
*
|
|
10
|
+
* Anything that may have crossed the session-intent or mutation boundary must
|
|
11
|
+
* be reconciled by a richer session/workspace recovery planner. Refusing those
|
|
12
|
+
* phases is important: a saga phase is a durable lower bound, not permission to
|
|
13
|
+
* guess that an external side effect did not happen.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
CheckpointRestoreSagaStore,
|
|
18
|
+
CHECKPOINT_RESTORE_SAGA_ERROR_CODES,
|
|
19
|
+
computeCheckpointRestoreWorkspaceLockOwnerDigest,
|
|
20
|
+
} from "./checkpoint-restore-saga.js";
|
|
21
|
+
import {
|
|
22
|
+
inspectWorkspaceLockOwnerSync,
|
|
23
|
+
withWorkspaceRecoveryLockSync,
|
|
24
|
+
} from "./process-execution-broker/workspace-transaction.js";
|
|
25
|
+
|
|
26
|
+
const HASH_PATTERN = /^sha256:[a-f0-9]{64}$/;
|
|
27
|
+
const RECOVERY_PHASES = new Set(["recovery_required", "recovery_started"]);
|
|
28
|
+
const SAFE_ABORT_BASE_PHASES = new Set(["created", "locked"]);
|
|
29
|
+
const TERMINAL_PHASES = new Set(["completed", "aborted", "rolled_back"]);
|
|
30
|
+
const CHECKPOINT_RESTORE_PURPOSE = "checkpoint-restore";
|
|
31
|
+
|
|
32
|
+
export const CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES = Object.freeze({
|
|
33
|
+
INVALID_ARGUMENT: "CHECKPOINT_RESTORE_RECOVERY_INVALID_ARGUMENT",
|
|
34
|
+
SAGA_CONFLICT: "CHECKPOINT_RESTORE_RECOVERY_SAGA_CONFLICT",
|
|
35
|
+
ACTION_NOT_ALLOWED: "CHECKPOINT_RESTORE_RECOVERY_ACTION_NOT_ALLOWED",
|
|
36
|
+
OWNER_CONFLICT: "CHECKPOINT_RESTORE_RECOVERY_OWNER_CONFLICT",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export class CheckpointRestoreRecoveryError extends Error {
|
|
40
|
+
constructor(code, message, details = {}, cause = null) {
|
|
41
|
+
super(message, cause ? { cause } : undefined);
|
|
42
|
+
this.name = "CheckpointRestoreRecoveryError";
|
|
43
|
+
this.code = code;
|
|
44
|
+
Object.assign(this, details);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function recoveryError(code, message, details = {}, cause = null) {
|
|
49
|
+
return new CheckpointRestoreRecoveryError(code, message, details, cause);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function canonicalJson(value) {
|
|
53
|
+
if (value === null) return "null";
|
|
54
|
+
if (typeof value === "string" || typeof value === "boolean") {
|
|
55
|
+
return JSON.stringify(value);
|
|
56
|
+
}
|
|
57
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
58
|
+
return JSON.stringify(value);
|
|
59
|
+
}
|
|
60
|
+
if (Array.isArray(value)) {
|
|
61
|
+
return `[${value.map((entry) => canonicalJson(entry)).join(",")}]`;
|
|
62
|
+
}
|
|
63
|
+
if (value && typeof value === "object") {
|
|
64
|
+
return `{${Object.keys(value)
|
|
65
|
+
.sort()
|
|
66
|
+
.map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`)
|
|
67
|
+
.join(",")}}`;
|
|
68
|
+
}
|
|
69
|
+
throw recoveryError(
|
|
70
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.INVALID_ARGUMENT,
|
|
71
|
+
"Checkpoint restore recovery evidence is not canonical JSON",
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function boundedText(value, fallback, maximum) {
|
|
76
|
+
let result = "";
|
|
77
|
+
for (const character of String(value || fallback)) {
|
|
78
|
+
const code = character.charCodeAt(0);
|
|
79
|
+
result += code < 32 || code === 127 ? " " : character;
|
|
80
|
+
}
|
|
81
|
+
return (result.trim() || fallback).slice(0, maximum);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function validateExpectedAuthority({
|
|
85
|
+
expectedSeq,
|
|
86
|
+
expectedHash,
|
|
87
|
+
expectedOwnerDigest,
|
|
88
|
+
}) {
|
|
89
|
+
if (
|
|
90
|
+
!Number.isSafeInteger(expectedSeq) ||
|
|
91
|
+
expectedSeq < 1 ||
|
|
92
|
+
typeof expectedHash !== "string" ||
|
|
93
|
+
!HASH_PATTERN.test(expectedHash) ||
|
|
94
|
+
!(
|
|
95
|
+
expectedOwnerDigest === null ||
|
|
96
|
+
(typeof expectedOwnerDigest === "string" &&
|
|
97
|
+
HASH_PATTERN.test(expectedOwnerDigest))
|
|
98
|
+
)
|
|
99
|
+
) {
|
|
100
|
+
throw recoveryError(
|
|
101
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.INVALID_ARGUMENT,
|
|
102
|
+
"Checkpoint restore recovery requires exact saga and observed-owner authority",
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function exactSaga(saga, operationId, expectedSeq, expectedHash) {
|
|
108
|
+
if (
|
|
109
|
+
!saga ||
|
|
110
|
+
saga.operationId !== operationId ||
|
|
111
|
+
saga.seq !== expectedSeq ||
|
|
112
|
+
saga.headHash !== expectedHash
|
|
113
|
+
) {
|
|
114
|
+
throw recoveryError(
|
|
115
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.SAGA_CONFLICT,
|
|
116
|
+
"Checkpoint restore saga changed after recovery preview",
|
|
117
|
+
{
|
|
118
|
+
operationId,
|
|
119
|
+
expectedSeq,
|
|
120
|
+
expectedHash,
|
|
121
|
+
actualSeq: saga?.seq ?? null,
|
|
122
|
+
actualHash: saga?.headHash ?? null,
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return saga;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function basePhase(saga) {
|
|
130
|
+
for (let index = saga.events.length - 1; index >= 0; index -= 1) {
|
|
131
|
+
const phase = saga.events[index]?.phase;
|
|
132
|
+
if (!RECOVERY_PHASES.has(phase)) return phase || null;
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function terminalSummary(value) {
|
|
138
|
+
return Object.freeze({
|
|
139
|
+
operationId: value.operationId,
|
|
140
|
+
phase: value.phase,
|
|
141
|
+
seq: value.seq,
|
|
142
|
+
headHash: value.headHash,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function archiveWarning(error) {
|
|
147
|
+
return Object.freeze({
|
|
148
|
+
code: boundedText(
|
|
149
|
+
error?.code,
|
|
150
|
+
"CHECKPOINT_RESTORE_SAGA_ARCHIVE_PENDING",
|
|
151
|
+
128,
|
|
152
|
+
),
|
|
153
|
+
message: boundedText(
|
|
154
|
+
error?.message,
|
|
155
|
+
"Checkpoint restore saga archive is pending",
|
|
156
|
+
512,
|
|
157
|
+
),
|
|
158
|
+
commitState:
|
|
159
|
+
typeof error?.commitState === "string"
|
|
160
|
+
? boundedText(error.commitState, "unknown", 128)
|
|
161
|
+
: null,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function processAlive(pid) {
|
|
166
|
+
try {
|
|
167
|
+
process.kill(pid, 0);
|
|
168
|
+
return true;
|
|
169
|
+
} catch (error) {
|
|
170
|
+
// A permission failure proves that the PID exists but is not observable.
|
|
171
|
+
// PID reuse is intentionally fail-closed and is therefore also "alive".
|
|
172
|
+
return error?.code === "EPERM";
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function propagateRecoveryDiagnostics(error) {
|
|
177
|
+
if (!error || typeof error !== "object") return error;
|
|
178
|
+
const nested = error.cause;
|
|
179
|
+
if (!nested || typeof nested !== "object") return error;
|
|
180
|
+
for (const field of [
|
|
181
|
+
"checkpointRestoreRecoveryCause",
|
|
182
|
+
"checkpointRestoreOperationId",
|
|
183
|
+
"checkpointRestoreSagaPhase",
|
|
184
|
+
"checkpointRestoreSagaSeq",
|
|
185
|
+
"checkpointRestoreSagaHeadHash",
|
|
186
|
+
"checkpointRestoreRecoveryRequired",
|
|
187
|
+
]) {
|
|
188
|
+
if (error[field] === undefined && nested[field] !== undefined) {
|
|
189
|
+
error[field] = nested[field];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return error;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The controller never keeps a saga store lock while acquiring a workspace or
|
|
197
|
+
* session authority. Store methods take short internal locks, and all calls
|
|
198
|
+
* made from a workspace callback therefore follow workspace -> saga order.
|
|
199
|
+
*/
|
|
200
|
+
export class CheckpointRestoreRecoveryController {
|
|
201
|
+
constructor(options = {}) {
|
|
202
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
203
|
+
throw recoveryError(
|
|
204
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.INVALID_ARGUMENT,
|
|
205
|
+
"Checkpoint restore recovery controller options must be an object",
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
if (
|
|
209
|
+
typeof options.workspaceRoot !== "string" ||
|
|
210
|
+
options.workspaceRoot.length === 0
|
|
211
|
+
) {
|
|
212
|
+
throw recoveryError(
|
|
213
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.INVALID_ARGUMENT,
|
|
214
|
+
"Checkpoint restore recovery requires a workspaceRoot",
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
this.workspaceRoot = options.workspaceRoot;
|
|
218
|
+
this.store =
|
|
219
|
+
options.store ||
|
|
220
|
+
new CheckpointRestoreSagaStore({
|
|
221
|
+
workspaceRoot: this.workspaceRoot,
|
|
222
|
+
});
|
|
223
|
+
this._inspectWorkspaceLockOwnerSync =
|
|
224
|
+
options.inspectWorkspaceLockOwnerSync || inspectWorkspaceLockOwnerSync;
|
|
225
|
+
this._withWorkspaceRecoveryLockSync =
|
|
226
|
+
options.withWorkspaceRecoveryLockSync || withWorkspaceRecoveryLockSync;
|
|
227
|
+
this._workspaceLockOwnerDigest =
|
|
228
|
+
options.computeWorkspaceLockOwnerDigest ||
|
|
229
|
+
computeCheckpointRestoreWorkspaceLockOwnerDigest;
|
|
230
|
+
this._isProcessAlive = options.isProcessAlive || processAlive;
|
|
231
|
+
this._workspaceLockOptions = Object.freeze({
|
|
232
|
+
...(options.workspaceLockOptions || {}),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
_lockOptions(operationId) {
|
|
237
|
+
return {
|
|
238
|
+
...this._workspaceLockOptions,
|
|
239
|
+
workspaceRoot: this.workspaceRoot,
|
|
240
|
+
operationId,
|
|
241
|
+
purpose: CHECKPOINT_RESTORE_PURPOSE,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
_loadExact(operationId, expectedSeq, expectedHash) {
|
|
246
|
+
return exactSaga(
|
|
247
|
+
this.store.load(operationId),
|
|
248
|
+
operationId,
|
|
249
|
+
expectedSeq,
|
|
250
|
+
expectedHash,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
_assertAbortable(saga) {
|
|
255
|
+
const sourcePhase = basePhase(saga);
|
|
256
|
+
if (!SAFE_ABORT_BASE_PHASES.has(sourcePhase) || saga.terminal) {
|
|
257
|
+
throw recoveryError(
|
|
258
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.ACTION_NOT_ALLOWED,
|
|
259
|
+
"Checkpoint restore can only be aborted before prepared session intent authority",
|
|
260
|
+
{
|
|
261
|
+
operationId: saga.operationId,
|
|
262
|
+
phase: saga.phase,
|
|
263
|
+
basePhase: sourcePhase,
|
|
264
|
+
},
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return sourcePhase;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
_assertTerminal(saga) {
|
|
271
|
+
if (!saga.terminal || !TERMINAL_PHASES.has(saga.phase)) {
|
|
272
|
+
throw recoveryError(
|
|
273
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.ACTION_NOT_ALLOWED,
|
|
274
|
+
"Only a terminal checkpoint restore can release recovery authority",
|
|
275
|
+
{
|
|
276
|
+
operationId: saga.operationId,
|
|
277
|
+
phase: saga.phase,
|
|
278
|
+
basePhase: basePhase(saga),
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
return saga;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
_advanceExact(saga, phase, evidence) {
|
|
286
|
+
try {
|
|
287
|
+
return this.store.advance(saga.operationId, {
|
|
288
|
+
expectedSeq: saga.seq,
|
|
289
|
+
expectedHash: saga.headHash,
|
|
290
|
+
phase,
|
|
291
|
+
evidence,
|
|
292
|
+
});
|
|
293
|
+
} catch (error) {
|
|
294
|
+
// Event publication and HEAD replacement are separate durable renames.
|
|
295
|
+
// Reconcile only the exact intended successor; never blind-retry a CAS.
|
|
296
|
+
try {
|
|
297
|
+
const observed = this.store.load(saga.operationId);
|
|
298
|
+
const latest = observed.events.at(-1);
|
|
299
|
+
if (
|
|
300
|
+
observed.seq === saga.seq + 1 &&
|
|
301
|
+
observed.phase === phase &&
|
|
302
|
+
latest?.prevHash === saga.headHash &&
|
|
303
|
+
canonicalJson(latest.evidence) === canonicalJson(evidence)
|
|
304
|
+
) {
|
|
305
|
+
return observed;
|
|
306
|
+
}
|
|
307
|
+
} catch (loadError) {
|
|
308
|
+
if (error && typeof error === "object") {
|
|
309
|
+
error.checkpointRestoreRecoveryLoadError = loadError;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
_inspectBoundOwner(operationId, expectedOwnerDigest) {
|
|
317
|
+
const owner = this._inspectWorkspaceLockOwnerSync(
|
|
318
|
+
this._lockOptions(operationId),
|
|
319
|
+
);
|
|
320
|
+
if (owner === null) {
|
|
321
|
+
if (expectedOwnerDigest !== null) {
|
|
322
|
+
throw recoveryError(
|
|
323
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.OWNER_CONFLICT,
|
|
324
|
+
"Checkpoint restore workspace owner disappeared after recovery preview",
|
|
325
|
+
{ operationId, expectedOwnerDigest, actualOwnerDigest: null },
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
const actualOwnerDigest = this._workspaceLockOwnerDigest(owner);
|
|
331
|
+
if (actualOwnerDigest !== expectedOwnerDigest) {
|
|
332
|
+
throw recoveryError(
|
|
333
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.OWNER_CONFLICT,
|
|
334
|
+
"Checkpoint restore workspace owner changed after recovery preview",
|
|
335
|
+
{ operationId, expectedOwnerDigest, actualOwnerDigest },
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
return owner;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
_withBoundWorkspaceAuthority(operationId, expectedOwnerDigest, callback) {
|
|
342
|
+
const observedOwner = this._inspectBoundOwner(
|
|
343
|
+
operationId,
|
|
344
|
+
expectedOwnerDigest,
|
|
345
|
+
);
|
|
346
|
+
if (!observedOwner) {
|
|
347
|
+
throw recoveryError(
|
|
348
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.OWNER_CONFLICT,
|
|
349
|
+
"Checkpoint restore has no retained workspace owner to take over",
|
|
350
|
+
{ operationId, expectedOwnerDigest: null, actualOwnerDigest: null },
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
return this._withWorkspaceRecoveryLockSync(
|
|
354
|
+
{
|
|
355
|
+
...this._lockOptions(operationId),
|
|
356
|
+
expectedOwner: observedOwner,
|
|
357
|
+
},
|
|
358
|
+
callback,
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
_abortCreatedWithoutPublishedOwner(
|
|
363
|
+
saga,
|
|
364
|
+
expectedOwnerDigest,
|
|
365
|
+
normalizedReason,
|
|
366
|
+
) {
|
|
367
|
+
if (expectedOwnerDigest !== null || saga.phase !== "created") return null;
|
|
368
|
+
const created = saga.events[0];
|
|
369
|
+
const actorPid = created?.evidence?.actorPid;
|
|
370
|
+
if (!Number.isSafeInteger(actorPid) || actorPid <= 0) {
|
|
371
|
+
throw recoveryError(
|
|
372
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.ACTION_NOT_ALLOWED,
|
|
373
|
+
"Lock-free created recovery requires the durable initiator PID",
|
|
374
|
+
{ operationId: saga.operationId, phase: saga.phase },
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
if (this._isProcessAlive(actorPid)) {
|
|
378
|
+
throw recoveryError(
|
|
379
|
+
CHECKPOINT_RESTORE_RECOVERY_ERROR_CODES.OWNER_CONFLICT,
|
|
380
|
+
"Checkpoint restore initiator is still live",
|
|
381
|
+
{ operationId: saga.operationId, actorPid },
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
// Re-read absence immediately before the saga CAS. Once the initiator is
|
|
385
|
+
// dead, no original process can publish a later workspace lock. Competing
|
|
386
|
+
// recovery callers are serialized by the exact saga head CAS.
|
|
387
|
+
this._inspectBoundOwner(saga.operationId, null);
|
|
388
|
+
const aborted = this._advanceExact(saga, "aborted", {
|
|
389
|
+
recoveryAction: "abort-before-lock",
|
|
390
|
+
reason: normalizedReason,
|
|
391
|
+
});
|
|
392
|
+
const archive = this._archiveTerminal(aborted);
|
|
393
|
+
return Object.freeze({
|
|
394
|
+
ok: true,
|
|
395
|
+
action: "abort",
|
|
396
|
+
...terminalSummary(aborted),
|
|
397
|
+
reconciledFromError: false,
|
|
398
|
+
...archive,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
_archiveTerminal(saga) {
|
|
403
|
+
try {
|
|
404
|
+
const archived = this.store.archiveTerminal(saga.operationId, {
|
|
405
|
+
expectedSeq: saga.seq,
|
|
406
|
+
expectedHash: saga.headHash,
|
|
407
|
+
});
|
|
408
|
+
return Object.freeze({
|
|
409
|
+
archived: true,
|
|
410
|
+
alreadyArchived: archived.alreadyArchived === true,
|
|
411
|
+
warning: null,
|
|
412
|
+
});
|
|
413
|
+
} catch (error) {
|
|
414
|
+
return Object.freeze({
|
|
415
|
+
archived: false,
|
|
416
|
+
alreadyArchived: false,
|
|
417
|
+
warning: archiveWarning(error),
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
_retainAbortFailure(operationError, lease) {
|
|
423
|
+
let observed = null;
|
|
424
|
+
try {
|
|
425
|
+
observed = this.store.load(lease.recoveryOfOperationId);
|
|
426
|
+
} catch (loadError) {
|
|
427
|
+
if (operationError && typeof operationError === "object") {
|
|
428
|
+
operationError.checkpointRestoreRecoveryLoadError = loadError;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (observed?.terminal) return observed;
|
|
433
|
+
|
|
434
|
+
if (observed && observed.phase !== "recovery_required") {
|
|
435
|
+
try {
|
|
436
|
+
observed = this._advanceExact(observed, "recovery_required", {
|
|
437
|
+
reason: boundedText(
|
|
438
|
+
operationError?.message,
|
|
439
|
+
"Checkpoint restore abort requires another recovery attempt",
|
|
440
|
+
2_048,
|
|
441
|
+
),
|
|
442
|
+
errorCode: boundedText(
|
|
443
|
+
operationError?.code,
|
|
444
|
+
"CHECKPOINT_RESTORE_RECOVERY_ABORT_FAILED",
|
|
445
|
+
128,
|
|
446
|
+
),
|
|
447
|
+
});
|
|
448
|
+
} catch (journalError) {
|
|
449
|
+
if (operationError && typeof operationError === "object") {
|
|
450
|
+
operationError.checkpointRestoreRecoveryJournalError = journalError;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
try {
|
|
456
|
+
lease.retainForRecovery(
|
|
457
|
+
boundedText(
|
|
458
|
+
`${operationError?.code || "CHECKPOINT_RESTORE_RECOVERY_ABORT_FAILED"}: ${operationError?.message || "abort outcome is unknown"}`,
|
|
459
|
+
"Checkpoint restore abort outcome is unknown",
|
|
460
|
+
512,
|
|
461
|
+
),
|
|
462
|
+
);
|
|
463
|
+
} catch (retentionError) {
|
|
464
|
+
if (retentionError && typeof retentionError === "object") {
|
|
465
|
+
retentionError.checkpointRestoreRecoveryCause = operationError;
|
|
466
|
+
retentionError.checkpointRestoreOperationId =
|
|
467
|
+
lease.recoveryOfOperationId;
|
|
468
|
+
retentionError.checkpointRestoreSagaPhase = observed?.phase || null;
|
|
469
|
+
retentionError.checkpointRestoreSagaSeq = observed?.seq || null;
|
|
470
|
+
retentionError.checkpointRestoreSagaHeadHash =
|
|
471
|
+
observed?.headHash || null;
|
|
472
|
+
retentionError.checkpointRestoreRecoveryRequired = true;
|
|
473
|
+
}
|
|
474
|
+
throw retentionError;
|
|
475
|
+
}
|
|
476
|
+
throw operationError;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
abort(
|
|
480
|
+
operationId,
|
|
481
|
+
{
|
|
482
|
+
expectedSeq,
|
|
483
|
+
expectedHash,
|
|
484
|
+
expectedOwnerDigest,
|
|
485
|
+
reason = "Operator aborted checkpoint restore before durable intent",
|
|
486
|
+
} = {},
|
|
487
|
+
) {
|
|
488
|
+
validateExpectedAuthority({
|
|
489
|
+
expectedSeq,
|
|
490
|
+
expectedHash,
|
|
491
|
+
expectedOwnerDigest,
|
|
492
|
+
});
|
|
493
|
+
const previewSaga = this._loadExact(operationId, expectedSeq, expectedHash);
|
|
494
|
+
this._assertAbortable(previewSaga);
|
|
495
|
+
const normalizedReason = boundedText(
|
|
496
|
+
reason,
|
|
497
|
+
"Operator aborted checkpoint restore before durable intent",
|
|
498
|
+
2_048,
|
|
499
|
+
);
|
|
500
|
+
const lockFreeAbort = this._abortCreatedWithoutPublishedOwner(
|
|
501
|
+
previewSaga,
|
|
502
|
+
expectedOwnerDigest,
|
|
503
|
+
normalizedReason,
|
|
504
|
+
);
|
|
505
|
+
if (lockFreeAbort) return lockFreeAbort;
|
|
506
|
+
|
|
507
|
+
let settled;
|
|
508
|
+
try {
|
|
509
|
+
settled = this._withBoundWorkspaceAuthority(
|
|
510
|
+
operationId,
|
|
511
|
+
expectedOwnerDigest,
|
|
512
|
+
(lease) => {
|
|
513
|
+
try {
|
|
514
|
+
lease.assertOwned();
|
|
515
|
+
let saga = this._loadExact(operationId, expectedSeq, expectedHash);
|
|
516
|
+
this._assertAbortable(saga);
|
|
517
|
+
|
|
518
|
+
if (saga.phase !== "recovery_required") {
|
|
519
|
+
saga = this._advanceExact(saga, "recovery_required", {
|
|
520
|
+
reason:
|
|
521
|
+
"Checkpoint restore was interrupted before durable intent",
|
|
522
|
+
errorCode: "CHECKPOINT_RESTORE_RECOVERY_ABORT_REQUESTED",
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
lease.assertOwned();
|
|
526
|
+
const recoveryStartedEvidence = {
|
|
527
|
+
workspaceLockOwner: lease.owner,
|
|
528
|
+
lockOwnerDigest: this._workspaceLockOwnerDigest(lease.owner),
|
|
529
|
+
recoveryAction: "abort-pre-intent",
|
|
530
|
+
};
|
|
531
|
+
saga = this._advanceExact(
|
|
532
|
+
saga,
|
|
533
|
+
"recovery_started",
|
|
534
|
+
recoveryStartedEvidence,
|
|
535
|
+
);
|
|
536
|
+
lease.assertOwned();
|
|
537
|
+
saga = this._advanceExact(saga, "aborted", {
|
|
538
|
+
recoveryAction: "abort-pre-intent",
|
|
539
|
+
reason: normalizedReason,
|
|
540
|
+
});
|
|
541
|
+
lease.assertOwned();
|
|
542
|
+
return Object.freeze({ saga, reconciledFromError: false });
|
|
543
|
+
} catch (error) {
|
|
544
|
+
const saga = this._retainAbortFailure(error, lease);
|
|
545
|
+
return Object.freeze({ saga, reconciledFromError: true });
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
);
|
|
549
|
+
} catch (error) {
|
|
550
|
+
throw propagateRecoveryDiagnostics(error);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const archive = this._archiveTerminal(settled.saga);
|
|
554
|
+
return Object.freeze({
|
|
555
|
+
ok: true,
|
|
556
|
+
action: "abort",
|
|
557
|
+
...terminalSummary(settled.saga),
|
|
558
|
+
reconciledFromError: settled.reconciledFromError,
|
|
559
|
+
...archive,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
_readTerminalRetention(operationId, expectedSeq, expectedHash) {
|
|
564
|
+
try {
|
|
565
|
+
return Object.freeze({
|
|
566
|
+
saga: this._assertTerminal(
|
|
567
|
+
this._loadExact(operationId, expectedSeq, expectedHash),
|
|
568
|
+
),
|
|
569
|
+
alreadyArchived: false,
|
|
570
|
+
});
|
|
571
|
+
} catch (error) {
|
|
572
|
+
if (error?.code !== CHECKPOINT_RESTORE_SAGA_ERROR_CODES.NOT_FOUND) {
|
|
573
|
+
throw error;
|
|
574
|
+
}
|
|
575
|
+
// `archiveTerminal` is an exact, idempotent retained-state read when the
|
|
576
|
+
// active operation is absent and the requested archive already exists.
|
|
577
|
+
const archived = this.store.archiveTerminal(operationId, {
|
|
578
|
+
expectedSeq,
|
|
579
|
+
expectedHash,
|
|
580
|
+
});
|
|
581
|
+
return Object.freeze({
|
|
582
|
+
saga: Object.freeze({
|
|
583
|
+
operationId,
|
|
584
|
+
phase: archived.phase,
|
|
585
|
+
seq: archived.seq,
|
|
586
|
+
headHash: archived.headHash,
|
|
587
|
+
terminal: true,
|
|
588
|
+
}),
|
|
589
|
+
alreadyArchived: archived.alreadyArchived === true,
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
_retainTerminalVerificationFailure(operationError, lease) {
|
|
595
|
+
try {
|
|
596
|
+
lease.retainForRecovery(
|
|
597
|
+
boundedText(
|
|
598
|
+
`${operationError?.code || "CHECKPOINT_RESTORE_TERMINAL_VERIFY_FAILED"}: ${operationError?.message || "terminal authority is unknown"}`,
|
|
599
|
+
"Checkpoint restore terminal authority is unknown",
|
|
600
|
+
512,
|
|
601
|
+
),
|
|
602
|
+
);
|
|
603
|
+
} catch (retentionError) {
|
|
604
|
+
if (retentionError && typeof retentionError === "object") {
|
|
605
|
+
retentionError.checkpointRestoreRecoveryCause = operationError;
|
|
606
|
+
retentionError.checkpointRestoreOperationId =
|
|
607
|
+
lease.recoveryOfOperationId;
|
|
608
|
+
retentionError.checkpointRestoreRecoveryRequired = true;
|
|
609
|
+
}
|
|
610
|
+
throw retentionError;
|
|
611
|
+
}
|
|
612
|
+
throw operationError;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
release(
|
|
616
|
+
operationId,
|
|
617
|
+
{ expectedSeq, expectedHash, expectedOwnerDigest } = {},
|
|
618
|
+
) {
|
|
619
|
+
validateExpectedAuthority({
|
|
620
|
+
expectedSeq,
|
|
621
|
+
expectedHash,
|
|
622
|
+
expectedOwnerDigest,
|
|
623
|
+
});
|
|
624
|
+
const preview = this._readTerminalRetention(
|
|
625
|
+
operationId,
|
|
626
|
+
expectedSeq,
|
|
627
|
+
expectedHash,
|
|
628
|
+
);
|
|
629
|
+
this._assertTerminal(preview.saga);
|
|
630
|
+
|
|
631
|
+
const observedOwner = this._inspectBoundOwner(
|
|
632
|
+
operationId,
|
|
633
|
+
expectedOwnerDigest,
|
|
634
|
+
);
|
|
635
|
+
if (!observedOwner) {
|
|
636
|
+
const archive = preview.alreadyArchived
|
|
637
|
+
? Object.freeze({
|
|
638
|
+
archived: true,
|
|
639
|
+
alreadyArchived: true,
|
|
640
|
+
warning: null,
|
|
641
|
+
})
|
|
642
|
+
: this._archiveTerminal(preview.saga);
|
|
643
|
+
return Object.freeze({
|
|
644
|
+
ok: true,
|
|
645
|
+
action: "release",
|
|
646
|
+
...terminalSummary(preview.saga),
|
|
647
|
+
...archive,
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const settled = this._withBoundWorkspaceAuthority(
|
|
652
|
+
operationId,
|
|
653
|
+
expectedOwnerDigest,
|
|
654
|
+
(lease) => {
|
|
655
|
+
try {
|
|
656
|
+
lease.assertOwned();
|
|
657
|
+
const retained = this._readTerminalRetention(
|
|
658
|
+
operationId,
|
|
659
|
+
expectedSeq,
|
|
660
|
+
expectedHash,
|
|
661
|
+
);
|
|
662
|
+
this._assertTerminal(retained.saga);
|
|
663
|
+
lease.assertOwned();
|
|
664
|
+
return retained;
|
|
665
|
+
} catch (error) {
|
|
666
|
+
return this._retainTerminalVerificationFailure(error, lease);
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
);
|
|
670
|
+
|
|
671
|
+
const archive = settled.alreadyArchived
|
|
672
|
+
? Object.freeze({
|
|
673
|
+
archived: true,
|
|
674
|
+
alreadyArchived: true,
|
|
675
|
+
warning: null,
|
|
676
|
+
})
|
|
677
|
+
: this._archiveTerminal(settled.saga);
|
|
678
|
+
return Object.freeze({
|
|
679
|
+
ok: true,
|
|
680
|
+
action: "release",
|
|
681
|
+
...terminalSummary(settled.saga),
|
|
682
|
+
...archive,
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
export function createCheckpointRestoreRecoveryController(options = {}) {
|
|
688
|
+
return new CheckpointRestoreRecoveryController(options);
|
|
689
|
+
}
|