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
|
@@ -20,8 +20,106 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import chalk from "chalk";
|
|
23
|
+
import { createHash } from "node:crypto";
|
|
23
24
|
import { resolve } from "path";
|
|
25
|
+
import {
|
|
26
|
+
buildCheckpointTimeline,
|
|
27
|
+
projectCheckpointTimeline,
|
|
28
|
+
} from "../lib/checkpoint-timeline.js";
|
|
29
|
+
import {
|
|
30
|
+
CHECKPOINT_TIMELINE_AUDIT_EVENT,
|
|
31
|
+
CHECKPOINT_TIMELINE_INTENT_EVENT,
|
|
32
|
+
CHECKPOINT_TIMELINE_RESULT_SCHEMA,
|
|
33
|
+
CHECKPOINT_TIMELINE_RESULT_VERSION,
|
|
34
|
+
checkpointTimelineConfirmationsMatch,
|
|
35
|
+
planCheckpointTimelineAction,
|
|
36
|
+
timelineActionError,
|
|
37
|
+
validateCheckpointTimelineConfirmationSubmission,
|
|
38
|
+
validateCheckpointTimelineSubmission,
|
|
39
|
+
} from "../lib/checkpoint-timeline-authority.js";
|
|
24
40
|
import { logger } from "../lib/logger.js";
|
|
41
|
+
import {
|
|
42
|
+
loadTurnBindingLog,
|
|
43
|
+
TURN_BINDING_TIMELINE_EVENT,
|
|
44
|
+
} from "../lib/turn-binding-store.js";
|
|
45
|
+
import {
|
|
46
|
+
createBranchSession,
|
|
47
|
+
findLatestEvent,
|
|
48
|
+
readVerifiedMessages,
|
|
49
|
+
withSessionAuthorityTransaction,
|
|
50
|
+
} from "../harness/jsonl-session-store.js";
|
|
51
|
+
import { withWorkspaceLockSync as withCanonicalWorkspaceLockSync } from "../lib/process-execution-broker/workspace-transaction.js";
|
|
52
|
+
import {
|
|
53
|
+
CheckpointRestoreSagaStore,
|
|
54
|
+
computeCheckpointRestoreWorkspaceLockOwnerDigest,
|
|
55
|
+
} from "../lib/checkpoint-restore-saga.js";
|
|
56
|
+
import {
|
|
57
|
+
computeCheckpointRestoreDigest,
|
|
58
|
+
runCheckpointRestoreOperation,
|
|
59
|
+
} from "../lib/checkpoint-restore-orchestrator.js";
|
|
60
|
+
import { registerManagedCheckpointCommands } from "./checkpoint-managed.js";
|
|
61
|
+
import { registerCheckpointRestoreRecoveryCommands } from "./checkpoint-restore-recovery.js";
|
|
62
|
+
|
|
63
|
+
function stableCheckpointRestoreValue(value) {
|
|
64
|
+
if (value === undefined) return "null";
|
|
65
|
+
if (
|
|
66
|
+
value === null ||
|
|
67
|
+
typeof value === "string" ||
|
|
68
|
+
typeof value === "boolean"
|
|
69
|
+
) {
|
|
70
|
+
return JSON.stringify(value);
|
|
71
|
+
}
|
|
72
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
73
|
+
return JSON.stringify(value);
|
|
74
|
+
}
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
return `[${value.map((entry) => stableCheckpointRestoreValue(entry)).join(",")}]`;
|
|
77
|
+
}
|
|
78
|
+
if (value && typeof value === "object") {
|
|
79
|
+
return `{${Object.keys(value)
|
|
80
|
+
.sort()
|
|
81
|
+
.map(
|
|
82
|
+
(key) =>
|
|
83
|
+
`${JSON.stringify(key)}:${stableCheckpointRestoreValue(value[key])}`,
|
|
84
|
+
)
|
|
85
|
+
.join(",")}}`;
|
|
86
|
+
}
|
|
87
|
+
throw new TypeError("checkpoint restore evidence is not canonical JSON");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function checkpointRestoreDigest(domain, value) {
|
|
91
|
+
return `sha256:${createHash("sha256")
|
|
92
|
+
.update(`${domain}\0${stableCheckpointRestoreValue(value)}`)
|
|
93
|
+
.digest("hex")}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function boundedCheckpointRestoreText(value, fallback, maximum = 2_048) {
|
|
97
|
+
let sanitized = "";
|
|
98
|
+
for (const character of String(value || fallback)) {
|
|
99
|
+
const code = character.charCodeAt(0);
|
|
100
|
+
sanitized += code < 32 || code === 127 ? " " : character;
|
|
101
|
+
}
|
|
102
|
+
const normalized = sanitized.trim();
|
|
103
|
+
return (normalized || fallback).slice(0, maximum);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function checkpointRestoreErrorCode(error) {
|
|
107
|
+
return boundedCheckpointRestoreText(
|
|
108
|
+
error?.code,
|
|
109
|
+
"CHECKPOINT_RESTORE_FAILED",
|
|
110
|
+
128,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function checkpointRestoreTargetCount(planned) {
|
|
115
|
+
const total = planned?.preview?.code?.summary?.total;
|
|
116
|
+
if (!Number.isSafeInteger(total) || total < 0) {
|
|
117
|
+
const error = new Error("checkpoint restore target count is unavailable");
|
|
118
|
+
error.code = "TIMELINE_WORKSPACE_BINDING_INVALID";
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
return total;
|
|
122
|
+
}
|
|
25
123
|
|
|
26
124
|
/** git-plumbing engine adapter (normalized interface). */
|
|
27
125
|
function gitEngine(gs, dir, session) {
|
|
@@ -42,12 +140,37 @@ function gitEngine(gs, dir, session) {
|
|
|
42
140
|
label: r.label,
|
|
43
141
|
createdAt: r.createdAt,
|
|
44
142
|
fileCount: null,
|
|
143
|
+
identity: r.commit ? `git:${r.commit}` : null,
|
|
45
144
|
})),
|
|
46
145
|
show: (id) => gs.showCheckpoint(dir, id, { session }),
|
|
47
|
-
status: (id) =>
|
|
146
|
+
status: (id, o) => {
|
|
147
|
+
const status = gs.statusAgainst(dir, id, {
|
|
148
|
+
session,
|
|
149
|
+
expectedIdentity: o?.expectedIdentity,
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
checkpointIdentity: status.checkpointIdentity,
|
|
153
|
+
modified: status.modified,
|
|
154
|
+
added: status.added,
|
|
155
|
+
deleted: status.deleted,
|
|
156
|
+
...(o?.includeWorkspaceBinding
|
|
157
|
+
? { workspaceBinding: status.workspaceBinding }
|
|
158
|
+
: {}),
|
|
159
|
+
};
|
|
160
|
+
},
|
|
48
161
|
diffText: (id, o) => gs.diffCheckpoint(dir, id, { session, stat: o?.stat }),
|
|
49
162
|
restore: (id, o) => {
|
|
50
|
-
const r = gs.rewindTo(dir, id, {
|
|
163
|
+
const r = gs.rewindTo(dir, id, {
|
|
164
|
+
session,
|
|
165
|
+
dryRun: o?.dryRun,
|
|
166
|
+
skipSafety: o?.skipSafety,
|
|
167
|
+
expectedIdentity: o?.expectedIdentity,
|
|
168
|
+
expectedWorkspaceBinding: o?.expectedWorkspaceBinding,
|
|
169
|
+
expectedSafetyPlanIdentity: o?.expectedSafetyPlanIdentity,
|
|
170
|
+
onSafetyReady: o?.onSafetyReady,
|
|
171
|
+
onMutationStarted: o?.onMutationStarted,
|
|
172
|
+
onWorkspaceApplied: o?.onWorkspaceApplied,
|
|
173
|
+
});
|
|
51
174
|
return {
|
|
52
175
|
dryRun: !!r.dryRun,
|
|
53
176
|
restoredCount: r.modified + r.recreated,
|
|
@@ -55,6 +178,9 @@ function gitEngine(gs, dir, session) {
|
|
|
55
178
|
recreated: r.recreated,
|
|
56
179
|
deleted: r.deleted,
|
|
57
180
|
safetyId: r.safetyId,
|
|
181
|
+
safetyIdentity: r.safetyIdentity,
|
|
182
|
+
safetyPlanIdentity: r.safetyPlanIdentity,
|
|
183
|
+
safetyCoverage: r.safetyCoverage,
|
|
58
184
|
};
|
|
59
185
|
},
|
|
60
186
|
remove: (id) => gs.deleteCheckpoint(dir, id, { session }),
|
|
@@ -76,14 +202,15 @@ function copyEngine(cs, dir) {
|
|
|
76
202
|
};
|
|
77
203
|
},
|
|
78
204
|
list: () =>
|
|
79
|
-
cs.listCheckpoints().map((c) => ({
|
|
205
|
+
cs.listCheckpoints({ cwd: dir }).map((c) => ({
|
|
80
206
|
id: c.id,
|
|
81
207
|
label: c.label,
|
|
82
208
|
createdAt: c.createdAt,
|
|
83
209
|
fileCount: c.fileCount,
|
|
210
|
+
identity: c.identity || null,
|
|
84
211
|
})),
|
|
85
212
|
show: (id) => {
|
|
86
|
-
const m = cs.getCheckpoint(id);
|
|
213
|
+
const m = cs.getCheckpoint(id, { cwd: dir });
|
|
87
214
|
if (!m) throw new Error(`no such checkpoint: ${id}`);
|
|
88
215
|
return {
|
|
89
216
|
id: m.id,
|
|
@@ -93,27 +220,50 @@ function copyEngine(cs, dir) {
|
|
|
93
220
|
files: m.files.map((f) => ({ rel: f.rel, bytes: f.bytes })),
|
|
94
221
|
};
|
|
95
222
|
},
|
|
96
|
-
status: (id) => {
|
|
97
|
-
const d = cs.diffCheckpoint(id
|
|
98
|
-
|
|
223
|
+
status: (id, o) => {
|
|
224
|
+
const d = cs.diffCheckpoint(id, {
|
|
225
|
+
cwd: dir,
|
|
226
|
+
expectedIdentity: o?.expectedIdentity,
|
|
227
|
+
expectedSafetyPlanIdentity: o?.expectedSafetyPlanIdentity,
|
|
228
|
+
});
|
|
229
|
+
return {
|
|
230
|
+
checkpointIdentity: d.checkpointIdentity,
|
|
231
|
+
modified: d.modified,
|
|
232
|
+
added: [],
|
|
233
|
+
deleted: d.deleted,
|
|
234
|
+
...(o?.includeWorkspaceBinding
|
|
235
|
+
? { workspaceBinding: d.workspaceBinding }
|
|
236
|
+
: {}),
|
|
237
|
+
};
|
|
99
238
|
},
|
|
100
239
|
diffText: () => null, // copy engine has no raw patch — caller uses status()
|
|
101
240
|
restore: (id, o) => {
|
|
102
|
-
const r = cs.restoreCheckpoint(id, {
|
|
241
|
+
const r = cs.restoreCheckpoint(id, {
|
|
242
|
+
cwd: dir,
|
|
243
|
+
dryRun: o?.dryRun,
|
|
244
|
+
skipSafety: o?.skipSafety,
|
|
245
|
+
expectedIdentity: o?.expectedIdentity,
|
|
246
|
+
expectedWorkspaceBinding: o?.expectedWorkspaceBinding,
|
|
247
|
+
expectedSafetyPlanIdentity: o?.expectedSafetyPlanIdentity,
|
|
248
|
+
onSafetyReady: o?.onSafetyReady,
|
|
249
|
+
onMutationStarted: o?.onMutationStarted,
|
|
250
|
+
onWorkspaceApplied: o?.onWorkspaceApplied,
|
|
251
|
+
});
|
|
103
252
|
return {
|
|
104
253
|
dryRun: !!r.dryRun,
|
|
105
254
|
restoredCount: r.restored.length,
|
|
106
255
|
restored: r.restored,
|
|
107
256
|
missingBlob: r.missingBlob,
|
|
108
257
|
safetyId: r.safetyId,
|
|
258
|
+
safetyIdentity: r.safetyIdentity,
|
|
259
|
+
safetyPlanIdentity: r.safetyPlanIdentity,
|
|
260
|
+
safetyCoverage: r.safetyCoverage,
|
|
261
|
+
createdPaths: r.createdPaths,
|
|
262
|
+
deletedPaths: r.deletedPaths,
|
|
109
263
|
};
|
|
110
264
|
},
|
|
111
|
-
remove: (id) => cs.deleteCheckpoint(id),
|
|
112
|
-
clear: () => {
|
|
113
|
-
const all = cs.listCheckpoints();
|
|
114
|
-
for (const c of all) cs.deleteCheckpoint(c.id);
|
|
115
|
-
return all.length;
|
|
116
|
-
},
|
|
265
|
+
remove: (id) => cs.deleteCheckpoint(id, { cwd: dir }),
|
|
266
|
+
clear: () => cs.clearCheckpoints({ cwd: dir }),
|
|
117
267
|
};
|
|
118
268
|
}
|
|
119
269
|
|
|
@@ -125,13 +275,149 @@ async function pickEngine(dir, session) {
|
|
|
125
275
|
return copyEngine(cs, dir);
|
|
126
276
|
}
|
|
127
277
|
|
|
278
|
+
function loadTimelineContext(engine, sessionId) {
|
|
279
|
+
const binding = loadTurnBindingLog(sessionId);
|
|
280
|
+
const checkpoints = engine.list();
|
|
281
|
+
const headHash = findLatestEvent(sessionId, null)?.hash || null;
|
|
282
|
+
const timeline = buildCheckpointTimeline({
|
|
283
|
+
sessionId,
|
|
284
|
+
turns: binding,
|
|
285
|
+
checkpoints,
|
|
286
|
+
headHash,
|
|
287
|
+
});
|
|
288
|
+
return {
|
|
289
|
+
binding,
|
|
290
|
+
checkpoints,
|
|
291
|
+
headHash,
|
|
292
|
+
timeline,
|
|
293
|
+
messages: readVerifiedMessages(sessionId),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function printTimelineActionResult(result, asJson) {
|
|
298
|
+
if (asJson) {
|
|
299
|
+
console.log(JSON.stringify(result, null, 2));
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (result?.ok === false) {
|
|
303
|
+
logger.error(
|
|
304
|
+
chalk.red(
|
|
305
|
+
`${result.code || "TIMELINE_ACTION_FAILED"}: ${result.error || "request rejected"}`,
|
|
306
|
+
),
|
|
307
|
+
);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
logger.log(
|
|
311
|
+
chalk.green(
|
|
312
|
+
`${result.mode === "preview" ? "Preview" : "Completed"}: ${result.action} at ${result.turnId}`,
|
|
313
|
+
),
|
|
314
|
+
);
|
|
315
|
+
for (const warning of result.warnings || []) {
|
|
316
|
+
logger.log(chalk.yellow(` ${warning}`));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
128
320
|
const tag = (engine) => chalk.dim(engine.kind === "git" ? "[git]" : "[copy]");
|
|
129
321
|
|
|
130
|
-
|
|
322
|
+
function isCodeRestoreAction(action) {
|
|
323
|
+
return action === "restore-code" || action === "restore-both";
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function workspaceStaleError(message = "workspace changed; preview again") {
|
|
327
|
+
const error = new Error(message);
|
|
328
|
+
error.code = "TIMELINE_WORKSPACE_STALE";
|
|
329
|
+
return error;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function restoreWorkspaceStaleError(
|
|
333
|
+
message = "workspace changed before the restore lock was acquired; retry the restore",
|
|
334
|
+
) {
|
|
335
|
+
const error = new Error(message);
|
|
336
|
+
error.code = "CHECKPOINT_WORKSPACE_STALE";
|
|
337
|
+
return error;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function directRestoreTargetCount(preview) {
|
|
341
|
+
const groups = [preview?.modified, preview?.added, preview?.deleted];
|
|
342
|
+
if (groups.some((group) => !Array.isArray(group))) {
|
|
343
|
+
throw restoreWorkspaceStaleError(
|
|
344
|
+
"checkpoint restore target count is unavailable; retry the restore",
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
const total = groups.reduce((sum, group) => sum + group.length, 0);
|
|
348
|
+
if (!Number.isSafeInteger(total)) {
|
|
349
|
+
throw restoreWorkspaceStaleError(
|
|
350
|
+
"checkpoint restore target count is invalid; retry the restore",
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
return total;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function directRestorePlan({
|
|
357
|
+
engine,
|
|
358
|
+
checkpointId,
|
|
359
|
+
checkpointNamespace,
|
|
360
|
+
preview,
|
|
361
|
+
authorization,
|
|
362
|
+
}) {
|
|
363
|
+
const checkpointIdentity = preview?.checkpointIdentity;
|
|
364
|
+
const workspaceBinding = preview?.workspaceBinding;
|
|
365
|
+
const workspaceRoot = workspaceBinding?.workspaceRoot;
|
|
366
|
+
if (
|
|
367
|
+
typeof checkpointIdentity !== "string" ||
|
|
368
|
+
checkpointIdentity.length === 0 ||
|
|
369
|
+
!workspaceRoot
|
|
370
|
+
) {
|
|
371
|
+
throw restoreWorkspaceStaleError(
|
|
372
|
+
"checkpoint immutable identity or workspace scope is unavailable; retry the restore",
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
const targetCount = directRestoreTargetCount(preview);
|
|
376
|
+
return {
|
|
377
|
+
restoreKind: engine.kind,
|
|
378
|
+
restoreSurface: "direct",
|
|
379
|
+
checkpointId,
|
|
380
|
+
checkpointIdentity,
|
|
381
|
+
checkpointNamespace,
|
|
382
|
+
workspaceRoot,
|
|
383
|
+
workspaceBinding,
|
|
384
|
+
targetCount,
|
|
385
|
+
confirmationDigest: computeCheckpointRestoreDigest(
|
|
386
|
+
"cc-checkpoint-restore-direct-confirmation-v1",
|
|
387
|
+
{
|
|
388
|
+
authorization,
|
|
389
|
+
restoreKind: engine.kind,
|
|
390
|
+
checkpointId,
|
|
391
|
+
checkpointIdentity,
|
|
392
|
+
checkpointNamespace,
|
|
393
|
+
workspaceBinding,
|
|
394
|
+
targetCount,
|
|
395
|
+
},
|
|
396
|
+
),
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export function registerCheckpointCommand(program, dependencies = {}) {
|
|
401
|
+
const withWorkspaceLockSync =
|
|
402
|
+
dependencies.withWorkspaceLockSync || withCanonicalWorkspaceLockSync;
|
|
403
|
+
const createCheckpointRestoreSagaStore =
|
|
404
|
+
dependencies.createCheckpointRestoreSagaStore ||
|
|
405
|
+
((options) => new CheckpointRestoreSagaStore(options));
|
|
406
|
+
const workspaceLockOwnerDigest =
|
|
407
|
+
dependencies.computeCheckpointRestoreWorkspaceLockOwnerDigest ||
|
|
408
|
+
computeCheckpointRestoreWorkspaceLockOwnerDigest;
|
|
409
|
+
const runCheckpointRestore =
|
|
410
|
+
dependencies.runCheckpointRestoreOperation || runCheckpointRestoreOperation;
|
|
131
411
|
const cp = program
|
|
132
412
|
.command("checkpoint")
|
|
133
413
|
.description("Snapshot / rewind file state (git-plumbing, copy fallback)");
|
|
134
414
|
|
|
415
|
+
registerManagedCheckpointCommands(cp);
|
|
416
|
+
registerCheckpointRestoreRecoveryCommands(
|
|
417
|
+
cp,
|
|
418
|
+
dependencies.checkpointRestoreRecovery,
|
|
419
|
+
);
|
|
420
|
+
|
|
135
421
|
cp.command("create [paths...]")
|
|
136
422
|
.description(
|
|
137
423
|
"Snapshot the work tree (git) or the given files/dirs (fallback)",
|
|
@@ -223,6 +509,566 @@ export function registerCheckpointCommand(program) {
|
|
|
223
509
|
}
|
|
224
510
|
});
|
|
225
511
|
|
|
512
|
+
cp.command("timeline")
|
|
513
|
+
.description(
|
|
514
|
+
"Project persisted turns, checkpoints, side effects, and rewind actions",
|
|
515
|
+
)
|
|
516
|
+
.option("-d, --dir <dir>", "Target directory", ".")
|
|
517
|
+
.option("-s, --session <id>", "Checkpoint/session binding", "default")
|
|
518
|
+
.option("--json", "Output the canonical versioned projection as JSON")
|
|
519
|
+
.action(async (options) => {
|
|
520
|
+
try {
|
|
521
|
+
const dir = resolve(options.dir);
|
|
522
|
+
const engine = await pickEngine(dir, options.session);
|
|
523
|
+
const { timeline } = loadTimelineContext(engine, options.session);
|
|
524
|
+
if (options.json) {
|
|
525
|
+
console.log(JSON.stringify(timeline, null, 2));
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const projection = projectCheckpointTimeline(timeline);
|
|
530
|
+
if (projection.entries.length === 0) {
|
|
531
|
+
logger.log(
|
|
532
|
+
chalk.gray(
|
|
533
|
+
timeline.unboundMarkers.length > 0
|
|
534
|
+
? `${timeline.unboundMarkers.length} checkpoint/marker(s) are not bound to a persisted turn.`
|
|
535
|
+
: "No persisted checkpoint timeline for this session.",
|
|
536
|
+
),
|
|
537
|
+
);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
for (const entry of projection.entries) {
|
|
541
|
+
const coverage =
|
|
542
|
+
entry.coverage === "full"
|
|
543
|
+
? chalk.green(entry.coverage)
|
|
544
|
+
: entry.coverage === "partial"
|
|
545
|
+
? chalk.yellow(entry.coverage)
|
|
546
|
+
: chalk.red(entry.coverage);
|
|
547
|
+
logger.log(
|
|
548
|
+
`${chalk.cyan(entry.turnId)} ${coverage} ` +
|
|
549
|
+
`${entry.markerKinds.join(", ") || "no markers"} ` +
|
|
550
|
+
`${entry.enabledActions.join(", ") || "no actions"}`,
|
|
551
|
+
);
|
|
552
|
+
if (entry.excludedPaths.length > 0) {
|
|
553
|
+
logger.log(
|
|
554
|
+
chalk.yellow(` excluded: ${entry.excludedPaths.join(", ")}`),
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
if (entry.irreversibleSideEffects.length > 0) {
|
|
558
|
+
logger.log(
|
|
559
|
+
chalk.red(
|
|
560
|
+
` irreversible: ${entry.irreversibleSideEffects.join(", ")}`,
|
|
561
|
+
),
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
} catch (err) {
|
|
566
|
+
logger.error(chalk.red(`checkpoint timeline failed: ${err.message}`));
|
|
567
|
+
process.exitCode = 1;
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
cp.command("action")
|
|
572
|
+
.description(
|
|
573
|
+
"Preview or commit a CLI-authored checkpoint timeline submission",
|
|
574
|
+
)
|
|
575
|
+
.requiredOption(
|
|
576
|
+
"--submission <json>",
|
|
577
|
+
"Exact action envelope embedded in checkpoint timeline --json",
|
|
578
|
+
)
|
|
579
|
+
.option("-d, --dir <dir>", "Target directory", ".")
|
|
580
|
+
.option("-s, --session <id>", "Checkpoint/session binding", "default")
|
|
581
|
+
.option("--preview", "Validate and preview without writing")
|
|
582
|
+
.option("--confirm", "Commit after an IDE/user confirmation")
|
|
583
|
+
.option("--json", "Output the versioned result as JSON")
|
|
584
|
+
.action(async (options) => {
|
|
585
|
+
let output;
|
|
586
|
+
try {
|
|
587
|
+
if (options.preview === options.confirm) {
|
|
588
|
+
const error = new Error(
|
|
589
|
+
"choose exactly one of --preview or --confirm",
|
|
590
|
+
);
|
|
591
|
+
error.code = "TIMELINE_CONFIRMATION_REQUIRED";
|
|
592
|
+
throw error;
|
|
593
|
+
}
|
|
594
|
+
if (String(options.submission).length > 16_384) {
|
|
595
|
+
const error = new Error("timeline submission is too large");
|
|
596
|
+
error.code = "TIMELINE_SUBMISSION_INVALID";
|
|
597
|
+
throw error;
|
|
598
|
+
}
|
|
599
|
+
let submittedEnvelope;
|
|
600
|
+
try {
|
|
601
|
+
submittedEnvelope = JSON.parse(options.submission);
|
|
602
|
+
} catch {
|
|
603
|
+
const error = new Error("timeline submission must be valid JSON");
|
|
604
|
+
error.code = "TIMELINE_SUBMISSION_INVALID";
|
|
605
|
+
throw error;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const dir = resolve(options.dir);
|
|
609
|
+
const engine = await pickEngine(dir, options.session);
|
|
610
|
+
const planForContext = (authoritativeContext) => {
|
|
611
|
+
const validation = options.preview
|
|
612
|
+
? validateCheckpointTimelineSubmission(
|
|
613
|
+
authoritativeContext.timeline,
|
|
614
|
+
submittedEnvelope,
|
|
615
|
+
)
|
|
616
|
+
: validateCheckpointTimelineConfirmationSubmission(
|
|
617
|
+
authoritativeContext.timeline,
|
|
618
|
+
submittedEnvelope,
|
|
619
|
+
);
|
|
620
|
+
if (!validation.ok) {
|
|
621
|
+
const error = new Error(
|
|
622
|
+
validation.code === "TIMELINE_STALE"
|
|
623
|
+
? "checkpoint timeline is stale; refresh before retrying"
|
|
624
|
+
: "checkpoint timeline submission was rejected",
|
|
625
|
+
);
|
|
626
|
+
error.code = validation.code;
|
|
627
|
+
throw error;
|
|
628
|
+
}
|
|
629
|
+
const authoritativeSubmission = validation.submission;
|
|
630
|
+
const checkpointIdentity =
|
|
631
|
+
authoritativeSubmission.checkpointIdentity || null;
|
|
632
|
+
let codePreview = null;
|
|
633
|
+
if (isCodeRestoreAction(authoritativeSubmission.action)) {
|
|
634
|
+
if (!checkpointIdentity) {
|
|
635
|
+
const error = new Error(
|
|
636
|
+
"checkpoint immutable identity is unavailable; refresh or recreate it",
|
|
637
|
+
);
|
|
638
|
+
error.code = "TIMELINE_CHECKPOINT_IDENTITY_INVALID";
|
|
639
|
+
throw error;
|
|
640
|
+
}
|
|
641
|
+
codePreview = engine.status(authoritativeSubmission.checkpointId, {
|
|
642
|
+
expectedIdentity: checkpointIdentity,
|
|
643
|
+
includeWorkspaceBinding: true,
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
const authoritativePlan = planCheckpointTimelineAction({
|
|
647
|
+
timeline: authoritativeContext.timeline,
|
|
648
|
+
submission: authoritativeSubmission,
|
|
649
|
+
messages: authoritativeContext.messages,
|
|
650
|
+
codePreview,
|
|
651
|
+
});
|
|
652
|
+
if (!authoritativePlan.ok) {
|
|
653
|
+
const error = new Error(
|
|
654
|
+
authoritativePlan.code === "TIMELINE_STALE"
|
|
655
|
+
? "checkpoint timeline is stale; refresh before retrying"
|
|
656
|
+
: "checkpoint timeline submission was rejected",
|
|
657
|
+
);
|
|
658
|
+
error.code = authoritativePlan.code;
|
|
659
|
+
throw error;
|
|
660
|
+
}
|
|
661
|
+
return {
|
|
662
|
+
submission: authoritativeSubmission,
|
|
663
|
+
planned: authoritativePlan,
|
|
664
|
+
codePreview,
|
|
665
|
+
};
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
let context = loadTimelineContext(engine, options.session);
|
|
669
|
+
let { submission, planned } = planForContext(context);
|
|
670
|
+
if (options.preview) {
|
|
671
|
+
output = planned.preview;
|
|
672
|
+
printTimelineActionResult(output, options.json);
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
if (
|
|
677
|
+
!checkpointTimelineConfirmationsMatch(
|
|
678
|
+
planned.preview.confirmationSubmission,
|
|
679
|
+
submittedEnvelope,
|
|
680
|
+
)
|
|
681
|
+
) {
|
|
682
|
+
const error = new Error(
|
|
683
|
+
isCodeRestoreAction(submission.action)
|
|
684
|
+
? "workspace changed after checkpoint preview; preview again"
|
|
685
|
+
: "timeline confirmation no longer matches the current plan",
|
|
686
|
+
);
|
|
687
|
+
error.code = isCodeRestoreAction(submission.action)
|
|
688
|
+
? "TIMELINE_WORKSPACE_STALE"
|
|
689
|
+
: "TIMELINE_CONFIRMATION_INVALID";
|
|
690
|
+
throw error;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
let checkpointIdentity = submission.checkpointIdentity || null;
|
|
694
|
+
let restoreExecution = null;
|
|
695
|
+
let restoreOperationId = null;
|
|
696
|
+
|
|
697
|
+
const executeUnderSessionAuthority = ({
|
|
698
|
+
operationId = null,
|
|
699
|
+
workspaceLease = null,
|
|
700
|
+
commitIntent = null,
|
|
701
|
+
restoreWorkspace = null,
|
|
702
|
+
} = {}) => {
|
|
703
|
+
let durableSessionCommitHash = null;
|
|
704
|
+
const transactionResult = withSessionAuthorityTransaction(
|
|
705
|
+
options.session,
|
|
706
|
+
context.headHash,
|
|
707
|
+
(transaction) => {
|
|
708
|
+
workspaceLease?.assertOwned();
|
|
709
|
+
const intent = transaction.appendAuthorityEvent(
|
|
710
|
+
CHECKPOINT_TIMELINE_INTENT_EVENT,
|
|
711
|
+
{
|
|
712
|
+
...(operationId ? { operationId } : {}),
|
|
713
|
+
revision: context.timeline.revision,
|
|
714
|
+
action: submission.action,
|
|
715
|
+
turnId: submission.turnId,
|
|
716
|
+
checkpointId: submission.checkpointId || null,
|
|
717
|
+
checkpointIdentity,
|
|
718
|
+
workspaceDir: dir,
|
|
719
|
+
workspaceScopeIdentity:
|
|
720
|
+
planned.workspaceBinding?.scopeIdentity || null,
|
|
721
|
+
workspacePrestateIdentity:
|
|
722
|
+
planned.workspaceBinding?.prestateIdentity || null,
|
|
723
|
+
workspaceWritePlanIdentity:
|
|
724
|
+
planned.workspaceBinding?.writePlanIdentity || null,
|
|
725
|
+
workspaceTargetPoststateIdentity:
|
|
726
|
+
planned.workspaceBinding?.targetPoststateIdentity || null,
|
|
727
|
+
confirmationDigest:
|
|
728
|
+
planned.preview.confirmationSubmission.digest,
|
|
729
|
+
},
|
|
730
|
+
);
|
|
731
|
+
const result = {};
|
|
732
|
+
try {
|
|
733
|
+
if (commitIntent) {
|
|
734
|
+
try {
|
|
735
|
+
commitIntent(
|
|
736
|
+
checkpointRestoreDigest(
|
|
737
|
+
"cc-checkpoint-restore-intent-commit-v1",
|
|
738
|
+
intent.hash,
|
|
739
|
+
),
|
|
740
|
+
);
|
|
741
|
+
} catch (error) {
|
|
742
|
+
// The transcript intent append precedes the short saga CAS.
|
|
743
|
+
// If the CAS fails, recovery must reconcile the durable
|
|
744
|
+
// transcript instead of treating this as a pre-intent abort.
|
|
745
|
+
if (error && typeof error === "object") {
|
|
746
|
+
error.checkpointRestoreSessionIntentCommitted = true;
|
|
747
|
+
// Let the orchestrator attach the post-settlement saga
|
|
748
|
+
// position rather than leaking this failed CAS's stale
|
|
749
|
+
// prepared-phase snapshot as the recovery status.
|
|
750
|
+
error.checkpointRestoreSagaPhase = null;
|
|
751
|
+
error.checkpointRestoreSagaSeq = null;
|
|
752
|
+
error.checkpointRestoreSagaHeadHash = null;
|
|
753
|
+
}
|
|
754
|
+
throw error;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if (restoreWorkspace) {
|
|
759
|
+
workspaceLease?.assertOwned();
|
|
760
|
+
result.code = restoreWorkspace();
|
|
761
|
+
transaction.retainRecoveryEvidence({
|
|
762
|
+
safetyId: result.code?.safetyId,
|
|
763
|
+
safetyIdentity: result.code?.safetyIdentity,
|
|
764
|
+
safetyPlanIdentity: result.code?.safetyPlanIdentity,
|
|
765
|
+
safetyCoverage: result.code?.safetyCoverage,
|
|
766
|
+
restorePhase: "workspace-applied",
|
|
767
|
+
createdPaths: result.code?.createdPaths,
|
|
768
|
+
});
|
|
769
|
+
workspaceLease?.assertOwned();
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (planned.commit.branchPlan) {
|
|
773
|
+
result.branch = createBranchSession({
|
|
774
|
+
branchSessionId: planned.commit.branchPlan.branchSessionId,
|
|
775
|
+
parentSessionId: options.session,
|
|
776
|
+
parentTurnId: submission.turnId,
|
|
777
|
+
messages: planned.commit.messages,
|
|
778
|
+
meta: { title: `Branch of ${options.session}` },
|
|
779
|
+
});
|
|
780
|
+
transaction.retainRecoveryEvidence({
|
|
781
|
+
branchSessionId:
|
|
782
|
+
result.branch?.branchSessionId ||
|
|
783
|
+
planned.commit.branchPlan.branchSessionId,
|
|
784
|
+
});
|
|
785
|
+
} else if (planned.commit.messages) {
|
|
786
|
+
context.binding.pruneFromOffset(
|
|
787
|
+
planned.commit.bindingPruneOffset,
|
|
788
|
+
);
|
|
789
|
+
const committed = transaction.appendAuthorityEvent(
|
|
790
|
+
TURN_BINDING_TIMELINE_EVENT,
|
|
791
|
+
{
|
|
792
|
+
...(operationId ? { operationId } : {}),
|
|
793
|
+
action: submission.action,
|
|
794
|
+
sourceRevision: context.timeline.revision,
|
|
795
|
+
turnId: submission.turnId,
|
|
796
|
+
messages: planned.commit.messages,
|
|
797
|
+
binding: context.binding.toJSON(),
|
|
798
|
+
},
|
|
799
|
+
);
|
|
800
|
+
result.conversation = {
|
|
801
|
+
messages: planned.commit.messages.length,
|
|
802
|
+
commitHash: committed.hash,
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
workspaceLease?.assertOwned();
|
|
807
|
+
const completedAudit = transaction.appendAuthorityEvent(
|
|
808
|
+
CHECKPOINT_TIMELINE_AUDIT_EVENT,
|
|
809
|
+
{
|
|
810
|
+
...(operationId ? { operationId } : {}),
|
|
811
|
+
revision: context.timeline.revision,
|
|
812
|
+
action: submission.action,
|
|
813
|
+
turnId: submission.turnId,
|
|
814
|
+
checkpointId: submission.checkpointId || null,
|
|
815
|
+
checkpointIdentity,
|
|
816
|
+
workspaceDir: dir,
|
|
817
|
+
workspaceScopeIdentity:
|
|
818
|
+
planned.workspaceBinding?.scopeIdentity || null,
|
|
819
|
+
workspacePrestateIdentity:
|
|
820
|
+
planned.workspaceBinding?.prestateIdentity || null,
|
|
821
|
+
workspaceWritePlanIdentity:
|
|
822
|
+
planned.workspaceBinding?.writePlanIdentity || null,
|
|
823
|
+
workspaceTargetPoststateIdentity:
|
|
824
|
+
planned.workspaceBinding?.targetPoststateIdentity || null,
|
|
825
|
+
confirmationDigest:
|
|
826
|
+
planned.preview.confirmationSubmission.digest,
|
|
827
|
+
status: "completed",
|
|
828
|
+
branchSessionId: result.branch?.branchSessionId || null,
|
|
829
|
+
safetyCheckpointId: result.code?.safetyId || null,
|
|
830
|
+
safetyCheckpointIdentity:
|
|
831
|
+
result.code?.safetyIdentity || null,
|
|
832
|
+
safetyPlanIdentity: result.code?.safetyPlanIdentity || null,
|
|
833
|
+
safetyCoverage: result.code?.safetyCoverage || null,
|
|
834
|
+
},
|
|
835
|
+
);
|
|
836
|
+
durableSessionCommitHash = completedAudit.hash;
|
|
837
|
+
} catch (error) {
|
|
838
|
+
if (error && typeof error === "object") {
|
|
839
|
+
error.safetyId ||= result.code?.safetyId || null;
|
|
840
|
+
error.safetyIdentity ||= result.code?.safetyIdentity || null;
|
|
841
|
+
error.safetyPlanIdentity ||=
|
|
842
|
+
result.code?.safetyPlanIdentity || null;
|
|
843
|
+
error.safetyCoverage ||= result.code?.safetyCoverage || null;
|
|
844
|
+
if (result.code && !error.restorePhase) {
|
|
845
|
+
error.restorePhase = "workspace-applied";
|
|
846
|
+
}
|
|
847
|
+
if (!Array.isArray(error.createdPaths)) {
|
|
848
|
+
error.createdPaths = Array.isArray(
|
|
849
|
+
result.code?.createdPaths,
|
|
850
|
+
)
|
|
851
|
+
? [...result.code.createdPaths]
|
|
852
|
+
: [];
|
|
853
|
+
}
|
|
854
|
+
error.branchSessionId ||=
|
|
855
|
+
result.branch?.branchSessionId ||
|
|
856
|
+
planned.commit.branchPlan?.branchSessionId ||
|
|
857
|
+
null;
|
|
858
|
+
}
|
|
859
|
+
try {
|
|
860
|
+
transaction.appendAuthorityEvent(
|
|
861
|
+
CHECKPOINT_TIMELINE_AUDIT_EVENT,
|
|
862
|
+
{
|
|
863
|
+
...(operationId ? { operationId } : {}),
|
|
864
|
+
revision: context.timeline.revision,
|
|
865
|
+
action: submission.action,
|
|
866
|
+
turnId: submission.turnId,
|
|
867
|
+
checkpointId: submission.checkpointId || null,
|
|
868
|
+
checkpointIdentity,
|
|
869
|
+
workspaceDir: dir,
|
|
870
|
+
workspaceScopeIdentity:
|
|
871
|
+
planned.workspaceBinding?.scopeIdentity || null,
|
|
872
|
+
workspacePrestateIdentity:
|
|
873
|
+
planned.workspaceBinding?.prestateIdentity || null,
|
|
874
|
+
workspaceWritePlanIdentity:
|
|
875
|
+
planned.workspaceBinding?.writePlanIdentity || null,
|
|
876
|
+
workspaceTargetPoststateIdentity:
|
|
877
|
+
planned.workspaceBinding?.targetPoststateIdentity ||
|
|
878
|
+
null,
|
|
879
|
+
confirmationDigest:
|
|
880
|
+
planned.preview.confirmationSubmission.digest,
|
|
881
|
+
status: "failed",
|
|
882
|
+
failureCode: checkpointRestoreErrorCode(error),
|
|
883
|
+
workspaceState: restoreWorkspace
|
|
884
|
+
? "unknown"
|
|
885
|
+
: "unchanged",
|
|
886
|
+
branchSessionId:
|
|
887
|
+
result.branch?.branchSessionId ||
|
|
888
|
+
planned.commit.branchPlan?.branchSessionId ||
|
|
889
|
+
null,
|
|
890
|
+
safetyCheckpointId:
|
|
891
|
+
result.code?.safetyId || error?.safetyId || null,
|
|
892
|
+
safetyCheckpointIdentity:
|
|
893
|
+
result.code?.safetyIdentity ||
|
|
894
|
+
error?.safetyIdentity ||
|
|
895
|
+
null,
|
|
896
|
+
safetyPlanIdentity:
|
|
897
|
+
result.code?.safetyPlanIdentity ||
|
|
898
|
+
error?.safetyPlanIdentity ||
|
|
899
|
+
null,
|
|
900
|
+
safetyCoverage:
|
|
901
|
+
result.code?.safetyCoverage ||
|
|
902
|
+
error?.safetyCoverage ||
|
|
903
|
+
null,
|
|
904
|
+
createdPaths: Array.isArray(error?.createdPaths)
|
|
905
|
+
? error.createdPaths.slice(0, 256)
|
|
906
|
+
: [],
|
|
907
|
+
},
|
|
908
|
+
);
|
|
909
|
+
} catch (auditError) {
|
|
910
|
+
// An authority append can fail after bytes reach the transcript.
|
|
911
|
+
// Retain the first operation error as the transaction's nested
|
|
912
|
+
// diagnosis and let a poisoned writer/final settlement report
|
|
913
|
+
// commitState=unknown instead of appending from a stale head.
|
|
914
|
+
if (error && typeof error === "object") {
|
|
915
|
+
error.checkpointAuditError = auditError;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
throw error;
|
|
919
|
+
}
|
|
920
|
+
return result;
|
|
921
|
+
},
|
|
922
|
+
);
|
|
923
|
+
|
|
924
|
+
if (operationId) {
|
|
925
|
+
workspaceLease?.assertOwned();
|
|
926
|
+
if (!durableSessionCommitHash) {
|
|
927
|
+
const error = new Error(
|
|
928
|
+
"checkpoint restore session settlement hash is unavailable",
|
|
929
|
+
);
|
|
930
|
+
error.code = "CHECKPOINT_RESTORE_SESSION_BOUNDARY_MISSING";
|
|
931
|
+
error.commitState = "unknown";
|
|
932
|
+
throw error;
|
|
933
|
+
}
|
|
934
|
+
return {
|
|
935
|
+
result: transactionResult,
|
|
936
|
+
sessionCommitDigest: checkpointRestoreDigest(
|
|
937
|
+
"cc-checkpoint-restore-session-commit-v1",
|
|
938
|
+
durableSessionCommitHash,
|
|
939
|
+
),
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
return transactionResult;
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
const buildTimelineRestorePlan = () => {
|
|
946
|
+
const workspaceRoot = planned.workspaceBinding?.workspaceRoot;
|
|
947
|
+
if (!workspaceRoot) {
|
|
948
|
+
throw workspaceStaleError(
|
|
949
|
+
"checkpoint restore scope is unavailable; preview again",
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
return {
|
|
953
|
+
restoreKind: engine.kind,
|
|
954
|
+
restoreSurface: "timeline",
|
|
955
|
+
checkpointId: submission.checkpointId,
|
|
956
|
+
checkpointIdentity,
|
|
957
|
+
checkpointNamespace: options.session,
|
|
958
|
+
workspaceRoot,
|
|
959
|
+
workspaceBinding: planned.workspaceBinding,
|
|
960
|
+
targetCount: checkpointRestoreTargetCount(planned),
|
|
961
|
+
confirmationDigest: planned.preview.confirmationSubmission.digest,
|
|
962
|
+
sessionId: options.session,
|
|
963
|
+
timelineEntryId: submission.turnId,
|
|
964
|
+
};
|
|
965
|
+
};
|
|
966
|
+
|
|
967
|
+
let result;
|
|
968
|
+
if (isCodeRestoreAction(submission.action)) {
|
|
969
|
+
const initialRestorePlan = buildTimelineRestorePlan();
|
|
970
|
+
restoreExecution = runCheckpointRestore({
|
|
971
|
+
plan: initialRestorePlan,
|
|
972
|
+
revalidate: () => {
|
|
973
|
+
// Both the session head and the full workspace write plan can
|
|
974
|
+
// change while this process waits for the workspace lock.
|
|
975
|
+
const lockedContext = loadTimelineContext(
|
|
976
|
+
engine,
|
|
977
|
+
options.session,
|
|
978
|
+
);
|
|
979
|
+
const lockedAction = planForContext(lockedContext);
|
|
980
|
+
if (
|
|
981
|
+
!checkpointTimelineConfirmationsMatch(
|
|
982
|
+
lockedAction.planned.preview.confirmationSubmission,
|
|
983
|
+
submittedEnvelope,
|
|
984
|
+
)
|
|
985
|
+
) {
|
|
986
|
+
throw workspaceStaleError(
|
|
987
|
+
"workspace or checkpoint timeline changed while waiting for the restore lock; preview again",
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
context = lockedContext;
|
|
991
|
+
submission = lockedAction.submission;
|
|
992
|
+
planned = lockedAction.planned;
|
|
993
|
+
checkpointIdentity = submission.checkpointIdentity || null;
|
|
994
|
+
return buildTimelineRestorePlan();
|
|
995
|
+
},
|
|
996
|
+
restore: ({ expectedIdentity, expectedWorkspaceBinding, hooks }) =>
|
|
997
|
+
engine.restore(submission.checkpointId, {
|
|
998
|
+
expectedIdentity,
|
|
999
|
+
expectedWorkspaceBinding,
|
|
1000
|
+
...hooks,
|
|
1001
|
+
}),
|
|
1002
|
+
withSessionAuthority: (authority) => {
|
|
1003
|
+
try {
|
|
1004
|
+
return executeUnderSessionAuthority(authority);
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
const retained = error?.transactionRecoveryEvidence;
|
|
1007
|
+
if (
|
|
1008
|
+
error &&
|
|
1009
|
+
typeof error === "object" &&
|
|
1010
|
+
retained &&
|
|
1011
|
+
typeof retained === "object"
|
|
1012
|
+
) {
|
|
1013
|
+
error.safetyId ||= retained.safetyId || null;
|
|
1014
|
+
error.safetyIdentity ||= retained.safetyIdentity || null;
|
|
1015
|
+
error.safetyPlanIdentity ||=
|
|
1016
|
+
retained.safetyPlanIdentity || null;
|
|
1017
|
+
error.safetyCoverage ||= retained.safetyCoverage || null;
|
|
1018
|
+
error.restorePhase ||= retained.restorePhase || null;
|
|
1019
|
+
}
|
|
1020
|
+
throw error;
|
|
1021
|
+
}
|
|
1022
|
+
},
|
|
1023
|
+
dependencies: {
|
|
1024
|
+
withWorkspaceLockSync,
|
|
1025
|
+
createSagaStore: createCheckpointRestoreSagaStore,
|
|
1026
|
+
computeWorkspaceLockOwnerDigest: workspaceLockOwnerDigest,
|
|
1027
|
+
},
|
|
1028
|
+
});
|
|
1029
|
+
restoreOperationId = restoreExecution.operationId;
|
|
1030
|
+
result = restoreExecution.result;
|
|
1031
|
+
} else {
|
|
1032
|
+
result = executeUnderSessionAuthority();
|
|
1033
|
+
}
|
|
1034
|
+
let nextContext = null;
|
|
1035
|
+
let nextContextWarning = null;
|
|
1036
|
+
try {
|
|
1037
|
+
nextContext = loadTimelineContext(engine, options.session);
|
|
1038
|
+
} catch (reloadError) {
|
|
1039
|
+
if (restoreExecution?.saga?.phase !== "completed") throw reloadError;
|
|
1040
|
+
nextContextWarning = boundedCheckpointRestoreText(
|
|
1041
|
+
`restore completed, but the next timeline revision could not be reloaded: ${reloadError?.code || reloadError?.message || "reload failed"}`,
|
|
1042
|
+
"restore completed, but the next timeline revision could not be reloaded",
|
|
1043
|
+
512,
|
|
1044
|
+
);
|
|
1045
|
+
}
|
|
1046
|
+
output = {
|
|
1047
|
+
schema: CHECKPOINT_TIMELINE_RESULT_SCHEMA,
|
|
1048
|
+
version: CHECKPOINT_TIMELINE_RESULT_VERSION,
|
|
1049
|
+
ok: true,
|
|
1050
|
+
mode: "executed",
|
|
1051
|
+
action: submission.action,
|
|
1052
|
+
sessionId: options.session,
|
|
1053
|
+
turnId: submission.turnId,
|
|
1054
|
+
revision: context.timeline.revision,
|
|
1055
|
+
nextRevision: nextContext?.timeline?.revision || null,
|
|
1056
|
+
...(restoreOperationId ? { operationId: restoreOperationId } : {}),
|
|
1057
|
+
result,
|
|
1058
|
+
warnings: [
|
|
1059
|
+
...planned.preview.warnings,
|
|
1060
|
+
...(restoreExecution?.warnings || []),
|
|
1061
|
+
...(nextContextWarning ? [nextContextWarning] : []),
|
|
1062
|
+
],
|
|
1063
|
+
};
|
|
1064
|
+
printTimelineActionResult(output, options.json);
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
output = timelineActionError(error);
|
|
1067
|
+
printTimelineActionResult(output, options.json);
|
|
1068
|
+
process.exitCode = 1;
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
|
|
226
1072
|
cp.command("show <id>")
|
|
227
1073
|
.description("Show a checkpoint's files, or its diff vs current state")
|
|
228
1074
|
.option("-d, --dir <dir>", "Target directory", ".")
|
|
@@ -307,12 +1153,18 @@ export function registerCheckpointCommand(program) {
|
|
|
307
1153
|
return;
|
|
308
1154
|
}
|
|
309
1155
|
|
|
310
|
-
// Destructive:
|
|
311
|
-
//
|
|
1156
|
+
// Destructive: bind the exact workspace state that is displayed (or
|
|
1157
|
+
// immediately preflighted for --force), then require the engine to
|
|
1158
|
+
// observe the same full state before safety creation or file writes.
|
|
1159
|
+
// Require --force when not a TTY; prompt when interactive.
|
|
1160
|
+
const restorePreview = engine.status(id, {
|
|
1161
|
+
includeWorkspaceBinding: true,
|
|
1162
|
+
});
|
|
312
1163
|
if (!options.force) {
|
|
313
|
-
const d = engine.status(id);
|
|
314
1164
|
const willChange =
|
|
315
|
-
|
|
1165
|
+
restorePreview.modified.length +
|
|
1166
|
+
restorePreview.deleted.length +
|
|
1167
|
+
(restorePreview.added?.length || 0);
|
|
316
1168
|
if (process.stdin.isTTY) {
|
|
317
1169
|
const { confirm } = await import("@inquirer/prompts");
|
|
318
1170
|
const ok = await confirm({
|
|
@@ -334,9 +1186,50 @@ export function registerCheckpointCommand(program) {
|
|
|
334
1186
|
}
|
|
335
1187
|
}
|
|
336
1188
|
|
|
337
|
-
const
|
|
1189
|
+
const authorization = options.force ? "force" : "interactive";
|
|
1190
|
+
const initialPlan = directRestorePlan({
|
|
1191
|
+
engine,
|
|
1192
|
+
checkpointId: id,
|
|
1193
|
+
checkpointNamespace: options.session,
|
|
1194
|
+
preview: restorePreview,
|
|
1195
|
+
authorization,
|
|
1196
|
+
});
|
|
1197
|
+
const execution = runCheckpointRestore({
|
|
1198
|
+
plan: initialPlan,
|
|
1199
|
+
revalidate: () => {
|
|
1200
|
+
const lockedPreview = engine.status(id, {
|
|
1201
|
+
expectedIdentity: initialPlan.checkpointIdentity,
|
|
1202
|
+
includeWorkspaceBinding: true,
|
|
1203
|
+
});
|
|
1204
|
+
return directRestorePlan({
|
|
1205
|
+
engine,
|
|
1206
|
+
checkpointId: id,
|
|
1207
|
+
checkpointNamespace: options.session,
|
|
1208
|
+
preview: lockedPreview,
|
|
1209
|
+
authorization,
|
|
1210
|
+
});
|
|
1211
|
+
},
|
|
1212
|
+
restore: ({ expectedIdentity, expectedWorkspaceBinding, hooks }) =>
|
|
1213
|
+
engine.restore(id, {
|
|
1214
|
+
expectedIdentity,
|
|
1215
|
+
expectedWorkspaceBinding,
|
|
1216
|
+
...hooks,
|
|
1217
|
+
}),
|
|
1218
|
+
dependencies: {
|
|
1219
|
+
withWorkspaceLockSync,
|
|
1220
|
+
createSagaStore: createCheckpointRestoreSagaStore,
|
|
1221
|
+
computeWorkspaceLockOwnerDigest: workspaceLockOwnerDigest,
|
|
1222
|
+
},
|
|
1223
|
+
});
|
|
1224
|
+
const r = execution.result;
|
|
1225
|
+
const directOutput = {
|
|
1226
|
+
...r,
|
|
1227
|
+
operationId: execution.operationId,
|
|
1228
|
+
phase: execution.saga.phase,
|
|
1229
|
+
warnings: execution.warnings,
|
|
1230
|
+
};
|
|
338
1231
|
if (options.json) {
|
|
339
|
-
console.log(JSON.stringify(
|
|
1232
|
+
console.log(JSON.stringify(directOutput, null, 2));
|
|
340
1233
|
return;
|
|
341
1234
|
}
|
|
342
1235
|
logger.log(
|
|
@@ -362,8 +1255,43 @@ export function registerCheckpointCommand(program) {
|
|
|
362
1255
|
chalk.red(` missing blobs (skipped): ${r.missingBlob.join(", ")}`),
|
|
363
1256
|
);
|
|
364
1257
|
}
|
|
1258
|
+
logger.log(
|
|
1259
|
+
chalk.gray(
|
|
1260
|
+
` restore operation: ${execution.operationId} (${execution.saga.phase})`,
|
|
1261
|
+
),
|
|
1262
|
+
);
|
|
1263
|
+
for (const warning of execution.warnings) {
|
|
1264
|
+
logger.log(chalk.yellow(` ${warning}`));
|
|
1265
|
+
}
|
|
365
1266
|
} catch (err) {
|
|
366
|
-
|
|
1267
|
+
if (options.json) {
|
|
1268
|
+
console.log(
|
|
1269
|
+
JSON.stringify(
|
|
1270
|
+
{
|
|
1271
|
+
ok: false,
|
|
1272
|
+
code: err?.code || "CHECKPOINT_RESTORE_FAILED",
|
|
1273
|
+
error: err?.message || String(err),
|
|
1274
|
+
operationId:
|
|
1275
|
+
err?.checkpointRestoreOperationId || err?.operationId || null,
|
|
1276
|
+
phase: err?.checkpointRestoreSagaPhase || null,
|
|
1277
|
+
blockingOperationId:
|
|
1278
|
+
err?.ownerTransactionId ||
|
|
1279
|
+
err?.retainedOwner?.transactionId ||
|
|
1280
|
+
err?.priorOwner?.transactionId ||
|
|
1281
|
+
null,
|
|
1282
|
+
recoveryRequired: Boolean(
|
|
1283
|
+
err?.checkpointRestoreRecoveryRequired ||
|
|
1284
|
+
err?.workspaceLockRetained,
|
|
1285
|
+
),
|
|
1286
|
+
workspaceLockRetained: err?.workspaceLockRetained === true,
|
|
1287
|
+
},
|
|
1288
|
+
null,
|
|
1289
|
+
2,
|
|
1290
|
+
),
|
|
1291
|
+
);
|
|
1292
|
+
} else {
|
|
1293
|
+
logger.error(chalk.red(`checkpoint restore failed: ${err.message}`));
|
|
1294
|
+
}
|
|
367
1295
|
process.exitCode = 1;
|
|
368
1296
|
}
|
|
369
1297
|
});
|