chainlesschain 0.162.189 → 0.162.197
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 +84 -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-DbZft8w0.js → AIOps-D3Gu1a5d.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DoOZpwfN.js → ActionButton-DrwAoKaz.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-B0J0KLTW.js → Analytics-C7Q-gd_3.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-D9eIR0qq.js → AppLayout-DgBtae-n.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-BnZXEVeo.js → Artifacts-pQ6LcA-W.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-X1ce4Bjn.js → Audit-DK61tocB.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BvWR-y5v.js → BackgroundAgents-BOKgDFRh.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BublSGEd.js → Backup-Bo9G4vTP.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CR_77Hso.js → BaseInput-Do_dE9_v.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DixW52tq.js → Chat-COVvZzOL.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BUUyJ5QO.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-CCv1C87K.js → Checkbox-Z9MJMolt.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B03Cc3dd.js → Codegen-BVzWKVbf.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CA06-6pU.js → Col-C8MnYcbT.js} +1 -1
- package/src/assets/web-panel/assets/{Community-Bj881J52.js → Community-DQxqw15l.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-UZLTPoaL.js → Compact-BAUmrh3y.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-kGwzRb_0.js → Compliance-jPAkQ3zK.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-KundoYM8.js → Cowork-BBQyxbh8.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BYZyoqDe.js → Cron-W5koq8Z5.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BhrrppBX.js → Crosschain-BQhnu5hS.js} +1 -1
- package/src/assets/web-panel/assets/{DID-CdC6EguZ.js → DID-DD7PlI18.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CcQx3J4y.js → Dashboard-w_ILuwKI.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BpijtEzQ.js → Dropdown-qiDUAyAo.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DfZjeZTe.js → EmailListRenderer-PDeBcI5H.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BFYSf69C.js → FamilyGuardDashboard-SuArscRM.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Okdi3pJm.js → Federation-BCovZaUn.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-mH-HebKW.js → FormItemContext-DjlFdVFk.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-xivV69x7.js → GenericCardRenderer-DoEZ7fcS.js} +1 -1
- package/src/assets/web-panel/assets/{Git-1rh_YBwg.js → Git-CV0-6GPI.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-eAXkNxdY.js → Governance-B_SFN50w.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CS6dMVTN.js → Inference-heI0Eo6k.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Bq_TnkvD.js → KnowledgeGraph-Cz5INS5p.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-KchHtiHt.js → Logs-BSFxiHkg.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-1mNozZ8v.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-CsmGcX5X.js → Marketplace-0TWTpE7k.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BaPFnnyz.js → McpTools-CV4-wBLm.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-2m-sUUf7.js → Memory-x5bH5rw5.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-D5nVZBRu.js → MobileBridge-DaBmUu9G.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-Bo8MLZvT.js → MobileProjects-DqnoFPgR.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-3GDvamGa.js → Mtc-B_zKEBif.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-D2zGuZCH.js → MtcAudit-BCfKD2yg.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BBVQ4t6t.js → Multisig-DgYfj6c_.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BIOCyOGp.js → NLProgramming-Dz09lFUm.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DVwBGpzc.js → Notes-Cuj6ugBE.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BDnHfIfX.js → NotificationSettings-DK2WXjhg.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C7Ek20ag.js → OrderTableRenderer-D1IAOQoo.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-D3SRKaa7.js → Organization-9fqnPvTw.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-C2B8ahyn.js → Overflow-n5xHkhow.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-2iwQzG65.js → P2P-B5AUWwH-.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CoPiWqMN.js → PdhVaultBrowser-CoUDV6dy.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-yV5XNXo5.js → Permissions-DPRmQf_H.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-uCDJhMUF.js → PersonalDataHub-B37bsbEN.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-hN7ucMOA.js → Pipeline-CCAuPqu9.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CgU0ptN4.js → Privacy-B81msZdv.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CfYPiT6j.js → ProjectInit-B-Py2NbF.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-B0A2C4nw.js → ProjectSettings-CYsneiRB.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BE5C0ajn.js → Projects-Y975Mh6R.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CstQPf8W.js → Providers-uO-10aP1.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-Co3z_ynk.js → QrScannerModal-Bj_6gJ0l.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DMTS6pQ6.js → QuickAsk-BgpBCMPE.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-ETw4odUa.js → Recommend-DhUW3YUA.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-DdeyfbkA.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-BTVqQgKq.js → Reputation-fxW421xd.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CBKKwwtO.js → Row-De73w1og.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BHUQ438I.js → RssFeed-3US_Q_lj.js} +2 -2
- package/src/assets/web-panel/assets/{Search-B4NqeUsF.js → Search-DbZ_DU74.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BRjoYG5_.js → Security-DoIU1vMF.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CxQzu3p3.js → Services-DB_RGqpc.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BKzwwmWO.js → Skeleton-DnzLaayC.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DrsFcgth.js → Skills-VzVXHENF.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-D21tmXCH.js → Sla-DMfvjCAI.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CjyjLfp8.js → SpeechSettings-DQQOYWbF.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Cu45-tnx.js → SyncSettings-Bmx3FG1P.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-oMcILYOj.js → Tasks-DOQyfb-2.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Bh-Zcr5R.js → Templates-D6hTRT7c.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-68ns63aI.js → Tenant-CTPQZAzs.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BLJkFykE.js → Terminal-B7vII3oH.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-xcrJLq-O.js → TimelineRenderer-BkiFJeXV.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-C6PpFJtf.js → Tokens-BPPdQMNo.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BaKA7tdM.js → Trigger-BxG8JAEI.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BzhA57Jc.js → Trust-DHD7nMpM.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-4_U3JkIR.js → UkeySign-B4BJpU47.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-162AANxX.js → VideoEditing-CRiGpFx2.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-B1viuql_.js → Wallet-BSDSNiem.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-ZVWazhCy.js → WebAuthn-DLTUh5Zv.js} +5 -5
- package/src/assets/web-panel/assets/WorkflowEditor-tW2MwjhD.js +1 -0
- package/src/assets/web-panel/assets/{chat-DmrHNuwq.js → chat-SgsLQ3FC.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DAJ_M-PK.js → colors-BHjjLIKO.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-uIzmYwLs.js → compact-item-VceErdKq.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-D_ZmuLwb.js → createContext-BZ332MYl.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DosMaL-k.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CDt_shTL.js → hasIn-CBTjJOjn.js} +1 -1
- package/src/assets/web-panel/assets/index-0O5ePnUq.js +1 -0
- package/src/assets/web-panel/assets/{index-rXdwUeE3.js → index-1n7xjKDU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAvSmJXW.js → index-29wVzi-k.js} +1 -1
- package/src/assets/web-panel/assets/{index-C16fYKFD.js → index-B2u26AS3.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5WcdJH2.js → index-B40dQn0G.js} +3 -3
- package/src/assets/web-panel/assets/{index-CaelyMgF.js → index-B5KAGxNo.js} +1 -1
- package/src/assets/web-panel/assets/{index-OzTxFIJP.js → index-BC1xuQgX.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzXqvMuP.js → index-BETEjuNr.js} +1 -1
- package/src/assets/web-panel/assets/{index-clSX4PEc.js → index-BOtvPdHt.js} +1 -1
- package/src/assets/web-panel/assets/{index-saf3VNeP.js → index-BPXFLYdW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdUMcwRB.js → index-BQSmZGgs.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfSSy9uy.js → index-BRWhLbWF.js} +1 -1
- package/src/assets/web-panel/assets/{index-GcKOHD_c.js → index-BTlc-Bve.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHvlm0In.js → index-BY1wexYG.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cvak6Oe2.js → index-BcUenmCX.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKyd472S.js → index-BeiqkxJG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvFhhe6r.js → index-BhXZEUAc.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBFmcaCQ.js → index-By5jBtkV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DqDSF2RW.js → index-CLc19alH.js} +1 -1
- package/src/assets/web-panel/assets/{index-DeGBoLQA.js → index-CMVvxIS8.js} +1 -1
- package/src/assets/web-panel/assets/{index-FyplHuNt.js → index-C_B9rFQE.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeUpKjvi.js → index-CaIS_TNX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRa4kJ_S.js → index-CawpdGTW.js} +1 -1
- package/src/assets/web-panel/assets/index-ChAzPigF.js +1 -0
- package/src/assets/web-panel/assets/{index-QsgfSZrV.js → index-CpumS98o.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDF6EQBs.js → index-Cr4rjlW5.js} +1 -1
- package/src/assets/web-panel/assets/{index-rS3gl_68.js → index-CrUgDQkf.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4n1lGTB.js → index-CtjLcCNU.js} +1 -1
- package/src/assets/web-panel/assets/{index-ifxmu8SY.js → index-DUkVN794.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTCXf1t-.js → index-D_SePDWj.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bbxjfstq.js → index-DaCCy6lq.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9OE8B3d.js → index-DdefYBSw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CG0lY4h3.js → index-DePOeMDM.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bx4d6c1k.js → index-DgPvuIn2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Z8P4BexE.js → index-FJ3zC9LK.js} +1 -1
- package/src/assets/web-panel/assets/{index-ByzGvpR9.js → index-_PHrIO4O.js} +1 -1
- package/src/assets/web-panel/assets/{index-DwfvPe-j.js → index-fU7wNHDB.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcZ9xJI9.js → index-iYQNU9za.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbD_WW_6.js → index-vIXOpcjD.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-pmfaD-aK.js → initDefaultProps-D1oZNz0D.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CaQ2CpYF.js → motion-C8k6doMO.js} +1 -1
- package/src/assets/web-panel/assets/{move-CkKTqNbI.js → move-DJ4qSbYt.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-Dd4xsgRI.js → mtc-parser-CdpJD85j.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BFuA5YEs.js → omit-B0V4slKA.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DDfxOAJc.js → pickAttrs-Di5NiW1F.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Be595JCp.js → placementArrow-B5OMHgEM.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-7byljUM3.js → responsiveObserve-C6U9IWql.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CTWfOTaY.js → slide-BMyTH3rN.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CNZOuIxM.js → statusUtils-C7d6BG7Z.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DwOcBV78.js → styleChecker-CR50QnQy.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-B6MNwAO9.js → useFlexGapSupport-DBEeoMb8.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CzyQjH9r.js → useFs-3r099cjv.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-Dxh3KiVA.js → usePersonalDataHub-C5IYNdTK.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DVfZiS5N.js → vnode-DmEOLRHf.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BYme7fbY.js → zoom-DnpUiVCB.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 +1271 -175
- package/src/command-surface-policy.js +312 -0
- package/src/commands/agent.js +76 -33
- package/src/commands/artifacts.js +400 -2
- package/src/commands/background-session.js +41 -3
- package/src/commands/checkpoint-restore-recovery.js +1476 -0
- package/src/commands/checkpoint.js +947 -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/update.js +68 -6
- package/src/constants.js +11 -0
- package/src/data/changelog.json +14 -1
- 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 +1 -0
- package/src/lib/agent-sandbox.js +120 -26
- package/src/lib/agent-session-export.js +20 -0
- package/src/lib/agent-team/team-runner.js +230 -8
- 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/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 +53 -8
- package/src/lib/process-execution-broker/workspace-transaction.js +976 -73
- 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 +278 -0
- package/src/lib/public-ide-capability-validation.js +351 -0
- package/src/lib/secret-store.js +267 -43
- 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/with-file-lock.js +25 -1
- 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 +2332 -154
- 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 +404 -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 +25 -4
- package/src/repl/terminal-layout.js +88 -0
- package/src/runtime/agent-core.js +1264 -325
- 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 +298 -118
- package/src/runtime/headless-stream.js +353 -95
- package/src/runtime/mcp-config.js +148 -14
- package/src/runtime/status-diagnostics-lite.js +137 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DDHDpBBA.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-TabqHW9B.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-B0rkE09d.js +0 -5
- package/src/assets/web-panel/assets/WorkflowEditor-tzdyUk-1.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DIrncrV1.js +0 -1
- package/src/assets/web-panel/assets/index-D-Swwqld.js +0 -1
- package/src/assets/web-panel/assets/index-FMZAMKy8.js +0 -1
|
@@ -20,9 +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";
|
|
25
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
|
+
}
|
|
26
123
|
|
|
27
124
|
/** git-plumbing engine adapter (normalized interface). */
|
|
28
125
|
function gitEngine(gs, dir, session) {
|
|
@@ -43,12 +140,37 @@ function gitEngine(gs, dir, session) {
|
|
|
43
140
|
label: r.label,
|
|
44
141
|
createdAt: r.createdAt,
|
|
45
142
|
fileCount: null,
|
|
143
|
+
identity: r.commit ? `git:${r.commit}` : null,
|
|
46
144
|
})),
|
|
47
145
|
show: (id) => gs.showCheckpoint(dir, id, { session }),
|
|
48
|
-
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
|
+
},
|
|
49
161
|
diffText: (id, o) => gs.diffCheckpoint(dir, id, { session, stat: o?.stat }),
|
|
50
162
|
restore: (id, o) => {
|
|
51
|
-
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
|
+
});
|
|
52
174
|
return {
|
|
53
175
|
dryRun: !!r.dryRun,
|
|
54
176
|
restoredCount: r.modified + r.recreated,
|
|
@@ -56,6 +178,9 @@ function gitEngine(gs, dir, session) {
|
|
|
56
178
|
recreated: r.recreated,
|
|
57
179
|
deleted: r.deleted,
|
|
58
180
|
safetyId: r.safetyId,
|
|
181
|
+
safetyIdentity: r.safetyIdentity,
|
|
182
|
+
safetyPlanIdentity: r.safetyPlanIdentity,
|
|
183
|
+
safetyCoverage: r.safetyCoverage,
|
|
59
184
|
};
|
|
60
185
|
},
|
|
61
186
|
remove: (id) => gs.deleteCheckpoint(dir, id, { session }),
|
|
@@ -77,14 +202,15 @@ function copyEngine(cs, dir) {
|
|
|
77
202
|
};
|
|
78
203
|
},
|
|
79
204
|
list: () =>
|
|
80
|
-
cs.listCheckpoints().map((c) => ({
|
|
205
|
+
cs.listCheckpoints({ cwd: dir }).map((c) => ({
|
|
81
206
|
id: c.id,
|
|
82
207
|
label: c.label,
|
|
83
208
|
createdAt: c.createdAt,
|
|
84
209
|
fileCount: c.fileCount,
|
|
210
|
+
identity: c.identity || null,
|
|
85
211
|
})),
|
|
86
212
|
show: (id) => {
|
|
87
|
-
const m = cs.getCheckpoint(id);
|
|
213
|
+
const m = cs.getCheckpoint(id, { cwd: dir });
|
|
88
214
|
if (!m) throw new Error(`no such checkpoint: ${id}`);
|
|
89
215
|
return {
|
|
90
216
|
id: m.id,
|
|
@@ -94,27 +220,50 @@ function copyEngine(cs, dir) {
|
|
|
94
220
|
files: m.files.map((f) => ({ rel: f.rel, bytes: f.bytes })),
|
|
95
221
|
};
|
|
96
222
|
},
|
|
97
|
-
status: (id) => {
|
|
98
|
-
const d = cs.diffCheckpoint(id
|
|
99
|
-
|
|
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
|
+
};
|
|
100
238
|
},
|
|
101
239
|
diffText: () => null, // copy engine has no raw patch — caller uses status()
|
|
102
240
|
restore: (id, o) => {
|
|
103
|
-
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
|
+
});
|
|
104
252
|
return {
|
|
105
253
|
dryRun: !!r.dryRun,
|
|
106
254
|
restoredCount: r.restored.length,
|
|
107
255
|
restored: r.restored,
|
|
108
256
|
missingBlob: r.missingBlob,
|
|
109
257
|
safetyId: r.safetyId,
|
|
258
|
+
safetyIdentity: r.safetyIdentity,
|
|
259
|
+
safetyPlanIdentity: r.safetyPlanIdentity,
|
|
260
|
+
safetyCoverage: r.safetyCoverage,
|
|
261
|
+
createdPaths: r.createdPaths,
|
|
262
|
+
deletedPaths: r.deletedPaths,
|
|
110
263
|
};
|
|
111
264
|
},
|
|
112
|
-
remove: (id) => cs.deleteCheckpoint(id),
|
|
113
|
-
clear: () => {
|
|
114
|
-
const all = cs.listCheckpoints();
|
|
115
|
-
for (const c of all) cs.deleteCheckpoint(c.id);
|
|
116
|
-
return all.length;
|
|
117
|
-
},
|
|
265
|
+
remove: (id) => cs.deleteCheckpoint(id, { cwd: dir }),
|
|
266
|
+
clear: () => cs.clearCheckpoints({ cwd: dir }),
|
|
118
267
|
};
|
|
119
268
|
}
|
|
120
269
|
|
|
@@ -126,14 +275,148 @@ async function pickEngine(dir, session) {
|
|
|
126
275
|
return copyEngine(cs, dir);
|
|
127
276
|
}
|
|
128
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
|
+
|
|
129
320
|
const tag = (engine) => chalk.dim(engine.kind === "git" ? "[git]" : "[copy]");
|
|
130
321
|
|
|
131
|
-
|
|
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;
|
|
132
411
|
const cp = program
|
|
133
412
|
.command("checkpoint")
|
|
134
413
|
.description("Snapshot / rewind file state (git-plumbing, copy fallback)");
|
|
135
414
|
|
|
136
415
|
registerManagedCheckpointCommands(cp);
|
|
416
|
+
registerCheckpointRestoreRecoveryCommands(
|
|
417
|
+
cp,
|
|
418
|
+
dependencies.checkpointRestoreRecovery,
|
|
419
|
+
);
|
|
137
420
|
|
|
138
421
|
cp.command("create [paths...]")
|
|
139
422
|
.description(
|
|
@@ -226,6 +509,566 @@ export function registerCheckpointCommand(program) {
|
|
|
226
509
|
}
|
|
227
510
|
});
|
|
228
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
|
+
|
|
229
1072
|
cp.command("show <id>")
|
|
230
1073
|
.description("Show a checkpoint's files, or its diff vs current state")
|
|
231
1074
|
.option("-d, --dir <dir>", "Target directory", ".")
|
|
@@ -310,12 +1153,18 @@ export function registerCheckpointCommand(program) {
|
|
|
310
1153
|
return;
|
|
311
1154
|
}
|
|
312
1155
|
|
|
313
|
-
// Destructive:
|
|
314
|
-
//
|
|
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
|
+
});
|
|
315
1163
|
if (!options.force) {
|
|
316
|
-
const d = engine.status(id);
|
|
317
1164
|
const willChange =
|
|
318
|
-
|
|
1165
|
+
restorePreview.modified.length +
|
|
1166
|
+
restorePreview.deleted.length +
|
|
1167
|
+
(restorePreview.added?.length || 0);
|
|
319
1168
|
if (process.stdin.isTTY) {
|
|
320
1169
|
const { confirm } = await import("@inquirer/prompts");
|
|
321
1170
|
const ok = await confirm({
|
|
@@ -337,9 +1186,50 @@ export function registerCheckpointCommand(program) {
|
|
|
337
1186
|
}
|
|
338
1187
|
}
|
|
339
1188
|
|
|
340
|
-
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
|
+
};
|
|
341
1231
|
if (options.json) {
|
|
342
|
-
console.log(JSON.stringify(
|
|
1232
|
+
console.log(JSON.stringify(directOutput, null, 2));
|
|
343
1233
|
return;
|
|
344
1234
|
}
|
|
345
1235
|
logger.log(
|
|
@@ -365,8 +1255,43 @@ export function registerCheckpointCommand(program) {
|
|
|
365
1255
|
chalk.red(` missing blobs (skipped): ${r.missingBlob.join(", ")}`),
|
|
366
1256
|
);
|
|
367
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
|
+
}
|
|
368
1266
|
} catch (err) {
|
|
369
|
-
|
|
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
|
+
}
|
|
370
1295
|
process.exitCode = 1;
|
|
371
1296
|
}
|
|
372
1297
|
});
|