chainlesschain 0.162.188 → 0.162.193
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -11
- package/bin/chainlesschain.js +7 -52
- package/completions/_cc +209 -0
- package/completions/cc.bash +18 -0
- package/completions/cc.fish +13 -0
- package/completions/cc.ps1 +226 -0
- package/package.json +10 -2
- package/src/assets/web-panel/assets/{AIOps-Brr1LVcE.js → AIOps-DBLkaTje.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DQU74DKG.js → ActionButton-BAy-Qebo.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CYp9178y.js → Analytics-Dfb1Mlau.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DW4SCYsW.js → AppLayout-BbFkN8m0.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CYlD_ed1.js → Artifacts-kM4-cEEH.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CWl1rZOi.js → Audit-BSQfYWf3.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BOR-LPsa.js → BackgroundAgents-CWDbI_fG.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DLpG7j1X.js → Backup-BxLPbq_Q.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-B35K9wdr.js → BaseInput-DuIbdrp6.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CwUJb9gB.js → Chat-NEmjGahU.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-1v6aMS1m.js → ChatBubbleRenderer-DfJEo-5_.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-pi3-tPNW.js → Checkbox-CaoeG3Ns.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-FyNF35eL.js → Codegen-BFf_g3xY.js} +1 -1
- package/src/assets/web-panel/assets/{Col-tGt_JDEr.js → Col-CNOd8mdQ.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CC7LonQX.js → Community-BhpDqz-i.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BGXrHHjE.js → Compact-CQ42T7Z6.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DSW0TBZ8.js → Compliance-DI5PcLMN.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Bhk7rbvE.js → Cowork-wYYZe0wR.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DxyiQ2NZ.js → Cron-Dlq03DD_.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-B2ZkxHok.js → Crosschain-BzqS8PP3.js} +1 -1
- package/src/assets/web-panel/assets/{DID-yOJV99eJ.js → DID-It55Yem7.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CKsttz7D.js → Dashboard-9HkH6oxn.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CVFldXSL.js → Dropdown-00e2YWRB.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Bufv638J.js → EmailListRenderer-By-ugRDL.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-4ncjoIjL.js → FamilyGuardDashboard-C396yu5m.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DsknEDoX.js → Federation-BME5sbZY.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Cqe5jWXA.js → FormItemContext-B4TEZCr-.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-NAJaFKxc.js → GenericCardRenderer-_9CX9ieY.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CszKQpnl.js → Git-BrS6WVSr.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BXGodXi6.js → Governance-BMdCWnKF.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CkoYuG2c.js → Inference-Bcp3vzcA.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DFlEr_T0.js → KnowledgeGraph-DlLuqGY5.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CVaENddA.js → Logs-D-W_6x09.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-DElGPxCZ.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-CDlLT0fw.js → Marketplace-Dzn2guvI.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DdwOyszN.js → McpTools-uPict-KL.js} +6 -6
- package/src/assets/web-panel/assets/{Memory-DKQnkKuG.js → Memory-BCw43kOb.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DGseqlzJ.js → MobileBridge-DmV8Wv7F.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-COBbEOlj.js → MobileProjects-C-HaCmSM.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-O61Vwyli.js → Mtc-C6jxJ34Y.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Dw2SyasS.js → MtcAudit-Za8ExwhQ.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-hSu_l8kf.js → Multisig-KKDbuEgb.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CX1Epurr.js → NLProgramming-Dj97zbCk.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-R9xX8o8_.js → Notes-DBXvMraU.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-jtSNLCuK.js → NotificationSettings-DOxITLYl.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-7DVjZwMM.js +1 -0
- package/src/assets/web-panel/assets/{Organization-Pu2WO-U3.js → Organization-D1i73mjb.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BjnqUyFp.js → Overflow-kW6Q3rvg.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-jjkvz44c.js → P2P-C-v_8p2v.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-tUbbY64W.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-D4yDpivd.js → Permissions-DkHxEZ58.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-0qsL6iH1.js → PersonalDataHub-DWuMPGrK.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-Cx7Ybrk2.js → Pipeline-30IYXWWm.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-PbfR_M_V.js → Privacy-DZNQ4RrH.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CyV6Gsub.js → ProjectInit-De2MCElf.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C7KZ2Wdk.js → ProjectSettings-5uHk9wsC.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DQDjO-yK.js → Projects-XjDfrFLb.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DyFVBBMX.js → Providers-Bgwj6gcE.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CzB1BC9_.js → QrScannerModal-gKql9umv.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Bk636mC1.js → QuickAsk-BWvmwkh7.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DoWxHIT3.js → Recommend-Uok4hZPd.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-oOVIv9wq.js → RemoteSession-DLJzTk9a.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-BucoReGG.js → Reputation-ZVwIPMSO.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DzmOdJU7.js → Row-CN04HxWR.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-C1tK7KGU.js → RssFeed-BdSrEG4g.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CSnInOqk.js → Search-5PwVd0IR.js} +1 -1
- package/src/assets/web-panel/assets/{Security-dlejr15l.js → Security-DdBo4zjN.js} +3 -3
- package/src/assets/web-panel/assets/{Services-BioPZuNh.js → Services-D3letU7M.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DWMibwpC.js → Skeleton--Jb83f1v.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BAAnx1B2.js → Skills-Bg-CulHX.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-jWNp4hNO.js → Sla-7QOa7S-X.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-y5kqKha-.js → SpeechSettings-DQwppjRw.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-B9GNsc3e.js → SyncSettings-dNPQGL-s.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BDBYKRen.js → Tasks-Ggs-Z48q.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DRiE5eaN.js → Templates-78TYHgRO.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BGlAvExF.js → Tenant-xHwwv9vO.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B8uzrbNY.js → Terminal-BJXSJfm4.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Df48iveY.js → TimelineRenderer-fLbjRhJw.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CcmHndN6.js → Tokens-oMSBSBmB.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DPkWxmQ2.js → Trigger-CfnY_wX_.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DTb5a5AV.js → Trust-AdyTgYKO.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CBmg5vch.js → UkeySign-C5K_81dS.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B0mZrz4O.js → VideoEditing-vcGVDqki.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Bxs7_mpd.js → Wallet-hhOc3hlY.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-C8x3cMxU.js → WebAuthn-CVloC11s.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-DPrNznrP.js → WorkflowEditor-CYeE32R5.js} +1 -1
- package/src/assets/web-panel/assets/{chat-D3bYWu7S.js → chat-CNROR5bN.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DY-PwMu4.js → colors-Cjx-cnwU.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-LDk10Kw6.js → compact-item-DOIfelvD.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C-2MZnFq.js → createContext-DfA7UZ8u.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BQIBjcnB.js → hasIn-BMSCzcfM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQVb2dWp.js → index-5-HXgOX5.js} +1 -1
- package/src/assets/web-panel/assets/{index-BdXnYMCZ.js → index-AWg-0M1S.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cx6D_dx2.js → index-B4iYzpkL.js} +1 -1
- package/src/assets/web-panel/assets/{index-_HRO_cIu.js → index-B5Udeb_6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlRYqt3S.js → index-B7gf77CJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BrnRO9yE.js → index-BEMwBBHk.js} +1 -1
- package/src/assets/web-panel/assets/{index-VCh5zNJo.js → index-BM1y_9hM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRI3ngNg.js → index-BUonmKSE.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3GNvExG.js → index-BbQ2eQ8H.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSvo_bp0.js → index-BemJ2u_E.js} +1 -1
- package/src/assets/web-panel/assets/{index-L6qixyLc.js → index-BolaOQxx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BI4KgcYC.js → index-BxOk9vkG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CY4KnaO9.js → index-By61ojUx.js} +1 -1
- package/src/assets/web-panel/assets/{index-L3smcoQX.js → index-C2K9SNVX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BX2b9Orc.js → index-C3L5gdiD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cz4b6Z_7.js → index-C7xn0Nub.js} +1 -1
- package/src/assets/web-panel/assets/{index-COG61sRv.js → index-CAnvhwg6.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7FC8cP2.js → index-CEKMcOyU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BN-xdSWZ.js → index-CNC5onm4.js} +1 -1
- package/src/assets/web-panel/assets/{index-DY2FIrmz.js → index-CVL27kXC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiTvBAr-.js → index-CVm1_i3t.js} +1 -1
- package/src/assets/web-panel/assets/{index-DhRt9cYJ.js → index-CdYBye8B.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5pgtZnY.js → index-ClNMrqEY.js} +1 -1
- package/src/assets/web-panel/assets/{index-7xKu7Fns.js → index-Cs6l7uRu.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4K1weKc.js → index-CshQbilz.js} +1 -1
- package/src/assets/web-panel/assets/{index-k7C_oDCO.js → index-Cyweh0Hy.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3Oq-DLL.js → index-DBpWXFed.js} +3 -3
- package/src/assets/web-panel/assets/{index-DYyLJPlF.js → index-DHtpHZAQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dh-tkpKQ.js → index-DLmn0BoK.js} +1 -1
- package/src/assets/web-panel/assets/{index-8JC9BJa1.js → index-DPAWBrMf.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dd2Km97U.js → index-DjEsxO5h.js} +1 -1
- package/src/assets/web-panel/assets/{index-KFZO_lcx.js → index-FBc_CJBd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTIZpPtG.js → index-Icdoqmzt.js} +1 -1
- package/src/assets/web-panel/assets/index-Q2IajL54.js +1 -0
- package/src/assets/web-panel/assets/index-bRkHgOMc.js +1 -0
- package/src/assets/web-panel/assets/{index-lmm9p-jm.js → index-dCd8cSUq.js} +1 -1
- package/src/assets/web-panel/assets/{index-HasPt4-h.js → index-m2vG9gG_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaslYch6.js → index-mTpHoEzr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbR6ROOa.js → index-ti_lnTWl.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-zQPSHf2C.js → initDefaultProps-Bu8k7H1Y.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CKCStGVk.js → motion-s3iVK41k.js} +1 -1
- package/src/assets/web-panel/assets/{move-BufVDGYF.js → move-B6B-E5Jz.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DcaFxy5D.js → mtc-parser-BiYgTidA.js} +1 -1
- package/src/assets/web-panel/assets/{omit-EScpkGdo.js → omit-pU1RdUDA.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-72hG_qfT.js → pickAttrs-Cdoq_SeM.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CMFWiH20.js → placementArrow-DIuvAn2K.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DKQHS41p.js → responsiveObserve-8w16YR2D.js} +1 -1
- package/src/assets/web-panel/assets/{slide-QVAFHTIQ.js → slide-VZ01cZe3.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DIz38e2G.js → statusUtils-lh8YMN8Z.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-zK5cyLby.js → styleChecker-CmDyreec.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DhZngk62.js → useFlexGapSupport-hiJWMU5y.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BvV2cvkg.js → useFs-Thnu-Iaj.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CRmxb9l3.js → usePersonalDataHub-LBdOxjwY.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CgQIbobP.js → vnode-Cg6lFY_X.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-D4XrrO0R.js → zoom-DVfsR1YO.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/cli-help.js +181 -0
- package/src/command-help-index.json +181 -0
- package/src/command-manifest.json +1107 -175
- package/src/command-surface-policy.js +197 -0
- package/src/commands/agent.js +98 -33
- package/src/commands/artifacts.js +400 -2
- package/src/commands/background-session.js +41 -3
- package/src/commands/checkpoint-managed.js +1059 -0
- package/src/commands/checkpoint-restore-recovery.js +1476 -0
- package/src/commands/checkpoint.js +950 -22
- package/src/commands/compact.js +101 -25
- package/src/commands/config.js +506 -20
- package/src/commands/doctor.js +4 -1
- package/src/commands/hub.js +1 -1
- package/src/commands/mcp.js +688 -24
- package/src/commands/pack.js +16 -2
- package/src/commands/plugin.js +20 -12
- package/src/commands/session-mcp-recovery.js +218 -0
- package/src/commands/session.js +470 -110
- package/src/commands/setup.js +36 -10
- package/src/commands/skill.js +181 -110
- package/src/commands/status.js +21 -5
- package/src/commands/team-distributed.js +3324 -0
- package/src/commands/team.js +1130 -74
- package/src/commands/update.js +68 -6
- package/src/constants.js +11 -0
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/action-protocol.js +10 -0
- package/src/gateways/ws/session-protocol.js +555 -27
- package/src/gateways/ws/ws-agent-handler.js +541 -14
- package/src/gateways/ws/ws-session-gateway.js +129 -22
- package/src/gateways/ws/ws-session-state.js +431 -0
- package/src/harness/background-task-manager.js +233 -36
- package/src/harness/jsonl-session-store.js +2551 -167
- package/src/harness/mcp-client.js +802 -61
- package/src/harness/prompt-compressor.js +227 -27
- package/src/harness/session-index.js +89 -38
- package/src/harness/session-list-index.js +254 -0
- package/src/harness/structured-handoff.js +410 -0
- package/src/lazy-dispatch.js +747 -84
- package/src/lib/__tests__/logger.test.js +50 -2
- package/src/lib/advisor-runtime.js +1047 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/agent-sandbox.js +120 -26
- package/src/lib/agent-session-export.js +20 -0
- package/src/lib/agent-team/task-lease.js +271 -3
- package/src/lib/agent-team/team-adjudication.js +1501 -0
- package/src/lib/agent-team/team-budget.js +4 -1
- package/src/lib/agent-team/team-control-store.js +582 -0
- package/src/lib/agent-team/team-distributed-queue.js +5246 -0
- package/src/lib/agent-team/team-process-checkpoint.js +211 -0
- package/src/lib/agent-team/team-runner.js +543 -24
- package/src/lib/agent-team/team-worktree.js +1403 -95
- package/src/lib/artifact-store.js +131 -8
- package/src/lib/background-agent-supervisor.js +185 -13
- package/src/lib/background-interaction-journal.js +37 -9
- package/src/lib/background-launch-profile.js +1058 -0
- package/src/lib/checkpoint-restore-already-completed-controller.js +902 -0
- package/src/lib/checkpoint-restore-orchestrator.js +987 -0
- package/src/lib/checkpoint-restore-partial-rollback-controller.js +1953 -0
- package/src/lib/checkpoint-restore-recovery-controller.js +689 -0
- package/src/lib/checkpoint-restore-recovery.js +884 -0
- package/src/lib/checkpoint-restore-retention.js +126 -0
- package/src/lib/checkpoint-restore-saga.js +5079 -0
- package/src/lib/checkpoint-restore-session-recovery.js +1178 -0
- package/src/lib/checkpoint-restore-workspace-target-verifier.js +270 -0
- package/src/lib/checkpoint-store.js +1688 -97
- package/src/lib/checkpoint-timeline-authority.js +522 -0
- package/src/lib/checkpoint-timeline.js +642 -0
- package/src/lib/config-keys.js +18 -119
- package/src/lib/config-manager.js +446 -27
- package/src/lib/config-redaction.js +57 -0
- package/src/lib/config-schema.js +853 -0
- package/src/lib/cowork-mcp-tools.js +5 -0
- package/src/lib/cowork-task-runner.js +458 -63
- package/src/lib/delivery-coordinator.js +1234 -0
- package/src/lib/delivery-evidence.js +450 -0
- package/src/lib/doctor-checkup.js +199 -14
- package/src/lib/executable-path.js +34 -0
- package/src/lib/fatal-handler.js +16 -0
- package/src/lib/file-checkpoint.js +4909 -186
- package/src/lib/file-lines.js +125 -0
- package/src/lib/headless-config-command.js +16 -5
- package/src/lib/hook-runner.cjs +139 -38
- package/src/lib/hooks-v2-workspace-context.js +10 -0
- package/src/lib/impacted-gate-selector.js +424 -0
- package/src/lib/interaction-adapter.js +62 -0
- package/src/lib/jsonl-session-store.js +1 -35
- package/src/lib/logger.js +66 -14
- package/src/lib/managed-checkpoint-render.js +60 -0
- package/src/lib/managed-tool-checkpoint.js +194 -0
- package/src/lib/mcp-call-ledger-store.js +1069 -0
- package/src/lib/mcp-call-ledger.js +747 -0
- package/src/lib/mcp-client.js +5 -0
- package/src/lib/mcp-conflict-scheduler.js +468 -0
- package/src/lib/mcp-effect-contract.js +102 -0
- package/src/lib/mcp-headers-helper-trust.js +145 -0
- package/src/lib/mcp-headers-helper.js +616 -0
- package/src/lib/mcp-host-recovery-runtime.js +131 -0
- package/src/lib/mcp-ledger-recovery-admission.js +1065 -0
- package/src/lib/mcp-recovery-adjudication.js +443 -0
- package/src/lib/output-context.js +86 -0
- package/src/lib/packer/file-identity.js +170 -0
- package/src/lib/packer/index.js +3 -0
- package/src/lib/packer/native-update-state.js +277 -0
- package/src/lib/packer/pack-update-applier.js +1983 -136
- package/src/lib/packer/pack-update-checker.js +34 -0
- package/src/lib/packer/pack-update-downloader.js +875 -86
- package/src/lib/packer/pack-update-signature.js +86 -0
- package/src/lib/packer/pkg-config-generator.js +6 -0
- package/src/lib/paths.js +65 -10
- package/src/lib/plan-mode.js +831 -14
- package/src/lib/plan-persistence.js +540 -0
- package/src/lib/plugin-runtime/hooks.js +227 -69
- package/src/lib/plugin-runtime/plugin-options.js +189 -44
- package/src/lib/pr-automation-policy.js +146 -8
- package/src/lib/process-execution-broker/credential-agent.js +7 -0
- package/src/lib/process-execution-broker/index.js +491 -13
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
- package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
- package/src/lib/process-execution-broker/workspace-transaction.js +4459 -0
- package/src/lib/project-instructions.js +165 -29
- package/src/lib/project-mcp-trust.js +67 -0
- package/src/lib/public-ide-capability-manifest.js +272 -0
- package/src/lib/public-ide-capability-validation.js +351 -0
- package/src/lib/secret-store.js +267 -43
- package/src/lib/secure-file-identity.js +433 -0
- package/src/lib/secure-fs.js +1089 -0
- package/src/lib/security-audit.js +58 -0
- package/src/lib/service-manager.js +34 -13
- package/src/lib/session-budget-runtime.js +1452 -0
- package/src/lib/session-host-snapshot.js +388 -0
- package/src/lib/session-message-provenance.js +429 -0
- package/src/lib/session-projection.js +804 -0
- package/src/lib/session-resource-budget.js +1495 -0
- package/src/lib/settings-hooks.cjs +138 -22
- package/src/lib/settings-loader.cjs +25 -2
- package/src/lib/side-effect-ledger-store.js +45 -23
- package/src/lib/skill-execution-identity.js +217 -0
- package/src/lib/skill-loader.js +299 -18
- package/src/lib/sub-agent-context.js +236 -28
- package/src/lib/sub-agent-registry.js +5 -0
- package/src/lib/subagent-contract.js +94 -0
- package/src/lib/todo-manager.js +185 -18
- package/src/lib/todo-persistence.js +417 -0
- package/src/lib/turn-binding-store.js +41 -23
- package/src/lib/turn-binding.js +160 -2
- package/src/lib/with-file-lock.js +165 -24
- package/src/lib/ws-chat-handler.js +21 -0
- package/src/program-base.js +18 -0
- package/src/repl/advisor-command.js +103 -0
- package/src/repl/agent-repl.js +2175 -146
- package/src/repl/btw-command.js +93 -10
- package/src/repl/clipboard-image.js +119 -0
- package/src/repl/config-summary.js +64 -108
- package/src/repl/prompt-editor.js +233 -0
- package/src/repl/prompt-interactions.js +344 -0
- package/src/repl/prompt-stash.js +354 -0
- package/src/repl/prompt-suggestions.js +312 -0
- package/src/repl/repl-keybindings.js +227 -0
- package/src/repl/session-recap.js +217 -0
- package/src/repl/slash-command-registry.js +22 -0
- package/src/repl/terminal-layout.js +88 -0
- package/src/runtime/agent-core.js +1630 -447
- package/src/runtime/coding-agent-contract-shared.cjs +13 -1
- package/src/runtime/coding-agent-managed-tool-policy.cjs +7 -2
- package/src/runtime/diagnostics.js +32 -13
- package/src/runtime/headless-runner.js +310 -118
- package/src/runtime/headless-stream.js +356 -95
- package/src/runtime/mcp-config.js +148 -14
- package/src/runtime/policies/agent-policy.js +14 -0
- package/src/runtime/status-diagnostics-lite.js +137 -0
- package/src/assets/web-panel/assets/MarkdownRenderer-7iWGwZ-r.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-BU2Yqq5H.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-69ca_WeR.js +0 -7
- package/src/assets/web-panel/assets/devWarning-B4dXIJqD.js +0 -1
- package/src/assets/web-panel/assets/index-C-C7DaG6.js +0 -1
- package/src/assets/web-panel/assets/index-DkfWzWtd.js +0 -1
package/src/lib/plan-mode.js
CHANGED
|
@@ -8,6 +8,29 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { EventEmitter } from "events";
|
|
11
|
+
import {
|
|
12
|
+
createPlanSessionEvent,
|
|
13
|
+
PLAN_PERSISTENCE_ERROR_CODES,
|
|
14
|
+
PLAN_SESSION_LEGACY_SNAPSHOT_VERSION,
|
|
15
|
+
PLAN_SESSION_SNAPSHOT_SCHEMA,
|
|
16
|
+
PLAN_SESSION_SNAPSHOT_VERSION,
|
|
17
|
+
PlanPersistenceError,
|
|
18
|
+
PlanSessionPersistence,
|
|
19
|
+
} from "./plan-persistence.js";
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
isUnsafePlanSessionId,
|
|
23
|
+
PLAN_PERSISTENCE_ERROR_CODES,
|
|
24
|
+
PLAN_SESSION_EVENT_SCHEMA,
|
|
25
|
+
PLAN_SESSION_EVENT_VERSION,
|
|
26
|
+
PLAN_SESSION_LEGACY_SNAPSHOT_VERSION,
|
|
27
|
+
PLAN_SESSION_SNAPSHOT_SCHEMA,
|
|
28
|
+
PLAN_SESSION_SNAPSHOT_VERSION,
|
|
29
|
+
planSnapshotPath,
|
|
30
|
+
PlanPersistenceError,
|
|
31
|
+
PlanSessionPersistence,
|
|
32
|
+
resolvePlanStateDir,
|
|
33
|
+
} from "./plan-persistence.js";
|
|
11
34
|
|
|
12
35
|
/**
|
|
13
36
|
* Plan item status
|
|
@@ -91,10 +114,14 @@ export class PlanItem {
|
|
|
91
114
|
this.tool = data.tool || null;
|
|
92
115
|
this.params = data.params || {};
|
|
93
116
|
this.dependencies = data.dependencies || [];
|
|
117
|
+
this.owner = data.owner ?? null;
|
|
118
|
+
this.checkpoint = data.checkpoint ?? null;
|
|
119
|
+
this.approval = data.approval ?? null;
|
|
120
|
+
this.evidenceLineage = data.evidenceLineage ?? [];
|
|
94
121
|
this.estimatedImpact = data.estimatedImpact || "low"; // low, medium, high
|
|
95
122
|
this.status = data.status || PlanStatus.PENDING;
|
|
96
|
-
this.result = null;
|
|
97
|
-
this.error = null;
|
|
123
|
+
this.result = data.result ?? null;
|
|
124
|
+
this.error = data.error ?? null;
|
|
98
125
|
this.turn = Number.isInteger(data.turn) && data.turn > 0 ? data.turn : null;
|
|
99
126
|
this.toolUseId = data.toolUseId || null;
|
|
100
127
|
this.startedAt = data.startedAt || null;
|
|
@@ -127,7 +154,7 @@ export class ExecutionPlan {
|
|
|
127
154
|
this.version =
|
|
128
155
|
Number.isInteger(data.version) && data.version > 0 ? data.version : 1;
|
|
129
156
|
this.revisionOf = data.revisionOf || null;
|
|
130
|
-
this.createdAt = new Date().toISOString();
|
|
157
|
+
this.createdAt = data.createdAt || new Date().toISOString();
|
|
131
158
|
}
|
|
132
159
|
|
|
133
160
|
addItem(item) {
|
|
@@ -258,13 +285,523 @@ export class ExecutionPlan {
|
|
|
258
285
|
}
|
|
259
286
|
}
|
|
260
287
|
|
|
288
|
+
const PLAN_ITEM_SNAPSHOT_FIELDS = Object.freeze([
|
|
289
|
+
"id",
|
|
290
|
+
"order",
|
|
291
|
+
"title",
|
|
292
|
+
"description",
|
|
293
|
+
"tool",
|
|
294
|
+
"params",
|
|
295
|
+
"dependencies",
|
|
296
|
+
"owner",
|
|
297
|
+
"checkpoint",
|
|
298
|
+
"approval",
|
|
299
|
+
"evidenceLineage",
|
|
300
|
+
"estimatedImpact",
|
|
301
|
+
"status",
|
|
302
|
+
"result",
|
|
303
|
+
"error",
|
|
304
|
+
"turn",
|
|
305
|
+
"toolUseId",
|
|
306
|
+
"startedAt",
|
|
307
|
+
"completedAt",
|
|
308
|
+
]);
|
|
309
|
+
const PLAN_SNAPSHOT_FIELDS = Object.freeze([
|
|
310
|
+
"id",
|
|
311
|
+
"title",
|
|
312
|
+
"description",
|
|
313
|
+
"goal",
|
|
314
|
+
"items",
|
|
315
|
+
"status",
|
|
316
|
+
"version",
|
|
317
|
+
"revisionOf",
|
|
318
|
+
"createdAt",
|
|
319
|
+
]);
|
|
320
|
+
const PLAN_MANAGER_SNAPSHOT_FIELDS = Object.freeze([
|
|
321
|
+
"state",
|
|
322
|
+
"currentPlan",
|
|
323
|
+
"history",
|
|
324
|
+
"blockedToolLog",
|
|
325
|
+
"executionLock",
|
|
326
|
+
]);
|
|
327
|
+
const LEGACY_PLAN_ITEM_SNAPSHOT_FIELDS = Object.freeze(
|
|
328
|
+
PLAN_ITEM_SNAPSHOT_FIELDS.filter(
|
|
329
|
+
(field) =>
|
|
330
|
+
!["owner", "checkpoint", "approval", "evidenceLineage"].includes(field),
|
|
331
|
+
),
|
|
332
|
+
);
|
|
333
|
+
const LEGACY_PLAN_MANAGER_SNAPSHOT_FIELDS = Object.freeze([
|
|
334
|
+
"state",
|
|
335
|
+
"currentPlan",
|
|
336
|
+
"history",
|
|
337
|
+
"blockedToolLog",
|
|
338
|
+
]);
|
|
339
|
+
const LEGACY_SESSION_SNAPSHOT_FIELDS = Object.freeze([
|
|
340
|
+
"schema",
|
|
341
|
+
"version",
|
|
342
|
+
"sessionId",
|
|
343
|
+
"revision",
|
|
344
|
+
"updatedAt",
|
|
345
|
+
"state",
|
|
346
|
+
]);
|
|
347
|
+
const EXECUTION_LOCK_FIELDS = Object.freeze([
|
|
348
|
+
"planId",
|
|
349
|
+
"permissionMode",
|
|
350
|
+
"approvedItemIds",
|
|
351
|
+
"allowedTools",
|
|
352
|
+
"createdAt",
|
|
353
|
+
]);
|
|
354
|
+
const AUTHORIZED_ITEM_STATUSES = new Set([
|
|
355
|
+
PlanStatus.APPROVED,
|
|
356
|
+
PlanStatus.EXECUTING,
|
|
357
|
+
PlanStatus.COMPLETED,
|
|
358
|
+
PlanStatus.FAILED,
|
|
359
|
+
]);
|
|
360
|
+
const AUTHORITY_STATES = new Set([
|
|
361
|
+
PlanState.APPROVED,
|
|
362
|
+
PlanState.EXECUTING,
|
|
363
|
+
PlanState.COMPLETED,
|
|
364
|
+
PlanState.FAILED,
|
|
365
|
+
]);
|
|
366
|
+
|
|
367
|
+
function isPlainObject(value) {
|
|
368
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
369
|
+
const prototype = Object.getPrototypeOf(value);
|
|
370
|
+
return prototype === Object.prototype || prototype === null;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function assertExactKeys(value, fields, label) {
|
|
374
|
+
if (!isPlainObject(value)) throw new Error(`${label} must be an object`);
|
|
375
|
+
const actual = Object.keys(value).sort();
|
|
376
|
+
const expected = [...fields].sort();
|
|
377
|
+
if (
|
|
378
|
+
actual.length !== expected.length ||
|
|
379
|
+
actual.some((key, index) => key !== expected[index])
|
|
380
|
+
) {
|
|
381
|
+
throw new Error(`${label} has missing or unknown fields`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function cloneJsonValue(value, label) {
|
|
386
|
+
try {
|
|
387
|
+
return JSON.parse(JSON.stringify(value));
|
|
388
|
+
} catch (error) {
|
|
389
|
+
throw new Error(`${label} must be JSON-serializable`, { cause: error });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function optionalString(value, label) {
|
|
394
|
+
if (value == null) return null;
|
|
395
|
+
if (typeof value !== "string") throw new Error(`${label} must be a string`);
|
|
396
|
+
return value;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function serializePlanItem(item) {
|
|
400
|
+
return {
|
|
401
|
+
id: item.id,
|
|
402
|
+
order: item.order,
|
|
403
|
+
title: item.title,
|
|
404
|
+
description: item.description,
|
|
405
|
+
tool: item.tool,
|
|
406
|
+
params: cloneJsonValue(item.params ?? {}, "plan item params"),
|
|
407
|
+
dependencies: [...(item.dependencies || [])],
|
|
408
|
+
owner: item.owner ?? null,
|
|
409
|
+
checkpoint: cloneJsonValue(item.checkpoint ?? null, "plan checkpoint"),
|
|
410
|
+
approval: cloneJsonValue(item.approval ?? null, "plan approval"),
|
|
411
|
+
evidenceLineage: cloneJsonValue(
|
|
412
|
+
item.evidenceLineage ?? [],
|
|
413
|
+
"plan evidence lineage",
|
|
414
|
+
),
|
|
415
|
+
estimatedImpact: item.estimatedImpact,
|
|
416
|
+
status: item.status,
|
|
417
|
+
result: cloneJsonValue(item.result ?? null, "plan item result"),
|
|
418
|
+
error: item.error ?? null,
|
|
419
|
+
turn: item.turn ?? null,
|
|
420
|
+
toolUseId: item.toolUseId ?? null,
|
|
421
|
+
startedAt: item.startedAt ?? null,
|
|
422
|
+
completedAt: item.completedAt ?? null,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function normalizePlanItem(value, index) {
|
|
427
|
+
assertExactKeys(value, PLAN_ITEM_SNAPSHOT_FIELDS, "plan item");
|
|
428
|
+
if (typeof value.id !== "string" || !value.id) {
|
|
429
|
+
throw new Error("plan item id must be a non-empty string");
|
|
430
|
+
}
|
|
431
|
+
if (!Number.isSafeInteger(value.order) || value.order !== index) {
|
|
432
|
+
throw new Error("plan item order is invalid");
|
|
433
|
+
}
|
|
434
|
+
if (
|
|
435
|
+
typeof value.title !== "string" ||
|
|
436
|
+
typeof value.description !== "string"
|
|
437
|
+
) {
|
|
438
|
+
throw new Error("plan item title and description must be strings");
|
|
439
|
+
}
|
|
440
|
+
if (value.tool != null && typeof value.tool !== "string") {
|
|
441
|
+
throw new Error("plan item tool must be a string or null");
|
|
442
|
+
}
|
|
443
|
+
if (!isPlainObject(value.params)) {
|
|
444
|
+
throw new Error("plan item params must be an object");
|
|
445
|
+
}
|
|
446
|
+
if (
|
|
447
|
+
!Array.isArray(value.dependencies) ||
|
|
448
|
+
value.dependencies.some((dependency) => typeof dependency !== "string") ||
|
|
449
|
+
new Set(value.dependencies).size !== value.dependencies.length
|
|
450
|
+
) {
|
|
451
|
+
throw new Error("plan item dependencies must be unique strings");
|
|
452
|
+
}
|
|
453
|
+
if (value.owner != null && typeof value.owner !== "string") {
|
|
454
|
+
throw new Error("plan item owner must be a string or null");
|
|
455
|
+
}
|
|
456
|
+
if (!Array.isArray(value.evidenceLineage)) {
|
|
457
|
+
throw new Error("plan item evidenceLineage must be an array");
|
|
458
|
+
}
|
|
459
|
+
if (!Object.hasOwn(IMPACT_MULTIPLIERS, value.estimatedImpact)) {
|
|
460
|
+
throw new Error("plan item estimatedImpact is invalid");
|
|
461
|
+
}
|
|
462
|
+
if (!Object.values(PlanStatus).includes(value.status)) {
|
|
463
|
+
throw new Error("plan item status is invalid");
|
|
464
|
+
}
|
|
465
|
+
if (value.error != null && typeof value.error !== "string") {
|
|
466
|
+
throw new Error("plan item error must be a string or null");
|
|
467
|
+
}
|
|
468
|
+
if (
|
|
469
|
+
value.turn != null &&
|
|
470
|
+
(!Number.isSafeInteger(value.turn) || value.turn < 1)
|
|
471
|
+
) {
|
|
472
|
+
throw new Error("plan item turn must be a positive integer or null");
|
|
473
|
+
}
|
|
474
|
+
for (const [field, fieldValue] of [
|
|
475
|
+
["toolUseId", value.toolUseId],
|
|
476
|
+
["startedAt", value.startedAt],
|
|
477
|
+
["completedAt", value.completedAt],
|
|
478
|
+
]) {
|
|
479
|
+
optionalString(fieldValue, `plan item ${field}`);
|
|
480
|
+
}
|
|
481
|
+
return serializePlanItem(value);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function migrateLegacyPlanItem(value, index) {
|
|
485
|
+
assertExactKeys(value, LEGACY_PLAN_ITEM_SNAPSHOT_FIELDS, "legacy plan item");
|
|
486
|
+
return normalizePlanItem(
|
|
487
|
+
{
|
|
488
|
+
...value,
|
|
489
|
+
owner: null,
|
|
490
|
+
checkpoint: null,
|
|
491
|
+
approval: null,
|
|
492
|
+
evidenceLineage: [],
|
|
493
|
+
},
|
|
494
|
+
index,
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function serializePlan(plan) {
|
|
499
|
+
if (!plan) return null;
|
|
500
|
+
return {
|
|
501
|
+
id: plan.id,
|
|
502
|
+
title: plan.title,
|
|
503
|
+
description: plan.description,
|
|
504
|
+
goal: plan.goal,
|
|
505
|
+
items: plan.items.map((item) => serializePlanItem(item)),
|
|
506
|
+
status: plan.status,
|
|
507
|
+
version: plan.version,
|
|
508
|
+
revisionOf: plan.revisionOf,
|
|
509
|
+
createdAt: plan.createdAt,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function normalizePlan(value) {
|
|
514
|
+
assertExactKeys(value, PLAN_SNAPSHOT_FIELDS, "execution plan");
|
|
515
|
+
if (
|
|
516
|
+
typeof value.id !== "string" ||
|
|
517
|
+
!value.id ||
|
|
518
|
+
typeof value.title !== "string" ||
|
|
519
|
+
typeof value.description !== "string" ||
|
|
520
|
+
typeof value.goal !== "string" ||
|
|
521
|
+
typeof value.createdAt !== "string" ||
|
|
522
|
+
!value.createdAt
|
|
523
|
+
) {
|
|
524
|
+
throw new Error("execution plan identity fields are invalid");
|
|
525
|
+
}
|
|
526
|
+
if (!Array.isArray(value.items)) {
|
|
527
|
+
throw new Error("execution plan items must be an array");
|
|
528
|
+
}
|
|
529
|
+
if (!Object.values(PlanState).includes(value.status)) {
|
|
530
|
+
throw new Error("execution plan status is invalid");
|
|
531
|
+
}
|
|
532
|
+
if (!Number.isSafeInteger(value.version) || value.version < 1) {
|
|
533
|
+
throw new Error("execution plan version is invalid");
|
|
534
|
+
}
|
|
535
|
+
optionalString(value.revisionOf, "execution plan revisionOf");
|
|
536
|
+
const items = value.items.map((item, index) =>
|
|
537
|
+
normalizePlanItem(item, index),
|
|
538
|
+
);
|
|
539
|
+
if (new Set(items.map((item) => item.id)).size !== items.length) {
|
|
540
|
+
throw new Error("execution plan item ids must be unique");
|
|
541
|
+
}
|
|
542
|
+
return {
|
|
543
|
+
id: value.id,
|
|
544
|
+
title: value.title,
|
|
545
|
+
description: value.description,
|
|
546
|
+
goal: value.goal,
|
|
547
|
+
items,
|
|
548
|
+
status: value.status,
|
|
549
|
+
version: value.version,
|
|
550
|
+
revisionOf: value.revisionOf,
|
|
551
|
+
createdAt: value.createdAt,
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function migrateLegacyPlan(value) {
|
|
556
|
+
assertExactKeys(value, PLAN_SNAPSHOT_FIELDS, "legacy execution plan");
|
|
557
|
+
if (!Array.isArray(value.items)) {
|
|
558
|
+
throw new Error("legacy execution plan items must be an array");
|
|
559
|
+
}
|
|
560
|
+
return normalizePlan({
|
|
561
|
+
...value,
|
|
562
|
+
items: value.items.map((item, index) => migrateLegacyPlanItem(item, index)),
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function normalizeExecutionLock(value, plan) {
|
|
567
|
+
assertExactKeys(value, EXECUTION_LOCK_FIELDS, "execution lock");
|
|
568
|
+
if (
|
|
569
|
+
value.planId !== plan.id ||
|
|
570
|
+
typeof value.permissionMode !== "string" ||
|
|
571
|
+
!value.permissionMode ||
|
|
572
|
+
typeof value.createdAt !== "string" ||
|
|
573
|
+
!value.createdAt ||
|
|
574
|
+
!Array.isArray(value.approvedItemIds) ||
|
|
575
|
+
!Array.isArray(value.allowedTools)
|
|
576
|
+
) {
|
|
577
|
+
throw new Error("execution lock identity fields are invalid");
|
|
578
|
+
}
|
|
579
|
+
for (const [label, entries] of [
|
|
580
|
+
["approvedItemIds", value.approvedItemIds],
|
|
581
|
+
["allowedTools", value.allowedTools],
|
|
582
|
+
]) {
|
|
583
|
+
if (
|
|
584
|
+
entries.some((entry) => typeof entry !== "string" || !entry) ||
|
|
585
|
+
new Set(entries).size !== entries.length
|
|
586
|
+
) {
|
|
587
|
+
throw new Error(`execution lock ${label} must contain unique strings`);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const itemMap = new Map(plan.items.map((item) => [item.id, item]));
|
|
592
|
+
const approvedIds = new Set(value.approvedItemIds);
|
|
593
|
+
for (const itemId of approvedIds) {
|
|
594
|
+
const item = itemMap.get(itemId);
|
|
595
|
+
if (!item || !AUTHORIZED_ITEM_STATUSES.has(item.status)) {
|
|
596
|
+
throw new Error("execution lock references an unauthorized plan item");
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
for (const item of plan.items) {
|
|
600
|
+
if (
|
|
601
|
+
AUTHORIZED_ITEM_STATUSES.has(item.status) !== approvedIds.has(item.id)
|
|
602
|
+
) {
|
|
603
|
+
throw new Error("execution lock does not exactly cover authorized items");
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const expectedTools = new Set(READ_TOOLS);
|
|
608
|
+
for (const itemId of approvedIds) {
|
|
609
|
+
const tool = itemMap.get(itemId)?.tool;
|
|
610
|
+
if (tool) expectedTools.add(tool);
|
|
611
|
+
}
|
|
612
|
+
if (
|
|
613
|
+
expectedTools.size !== value.allowedTools.length ||
|
|
614
|
+
value.allowedTools.some((tool) => !expectedTools.has(tool))
|
|
615
|
+
) {
|
|
616
|
+
throw new Error("execution lock allowedTools widens or changes approval");
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
return Object.freeze({
|
|
620
|
+
planId: value.planId,
|
|
621
|
+
permissionMode: value.permissionMode,
|
|
622
|
+
approvedItemIds: Object.freeze([...value.approvedItemIds]),
|
|
623
|
+
allowedTools: Object.freeze([...value.allowedTools]),
|
|
624
|
+
createdAt: value.createdAt,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function emptyPlanManagerState() {
|
|
629
|
+
return {
|
|
630
|
+
state: PlanState.INACTIVE,
|
|
631
|
+
currentPlan: null,
|
|
632
|
+
history: [],
|
|
633
|
+
blockedToolLog: [],
|
|
634
|
+
executionLock: null,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function serializeManagerState(manager) {
|
|
639
|
+
return {
|
|
640
|
+
state: manager.state,
|
|
641
|
+
currentPlan: serializePlan(manager.currentPlan),
|
|
642
|
+
history: manager.history.map((plan) => serializePlan(plan)),
|
|
643
|
+
blockedToolLog: manager.blockedToolLog.map((entry) => ({ ...entry })),
|
|
644
|
+
executionLock: manager.getExecutionLock(),
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function normalizeManagerState(value) {
|
|
649
|
+
assertExactKeys(value, PLAN_MANAGER_SNAPSHOT_FIELDS, "plan manager state");
|
|
650
|
+
if (!Object.values(PlanState).includes(value.state)) {
|
|
651
|
+
throw new Error("plan manager state is invalid");
|
|
652
|
+
}
|
|
653
|
+
if (!Array.isArray(value.history) || !Array.isArray(value.blockedToolLog)) {
|
|
654
|
+
throw new Error("plan manager history and blockedToolLog must be arrays");
|
|
655
|
+
}
|
|
656
|
+
const currentPlan =
|
|
657
|
+
value.currentPlan == null ? null : normalizePlan(value.currentPlan);
|
|
658
|
+
const history = value.history.map((plan) => normalizePlan(plan));
|
|
659
|
+
const blockedToolLog = value.blockedToolLog.map((entry) => {
|
|
660
|
+
assertExactKeys(
|
|
661
|
+
entry,
|
|
662
|
+
["tool", "reason", "timestamp"],
|
|
663
|
+
"blocked tool entry",
|
|
664
|
+
);
|
|
665
|
+
if (
|
|
666
|
+
typeof entry.tool !== "string" ||
|
|
667
|
+
typeof entry.reason !== "string" ||
|
|
668
|
+
typeof entry.timestamp !== "string"
|
|
669
|
+
) {
|
|
670
|
+
throw new Error("blocked tool entry is invalid");
|
|
671
|
+
}
|
|
672
|
+
return { ...entry };
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
if (value.state === PlanState.INACTIVE) {
|
|
676
|
+
if (currentPlan !== null || value.executionLock !== null) {
|
|
677
|
+
throw new Error(
|
|
678
|
+
"inactive plan state cannot retain a plan authority lock",
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
} else {
|
|
682
|
+
if (!currentPlan || currentPlan.status !== value.state) {
|
|
683
|
+
throw new Error("active plan state and current plan do not match");
|
|
684
|
+
}
|
|
685
|
+
if (value.state !== PlanState.ANALYZING) {
|
|
686
|
+
const ids = new Set(currentPlan.items.map((item) => item.id));
|
|
687
|
+
for (const item of currentPlan.items) {
|
|
688
|
+
if (item.dependencies.some((dependency) => !ids.has(dependency))) {
|
|
689
|
+
throw new Error("execution plan contains an unknown dependency");
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
let executionLock = null;
|
|
696
|
+
if (AUTHORITY_STATES.has(value.state)) {
|
|
697
|
+
if (!value.executionLock) {
|
|
698
|
+
throw new Error("authority-bearing plan state is missing executionLock");
|
|
699
|
+
}
|
|
700
|
+
executionLock = normalizeExecutionLock(value.executionLock, currentPlan);
|
|
701
|
+
} else if (value.executionLock !== null) {
|
|
702
|
+
throw new Error("non-executing plan state cannot carry executionLock");
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return {
|
|
706
|
+
state: value.state,
|
|
707
|
+
currentPlan,
|
|
708
|
+
history,
|
|
709
|
+
blockedToolLog,
|
|
710
|
+
executionLock,
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function migrateLegacySessionSnapshot(value, { sessionId }) {
|
|
715
|
+
assertExactKeys(
|
|
716
|
+
value,
|
|
717
|
+
LEGACY_SESSION_SNAPSHOT_FIELDS,
|
|
718
|
+
"legacy plan session snapshot",
|
|
719
|
+
);
|
|
720
|
+
if (
|
|
721
|
+
value.schema !== PLAN_SESSION_SNAPSHOT_SCHEMA ||
|
|
722
|
+
value.version !== PLAN_SESSION_LEGACY_SNAPSHOT_VERSION ||
|
|
723
|
+
value.sessionId !== sessionId ||
|
|
724
|
+
!Number.isSafeInteger(value.revision) ||
|
|
725
|
+
value.revision < 1 ||
|
|
726
|
+
!Number.isFinite(value.updatedAt)
|
|
727
|
+
) {
|
|
728
|
+
throw new Error("legacy plan session snapshot identity is invalid");
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
assertExactKeys(
|
|
732
|
+
value.state,
|
|
733
|
+
LEGACY_PLAN_MANAGER_SNAPSHOT_FIELDS,
|
|
734
|
+
"legacy plan manager state",
|
|
735
|
+
);
|
|
736
|
+
if (
|
|
737
|
+
![PlanState.INACTIVE, PlanState.ANALYZING, PlanState.PLAN_READY].includes(
|
|
738
|
+
value.state.state,
|
|
739
|
+
)
|
|
740
|
+
) {
|
|
741
|
+
throw new Error(
|
|
742
|
+
"legacy authority-bearing plan state cannot be migrated without an executionLock",
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
if (
|
|
746
|
+
!Array.isArray(value.state.history) ||
|
|
747
|
+
!Array.isArray(value.state.blockedToolLog)
|
|
748
|
+
) {
|
|
749
|
+
throw new Error("legacy plan manager collections are invalid");
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const state = normalizeManagerState({
|
|
753
|
+
state: value.state.state,
|
|
754
|
+
currentPlan:
|
|
755
|
+
value.state.currentPlan == null
|
|
756
|
+
? null
|
|
757
|
+
: migrateLegacyPlan(value.state.currentPlan),
|
|
758
|
+
history: value.state.history.map((plan) => migrateLegacyPlan(plan)),
|
|
759
|
+
blockedToolLog: value.state.blockedToolLog.map((entry) => ({ ...entry })),
|
|
760
|
+
executionLock: null,
|
|
761
|
+
});
|
|
762
|
+
return {
|
|
763
|
+
schema: PLAN_SESSION_SNAPSHOT_SCHEMA,
|
|
764
|
+
version: PLAN_SESSION_SNAPSHOT_VERSION,
|
|
765
|
+
sessionId,
|
|
766
|
+
revision: value.revision,
|
|
767
|
+
updatedAt: value.updatedAt,
|
|
768
|
+
event: createPlanSessionEvent({
|
|
769
|
+
sessionId,
|
|
770
|
+
revision: value.revision,
|
|
771
|
+
previousRevision: value.revision - 1,
|
|
772
|
+
type: "legacy-snapshot-migrated",
|
|
773
|
+
timestamp: value.updatedAt,
|
|
774
|
+
}),
|
|
775
|
+
state,
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
function persistenceFailure(error) {
|
|
780
|
+
const known =
|
|
781
|
+
error instanceof PlanPersistenceError ||
|
|
782
|
+
String(error?.code || "").startsWith("PLAN_");
|
|
783
|
+
return {
|
|
784
|
+
error: known ? error.message : "Plan persistence failed",
|
|
785
|
+
code: known ? error.code : PLAN_PERSISTENCE_ERROR_CODES.WRITE_FAILED,
|
|
786
|
+
...(Number.isSafeInteger(error?.expectedRevision)
|
|
787
|
+
? { expectedRevision: error.expectedRevision }
|
|
788
|
+
: {}),
|
|
789
|
+
...(Number.isSafeInteger(error?.actualRevision)
|
|
790
|
+
? { actualRevision: error.actualRevision }
|
|
791
|
+
: {}),
|
|
792
|
+
...(typeof error?.recoveryStrategy === "string"
|
|
793
|
+
? { recoveryStrategy: error.recoveryStrategy }
|
|
794
|
+
: {}),
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
|
|
261
798
|
/**
|
|
262
799
|
* Plan Mode Manager
|
|
263
800
|
*
|
|
264
801
|
* Controls the plan mode lifecycle in the agent REPL.
|
|
265
802
|
*/
|
|
266
803
|
export class PlanModeManager extends EventEmitter {
|
|
267
|
-
constructor() {
|
|
804
|
+
constructor(options = {}) {
|
|
268
805
|
super();
|
|
269
806
|
this.state = PlanState.INACTIVE;
|
|
270
807
|
this.currentPlan = null;
|
|
@@ -272,6 +809,160 @@ export class PlanModeManager extends EventEmitter {
|
|
|
272
809
|
this.blockedToolLog = [];
|
|
273
810
|
this.executionLock = null;
|
|
274
811
|
this._hookDb = null;
|
|
812
|
+
const sessionId =
|
|
813
|
+
typeof options.sessionId === "string" && options.sessionId
|
|
814
|
+
? options.sessionId
|
|
815
|
+
: null;
|
|
816
|
+
const memoryOnly =
|
|
817
|
+
!sessionId ||
|
|
818
|
+
options.memoryOnly === true ||
|
|
819
|
+
options.persistence === false;
|
|
820
|
+
Object.defineProperties(this, {
|
|
821
|
+
sessionId: {
|
|
822
|
+
value: sessionId,
|
|
823
|
+
enumerable: true,
|
|
824
|
+
writable: false,
|
|
825
|
+
configurable: false,
|
|
826
|
+
},
|
|
827
|
+
memoryOnly: {
|
|
828
|
+
value: memoryOnly,
|
|
829
|
+
enumerable: true,
|
|
830
|
+
writable: false,
|
|
831
|
+
configurable: false,
|
|
832
|
+
},
|
|
833
|
+
});
|
|
834
|
+
this.revision = 0;
|
|
835
|
+
this.updatedAt = 0;
|
|
836
|
+
this.lastEvent = null;
|
|
837
|
+
this._persistence = null;
|
|
838
|
+
|
|
839
|
+
if (!this.memoryOnly) {
|
|
840
|
+
const supplied = options.persistence;
|
|
841
|
+
this._persistence =
|
|
842
|
+
supplied && typeof supplied === "object"
|
|
843
|
+
? supplied
|
|
844
|
+
: new PlanSessionPersistence({
|
|
845
|
+
...(options.persistenceOptions || {}),
|
|
846
|
+
...(options.stateDir ? { stateDir: options.stateDir } : {}),
|
|
847
|
+
normalizeState: normalizeManagerState,
|
|
848
|
+
emptyState: emptyPlanManagerState,
|
|
849
|
+
migrateSnapshot: migrateLegacySessionSnapshot,
|
|
850
|
+
});
|
|
851
|
+
if (typeof this._persistence.configureStateSchema === "function") {
|
|
852
|
+
this._persistence.configureStateSchema({
|
|
853
|
+
normalizeState: normalizeManagerState,
|
|
854
|
+
emptyState: emptyPlanManagerState,
|
|
855
|
+
migrateSnapshot: migrateLegacySessionSnapshot,
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
const snapshot = this._persistence.load(this.sessionId);
|
|
859
|
+
this._applyManagerState(snapshot.state);
|
|
860
|
+
this.revision = snapshot.revision;
|
|
861
|
+
this.updatedAt = snapshot.updatedAt;
|
|
862
|
+
this.lastEvent = snapshot.event;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
_applyManagerState(value) {
|
|
867
|
+
const normalized = normalizeManagerState(value);
|
|
868
|
+
this.state = normalized.state;
|
|
869
|
+
this.currentPlan = normalized.currentPlan
|
|
870
|
+
? new ExecutionPlan(normalized.currentPlan)
|
|
871
|
+
: null;
|
|
872
|
+
this.history = normalized.history.map((plan) => new ExecutionPlan(plan));
|
|
873
|
+
this.blockedToolLog = normalized.blockedToolLog.map((entry) => ({
|
|
874
|
+
...entry,
|
|
875
|
+
}));
|
|
876
|
+
this.executionLock = normalized.executionLock;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
_beginMutation() {
|
|
880
|
+
if (!this._persistence) return { rollback: null };
|
|
881
|
+
try {
|
|
882
|
+
return {
|
|
883
|
+
rollback: {
|
|
884
|
+
state: serializeManagerState(this),
|
|
885
|
+
revision: this.revision,
|
|
886
|
+
updatedAt: this.updatedAt,
|
|
887
|
+
event: this.lastEvent,
|
|
888
|
+
},
|
|
889
|
+
};
|
|
890
|
+
} catch (error) {
|
|
891
|
+
return { failure: persistenceFailure(error) };
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
_commitMutation(transaction, eventType) {
|
|
896
|
+
if (!this._persistence) {
|
|
897
|
+
const previousRevision = this.revision;
|
|
898
|
+
this.revision += 1;
|
|
899
|
+
this.updatedAt = Date.now();
|
|
900
|
+
this.lastEvent = this.sessionId
|
|
901
|
+
? createPlanSessionEvent({
|
|
902
|
+
sessionId: this.sessionId,
|
|
903
|
+
revision: this.revision,
|
|
904
|
+
previousRevision,
|
|
905
|
+
type: eventType,
|
|
906
|
+
timestamp: this.updatedAt,
|
|
907
|
+
})
|
|
908
|
+
: null;
|
|
909
|
+
if (this.lastEvent) this.emit("session-event", { ...this.lastEvent });
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
try {
|
|
914
|
+
const snapshot = this._persistence.compareAndSwap(
|
|
915
|
+
this.sessionId,
|
|
916
|
+
this.revision,
|
|
917
|
+
serializeManagerState(this),
|
|
918
|
+
eventType,
|
|
919
|
+
);
|
|
920
|
+
this.revision = snapshot.revision;
|
|
921
|
+
this.updatedAt = snapshot.updatedAt;
|
|
922
|
+
this.lastEvent = snapshot.event;
|
|
923
|
+
this.emit("session-event", { ...this.lastEvent });
|
|
924
|
+
return null;
|
|
925
|
+
} catch (error) {
|
|
926
|
+
const rollback = transaction?.rollback;
|
|
927
|
+
if (rollback) {
|
|
928
|
+
this._applyManagerState(rollback.state);
|
|
929
|
+
this.revision = rollback.revision;
|
|
930
|
+
this.updatedAt = rollback.updatedAt;
|
|
931
|
+
this.lastEvent = rollback.event;
|
|
932
|
+
}
|
|
933
|
+
return persistenceFailure(error);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
getSessionSnapshot() {
|
|
938
|
+
return {
|
|
939
|
+
schema: PLAN_SESSION_SNAPSHOT_SCHEMA,
|
|
940
|
+
version: PLAN_SESSION_SNAPSHOT_VERSION,
|
|
941
|
+
sessionId: this.sessionId,
|
|
942
|
+
revision: this.revision,
|
|
943
|
+
updatedAt: this.updatedAt,
|
|
944
|
+
event: this.lastEvent ? { ...this.lastEvent } : null,
|
|
945
|
+
state: serializeManagerState(this),
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
reloadSession() {
|
|
950
|
+
if (!this._persistence) {
|
|
951
|
+
return { memoryOnly: true, snapshot: this.getSessionSnapshot() };
|
|
952
|
+
}
|
|
953
|
+
const snapshot = this._persistence.load(this.sessionId);
|
|
954
|
+
this._applyManagerState(snapshot.state);
|
|
955
|
+
this.revision = snapshot.revision;
|
|
956
|
+
this.updatedAt = snapshot.updatedAt;
|
|
957
|
+
this.lastEvent = snapshot.event;
|
|
958
|
+
return { memoryOnly: false, snapshot: this.getSessionSnapshot() };
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
recoverSessionPersistence(strategy) {
|
|
962
|
+
if (!this._persistence) {
|
|
963
|
+
return { strategy, recovered: 0, memoryOnly: true };
|
|
964
|
+
}
|
|
965
|
+
return this._persistence.recover(this.sessionId, strategy);
|
|
275
966
|
}
|
|
276
967
|
|
|
277
968
|
/**
|
|
@@ -295,6 +986,8 @@ export class PlanModeManager extends EventEmitter {
|
|
|
295
986
|
if (this.isActive()) {
|
|
296
987
|
return { error: "Already in plan mode" };
|
|
297
988
|
}
|
|
989
|
+
const transaction = this._beginMutation();
|
|
990
|
+
if (transaction.failure) return transaction.failure;
|
|
298
991
|
|
|
299
992
|
this.currentPlan = new ExecutionPlan({
|
|
300
993
|
title: options.title || "New Plan",
|
|
@@ -304,9 +997,15 @@ export class PlanModeManager extends EventEmitter {
|
|
|
304
997
|
this.blockedToolLog = [];
|
|
305
998
|
this.executionLock = null;
|
|
306
999
|
|
|
1000
|
+
const persistenceFailureResult = this._commitMutation(
|
|
1001
|
+
transaction,
|
|
1002
|
+
"plan-entered",
|
|
1003
|
+
);
|
|
1004
|
+
if (persistenceFailureResult) return persistenceFailureResult;
|
|
1005
|
+
|
|
307
1006
|
this.emit("enter", { plan: this.currentPlan, state: this.state });
|
|
308
1007
|
this._fireHook("PlanModeEnter", { planId: this.currentPlan.id });
|
|
309
|
-
return { plan: this.currentPlan };
|
|
1008
|
+
return { plan: this.currentPlan, revision: this.revision };
|
|
310
1009
|
}
|
|
311
1010
|
|
|
312
1011
|
/**
|
|
@@ -316,6 +1015,8 @@ export class PlanModeManager extends EventEmitter {
|
|
|
316
1015
|
if (!this.isActive()) {
|
|
317
1016
|
return { error: "Not in plan mode" };
|
|
318
1017
|
}
|
|
1018
|
+
const transaction = this._beginMutation();
|
|
1019
|
+
if (transaction.failure) return transaction.failure;
|
|
319
1020
|
|
|
320
1021
|
if (options.savePlan && this.currentPlan) {
|
|
321
1022
|
this.history.push(this.currentPlan);
|
|
@@ -327,8 +1028,14 @@ export class PlanModeManager extends EventEmitter {
|
|
|
327
1028
|
this.blockedToolLog = [];
|
|
328
1029
|
this.executionLock = null;
|
|
329
1030
|
|
|
1031
|
+
const persistenceFailureResult = this._commitMutation(
|
|
1032
|
+
transaction,
|
|
1033
|
+
options.eventType || "plan-exited",
|
|
1034
|
+
);
|
|
1035
|
+
if (persistenceFailureResult) return persistenceFailureResult;
|
|
1036
|
+
|
|
330
1037
|
this.emit("exit", { plan, reason: options.reason || "manual" });
|
|
331
|
-
return { plan };
|
|
1038
|
+
return { plan, revision: this.revision };
|
|
332
1039
|
}
|
|
333
1040
|
|
|
334
1041
|
/**
|
|
@@ -344,10 +1051,17 @@ export class PlanModeManager extends EventEmitter {
|
|
|
344
1051
|
) {
|
|
345
1052
|
return { error: "Approved plan is locked" };
|
|
346
1053
|
}
|
|
1054
|
+
const transaction = this._beginMutation();
|
|
1055
|
+
if (transaction.failure) return transaction.failure;
|
|
347
1056
|
|
|
348
1057
|
const item = this.currentPlan.addItem(itemData);
|
|
1058
|
+
const persistenceFailureResult = this._commitMutation(
|
|
1059
|
+
transaction,
|
|
1060
|
+
"plan-item-added",
|
|
1061
|
+
);
|
|
1062
|
+
if (persistenceFailureResult) return persistenceFailureResult;
|
|
349
1063
|
this.emit("item-added", { planId: this.currentPlan.id, item });
|
|
350
|
-
return { item };
|
|
1064
|
+
return { item, revision: this.revision };
|
|
351
1065
|
}
|
|
352
1066
|
|
|
353
1067
|
/**
|
|
@@ -357,11 +1071,18 @@ export class PlanModeManager extends EventEmitter {
|
|
|
357
1071
|
if (this.state !== PlanState.ANALYZING) {
|
|
358
1072
|
return { error: "Plan is not in analyzing state" };
|
|
359
1073
|
}
|
|
1074
|
+
const transaction = this._beginMutation();
|
|
1075
|
+
if (transaction.failure) return transaction.failure;
|
|
360
1076
|
|
|
361
1077
|
this.state = PlanState.PLAN_READY;
|
|
362
1078
|
this.currentPlan.status = PlanState.PLAN_READY;
|
|
1079
|
+
const persistenceFailureResult = this._commitMutation(
|
|
1080
|
+
transaction,
|
|
1081
|
+
"plan-ready",
|
|
1082
|
+
);
|
|
1083
|
+
if (persistenceFailureResult) return persistenceFailureResult;
|
|
363
1084
|
this.emit("plan-ready", { plan: this.currentPlan });
|
|
364
|
-
return { plan: this.currentPlan };
|
|
1085
|
+
return { plan: this.currentPlan, revision: this.revision };
|
|
365
1086
|
}
|
|
366
1087
|
|
|
367
1088
|
/**
|
|
@@ -379,6 +1100,8 @@ export class PlanModeManager extends EventEmitter {
|
|
|
379
1100
|
) {
|
|
380
1101
|
return { error: "Approved plan is locked" };
|
|
381
1102
|
}
|
|
1103
|
+
const transaction = this._beginMutation();
|
|
1104
|
+
if (transaction.failure) return transaction.failure;
|
|
382
1105
|
|
|
383
1106
|
const previousPlan = this.currentPlan;
|
|
384
1107
|
this.history.push(previousPlan);
|
|
@@ -397,6 +1120,11 @@ export class PlanModeManager extends EventEmitter {
|
|
|
397
1120
|
previousPlan,
|
|
398
1121
|
reason: String(options.reason || "revision"),
|
|
399
1122
|
};
|
|
1123
|
+
const persistenceFailureResult = this._commitMutation(
|
|
1124
|
+
transaction,
|
|
1125
|
+
"plan-revised",
|
|
1126
|
+
);
|
|
1127
|
+
if (persistenceFailureResult) return persistenceFailureResult;
|
|
400
1128
|
this.emit("plan-revised", payload);
|
|
401
1129
|
this._fireHook("PlanRevised", {
|
|
402
1130
|
planId: this.currentPlan.id,
|
|
@@ -404,7 +1132,7 @@ export class PlanModeManager extends EventEmitter {
|
|
|
404
1132
|
version: this.currentPlan.version,
|
|
405
1133
|
reason: payload.reason,
|
|
406
1134
|
});
|
|
407
|
-
return payload;
|
|
1135
|
+
return { ...payload, revision: this.revision };
|
|
408
1136
|
}
|
|
409
1137
|
|
|
410
1138
|
/**
|
|
@@ -417,6 +1145,8 @@ export class PlanModeManager extends EventEmitter {
|
|
|
417
1145
|
) {
|
|
418
1146
|
return { error: "Plan is not ready for approval" };
|
|
419
1147
|
}
|
|
1148
|
+
const transaction = this._beginMutation();
|
|
1149
|
+
if (transaction.failure) return transaction.failure;
|
|
420
1150
|
|
|
421
1151
|
const approvedItems = options.itemIds
|
|
422
1152
|
? this.currentPlan.items.filter((i) => options.itemIds.includes(i.id))
|
|
@@ -430,8 +1160,23 @@ export class PlanModeManager extends EventEmitter {
|
|
|
430
1160
|
approvedItems,
|
|
431
1161
|
options.permissionMode,
|
|
432
1162
|
);
|
|
1163
|
+
for (const item of approvedItems) {
|
|
1164
|
+
item.approval = {
|
|
1165
|
+
...(isPlainObject(item.approval)
|
|
1166
|
+
? cloneJsonValue(item.approval, "plan approval")
|
|
1167
|
+
: {}),
|
|
1168
|
+
decision: "approved",
|
|
1169
|
+
permissionMode: this.executionLock.permissionMode,
|
|
1170
|
+
approvedAt: this.executionLock.createdAt,
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
433
1173
|
this.state = PlanState.APPROVED;
|
|
434
1174
|
this.currentPlan.status = PlanState.APPROVED;
|
|
1175
|
+
const persistenceFailureResult = this._commitMutation(
|
|
1176
|
+
transaction,
|
|
1177
|
+
"plan-approved",
|
|
1178
|
+
);
|
|
1179
|
+
if (persistenceFailureResult) return persistenceFailureResult;
|
|
435
1180
|
this.emit("plan-approved", {
|
|
436
1181
|
plan: this.currentPlan,
|
|
437
1182
|
approvedCount: approvedItems.length,
|
|
@@ -447,6 +1192,7 @@ export class PlanModeManager extends EventEmitter {
|
|
|
447
1192
|
plan: this.currentPlan,
|
|
448
1193
|
approvedCount: approvedItems.length,
|
|
449
1194
|
executionLock: this.getExecutionLock(),
|
|
1195
|
+
revision: this.revision,
|
|
450
1196
|
};
|
|
451
1197
|
}
|
|
452
1198
|
|
|
@@ -496,6 +1242,8 @@ export class PlanModeManager extends EventEmitter {
|
|
|
496
1242
|
candidate.status === PlanStatus.APPROVED && candidate.tool === tool,
|
|
497
1243
|
);
|
|
498
1244
|
if (!item) return null;
|
|
1245
|
+
const transaction = this._beginMutation();
|
|
1246
|
+
if (transaction.failure) return transaction.failure;
|
|
499
1247
|
|
|
500
1248
|
item.status = PlanStatus.EXECUTING;
|
|
501
1249
|
item.toolUseId = context.toolUseId || null;
|
|
@@ -505,8 +1253,19 @@ export class PlanModeManager extends EventEmitter {
|
|
|
505
1253
|
item.completedAt = null;
|
|
506
1254
|
item.result = null;
|
|
507
1255
|
item.error = null;
|
|
1256
|
+
if (context.owner !== undefined) item.owner = context.owner;
|
|
1257
|
+
if (context.checkpoint !== undefined) item.checkpoint = context.checkpoint;
|
|
1258
|
+
if (context.approval !== undefined) item.approval = context.approval;
|
|
1259
|
+
if (context.evidenceLineage !== undefined) {
|
|
1260
|
+
item.evidenceLineage = context.evidenceLineage;
|
|
1261
|
+
}
|
|
508
1262
|
this.state = PlanState.EXECUTING;
|
|
509
1263
|
this.currentPlan.status = PlanState.EXECUTING;
|
|
1264
|
+
const persistenceFailureResult = this._commitMutation(
|
|
1265
|
+
transaction,
|
|
1266
|
+
"plan-item-executing",
|
|
1267
|
+
);
|
|
1268
|
+
if (persistenceFailureResult) return persistenceFailureResult;
|
|
510
1269
|
this.emit("item-executing", { planId: this.currentPlan.id, item });
|
|
511
1270
|
this._fireHook("PlanItemExecute", {
|
|
512
1271
|
planId: this.currentPlan.id,
|
|
@@ -523,12 +1282,20 @@ export class PlanModeManager extends EventEmitter {
|
|
|
523
1282
|
if (!this.currentPlan || !itemId) return null;
|
|
524
1283
|
const item = this.currentPlan.getItem(itemId);
|
|
525
1284
|
if (!item || item.status !== PlanStatus.EXECUTING) return null;
|
|
1285
|
+
const transaction = this._beginMutation();
|
|
1286
|
+
if (transaction.failure) return transaction.failure;
|
|
526
1287
|
|
|
527
1288
|
const success = options.success !== false;
|
|
528
1289
|
item.status = success ? PlanStatus.COMPLETED : PlanStatus.FAILED;
|
|
529
1290
|
item.completedAt = String(options.completedAt || new Date().toISOString());
|
|
530
1291
|
item.result = success ? (options.result ?? null) : null;
|
|
531
1292
|
item.error = success ? null : String(options.error || "tool failed");
|
|
1293
|
+
if (options.owner !== undefined) item.owner = options.owner;
|
|
1294
|
+
if (options.checkpoint !== undefined) item.checkpoint = options.checkpoint;
|
|
1295
|
+
if (options.approval !== undefined) item.approval = options.approval;
|
|
1296
|
+
if (options.evidenceLineage !== undefined) {
|
|
1297
|
+
item.evidenceLineage = options.evidenceLineage;
|
|
1298
|
+
}
|
|
532
1299
|
|
|
533
1300
|
const tracked = this.currentPlan.items.filter((candidate) =>
|
|
534
1301
|
[
|
|
@@ -553,6 +1320,11 @@ export class PlanModeManager extends EventEmitter {
|
|
|
553
1320
|
this.state = PlanState.EXECUTING;
|
|
554
1321
|
}
|
|
555
1322
|
this.currentPlan.status = this.state;
|
|
1323
|
+
const persistenceFailureResult = this._commitMutation(
|
|
1324
|
+
transaction,
|
|
1325
|
+
"plan-item-settled",
|
|
1326
|
+
);
|
|
1327
|
+
if (persistenceFailureResult) return persistenceFailureResult;
|
|
556
1328
|
this.emit("item-settled", {
|
|
557
1329
|
planId: this.currentPlan.id,
|
|
558
1330
|
item,
|
|
@@ -569,14 +1341,29 @@ export class PlanModeManager extends EventEmitter {
|
|
|
569
1341
|
if (!this.isActive()) {
|
|
570
1342
|
return { error: "No active plan" };
|
|
571
1343
|
}
|
|
1344
|
+
const transaction = this._beginMutation();
|
|
1345
|
+
if (transaction.failure) return transaction.failure;
|
|
572
1346
|
|
|
573
1347
|
for (const item of this.currentPlan.items) {
|
|
574
1348
|
item.status = PlanStatus.REJECTED;
|
|
575
1349
|
}
|
|
576
1350
|
|
|
1351
|
+
const plan = this.currentPlan;
|
|
577
1352
|
this.state = PlanState.REJECTED;
|
|
578
|
-
this.
|
|
579
|
-
|
|
1353
|
+
this.currentPlan.status = PlanState.REJECTED;
|
|
1354
|
+
this.history.push(this.currentPlan);
|
|
1355
|
+
this.state = PlanState.INACTIVE;
|
|
1356
|
+
this.currentPlan = null;
|
|
1357
|
+
this.blockedToolLog = [];
|
|
1358
|
+
this.executionLock = null;
|
|
1359
|
+
const persistenceFailureResult = this._commitMutation(
|
|
1360
|
+
transaction,
|
|
1361
|
+
"plan-rejected",
|
|
1362
|
+
);
|
|
1363
|
+
if (persistenceFailureResult) return persistenceFailureResult;
|
|
1364
|
+
this._fireHook("PlanRejected", { planId: plan.id, reason });
|
|
1365
|
+
this.emit("exit", { plan, reason: reason || "rejected" });
|
|
1366
|
+
return { plan, revision: this.revision };
|
|
580
1367
|
}
|
|
581
1368
|
|
|
582
1369
|
/**
|
|
@@ -608,11 +1395,24 @@ export class PlanModeManager extends EventEmitter {
|
|
|
608
1395
|
}
|
|
609
1396
|
|
|
610
1397
|
_recordBlockedTool(toolName, reason) {
|
|
1398
|
+
const transaction = this._beginMutation();
|
|
1399
|
+
if (transaction.failure) {
|
|
1400
|
+
this.emit("persistence-error", transaction.failure);
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
611
1403
|
this.blockedToolLog.push({
|
|
612
1404
|
tool: toolName,
|
|
613
1405
|
reason,
|
|
614
1406
|
timestamp: new Date().toISOString(),
|
|
615
1407
|
});
|
|
1408
|
+
const persistenceFailureResult = this._commitMutation(
|
|
1409
|
+
transaction,
|
|
1410
|
+
"plan-tool-blocked",
|
|
1411
|
+
);
|
|
1412
|
+
if (persistenceFailureResult) {
|
|
1413
|
+
this.emit("persistence-error", persistenceFailureResult);
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
616
1416
|
this.emit("tool-blocked", { toolName, reason });
|
|
617
1417
|
}
|
|
618
1418
|
|
|
@@ -681,9 +1481,21 @@ export class PlanModeManager extends EventEmitter {
|
|
|
681
1481
|
if (!this.currentPlan) return { error: "No active plan" };
|
|
682
1482
|
if (this.state !== PlanState.APPROVED)
|
|
683
1483
|
return { error: "Plan not approved" };
|
|
1484
|
+
const startTransaction = this._beginMutation();
|
|
1485
|
+
if (startTransaction.failure) return startTransaction.failure;
|
|
684
1486
|
|
|
685
1487
|
this.state = PlanState.EXECUTING;
|
|
686
1488
|
this.currentPlan.status = PlanState.EXECUTING;
|
|
1489
|
+
const startPersistenceFailure = this._commitMutation(
|
|
1490
|
+
startTransaction,
|
|
1491
|
+
"plan-execution-started",
|
|
1492
|
+
);
|
|
1493
|
+
if (startPersistenceFailure) return startPersistenceFailure;
|
|
1494
|
+
// Keep the last committed state as the rollback point while the executor
|
|
1495
|
+
// mutates item statuses/results in memory. A failed final CAS/write must
|
|
1496
|
+
// not leave memory ahead of the durable execution-start snapshot.
|
|
1497
|
+
const settleTransaction = this._beginMutation();
|
|
1498
|
+
if (settleTransaction.failure) return settleTransaction.failure;
|
|
687
1499
|
|
|
688
1500
|
const results = await this.currentPlan.executeInOrder(async (item) => {
|
|
689
1501
|
this._fireHook("PlanItemExecute", {
|
|
@@ -697,8 +1509,13 @@ export class PlanModeManager extends EventEmitter {
|
|
|
697
1509
|
const allDone = results.every((r) => r.success);
|
|
698
1510
|
this.state = allDone ? PlanState.COMPLETED : PlanState.FAILED;
|
|
699
1511
|
this.currentPlan.status = allDone ? PlanState.COMPLETED : PlanState.FAILED;
|
|
1512
|
+
const settlePersistenceFailure = this._commitMutation(
|
|
1513
|
+
settleTransaction,
|
|
1514
|
+
"plan-execution-settled",
|
|
1515
|
+
);
|
|
1516
|
+
if (settlePersistenceFailure) return settlePersistenceFailure;
|
|
700
1517
|
|
|
701
|
-
return { results, success: allDone };
|
|
1518
|
+
return { results, success: allDone, revision: this.revision };
|
|
702
1519
|
}
|
|
703
1520
|
|
|
704
1521
|
/**
|
|
@@ -725,9 +1542,9 @@ export class PlanModeManager extends EventEmitter {
|
|
|
725
1542
|
// Singleton
|
|
726
1543
|
let _instance = null;
|
|
727
1544
|
|
|
728
|
-
export function getPlanModeManager() {
|
|
1545
|
+
export function getPlanModeManager(options = {}) {
|
|
729
1546
|
if (!_instance) {
|
|
730
|
-
_instance = new PlanModeManager();
|
|
1547
|
+
_instance = new PlanModeManager(options);
|
|
731
1548
|
}
|
|
732
1549
|
return _instance;
|
|
733
1550
|
}
|