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
|
@@ -24,7 +24,6 @@ import {
|
|
|
24
24
|
createTrustedMcpServerMap,
|
|
25
25
|
resolveMcpServerPolicy,
|
|
26
26
|
normalizeRiskLevel,
|
|
27
|
-
normalizeBoolean,
|
|
28
27
|
selectHigherRiskLevel,
|
|
29
28
|
} from "../../runtime/coding-agent-managed-tool-policy.cjs";
|
|
30
29
|
import {
|
|
@@ -49,6 +48,19 @@ import {
|
|
|
49
48
|
registerHostHooksV2Workspace,
|
|
50
49
|
releaseRegisteredHostHooksV2Workspace,
|
|
51
50
|
} from "../../lib/hooks-v2-workspace-context.js";
|
|
51
|
+
import { mcpEffectDescriptorFields } from "../../lib/mcp-effect-contract.js";
|
|
52
|
+
import {
|
|
53
|
+
sanitizePersistedMessages,
|
|
54
|
+
sanitizePersistedNonSystemMessages,
|
|
55
|
+
} from "../../lib/session-message-provenance.js";
|
|
56
|
+
import {
|
|
57
|
+
appendWsSessionStateEvent,
|
|
58
|
+
createWsSessionState,
|
|
59
|
+
getWsSessionStateSnapshot,
|
|
60
|
+
hydrateWsSessionState,
|
|
61
|
+
recoverWsSessionState,
|
|
62
|
+
serializeWsSessionState,
|
|
63
|
+
} from "./ws-session-state.js";
|
|
52
64
|
|
|
53
65
|
function normalizeHostWorkspaceRoot(workspaceRoot) {
|
|
54
66
|
if (
|
|
@@ -65,6 +77,23 @@ function normalizeHostWorkspaceRoot(workspaceRoot) {
|
|
|
65
77
|
}
|
|
66
78
|
}
|
|
67
79
|
|
|
80
|
+
function normalizeCanonicalHostSystemPrefix(messages) {
|
|
81
|
+
const clean = sanitizePersistedMessages(messages, { strict: true });
|
|
82
|
+
if (clean.some((message) => message.role !== "system")) {
|
|
83
|
+
throw new TypeError("Canonical WS host prefix must contain only systems");
|
|
84
|
+
}
|
|
85
|
+
return Object.freeze(clean.map((message) => Object.freeze(message)));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function bindCanonicalHostSystemPrefix(session, messages) {
|
|
89
|
+
const prefix = normalizeCanonicalHostSystemPrefix(messages);
|
|
90
|
+
Object.defineProperty(session, "_canonicalHostSystemPrefix", {
|
|
91
|
+
configurable: true,
|
|
92
|
+
value: prefix,
|
|
93
|
+
});
|
|
94
|
+
return prefix;
|
|
95
|
+
}
|
|
96
|
+
|
|
68
97
|
function bindSessionHooksV2Workspace(
|
|
69
98
|
session,
|
|
70
99
|
workspaceRoot,
|
|
@@ -263,14 +292,15 @@ export class WSSessionManager {
|
|
|
263
292
|
type: "object",
|
|
264
293
|
properties: {},
|
|
265
294
|
};
|
|
295
|
+
const effectFields = mcpEffectDescriptorFields(mcpTool, {
|
|
296
|
+
sourceTrusted: serverPolicy.trusted === true,
|
|
297
|
+
provenance: `desktop-mcp-registry:${serverName}`,
|
|
298
|
+
});
|
|
266
299
|
const riskLevel = selectHigherRiskLevel(
|
|
267
300
|
serverPolicy.securityLevel,
|
|
268
301
|
normalizeRiskLevel(mcpTool?.risk_level, null),
|
|
302
|
+
effectFields.riskLevel,
|
|
269
303
|
);
|
|
270
|
-
const isReadOnly =
|
|
271
|
-
normalizeBoolean(mcpTool?.isReadOnly, false) ||
|
|
272
|
-
normalizeBoolean(mcpTool?.is_read_only, false) ||
|
|
273
|
-
riskLevel === "low";
|
|
274
304
|
|
|
275
305
|
let toolName = `mcp_${serverName}_${mcpTool?.name || "tool"}`;
|
|
276
306
|
if (seenNames.has(toolName)) {
|
|
@@ -288,7 +318,7 @@ export class WSSessionManager {
|
|
|
288
318
|
name: toolName,
|
|
289
319
|
description: mcpTool?.description || `MCP tool from ${serverName}.`,
|
|
290
320
|
inputSchema: parsedSchema,
|
|
291
|
-
|
|
321
|
+
...effectFields,
|
|
292
322
|
riskLevel,
|
|
293
323
|
source: `mcp:${serverName}`,
|
|
294
324
|
mcpMetadata: {
|
|
@@ -495,6 +525,8 @@ export class WSSessionManager {
|
|
|
495
525
|
createdAt: new Date().toISOString(),
|
|
496
526
|
lastActivity: new Date().toISOString(),
|
|
497
527
|
};
|
|
528
|
+
bindCanonicalHostSystemPrefix(session, messages);
|
|
529
|
+
this._bindSessionStateJournal(session, createWsSessionState());
|
|
498
530
|
bindSessionHooksV2Workspace(
|
|
499
531
|
session,
|
|
500
532
|
hostAuthorizedBaseRoot
|
|
@@ -561,6 +593,20 @@ export class WSSessionManager {
|
|
|
561
593
|
metadata,
|
|
562
594
|
);
|
|
563
595
|
const planManager = this._hydratePlanManager(metadata.planSnapshot);
|
|
596
|
+
const sessionStateJournal = hydrateWsSessionState(
|
|
597
|
+
metadata.sessionState,
|
|
598
|
+
Object.prototype.hasOwnProperty.call(metadata, "planSnapshot")
|
|
599
|
+
? {
|
|
600
|
+
// Legacy/current metadata migration is pass-through only.
|
|
601
|
+
// PlanModeManager remains the sole owner of Plan hydration and
|
|
602
|
+
// persistence.
|
|
603
|
+
planSnapshot: metadata.planSnapshot,
|
|
604
|
+
}
|
|
605
|
+
: {},
|
|
606
|
+
);
|
|
607
|
+
const stateRecovery = recoverWsSessionState(sessionStateJournal, {
|
|
608
|
+
reason: "process_restart",
|
|
609
|
+
});
|
|
564
610
|
const externalTools = this._buildSessionExternalTools();
|
|
565
611
|
let contextEngine = null;
|
|
566
612
|
let permanentMemory = null;
|
|
@@ -618,6 +664,13 @@ export class WSSessionManager {
|
|
|
618
664
|
createdAt: dbSession.created_at,
|
|
619
665
|
lastActivity: new Date().toISOString(),
|
|
620
666
|
};
|
|
667
|
+
if (Array.isArray(metadata.canonicalHostSystemPrefix)) {
|
|
668
|
+
bindCanonicalHostSystemPrefix(
|
|
669
|
+
session,
|
|
670
|
+
metadata.canonicalHostSystemPrefix,
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
this._bindSessionStateJournal(session, sessionStateJournal);
|
|
621
674
|
const recoveredHooksWorkspaceRoot =
|
|
622
675
|
metadata.worktreeIsolation !== true &&
|
|
623
676
|
normalizeHostWorkspaceRoot(baseProjectRoot) ===
|
|
@@ -630,6 +683,9 @@ export class WSSessionManager {
|
|
|
630
683
|
|
|
631
684
|
this._bindPlanManagerPersistence(session);
|
|
632
685
|
this.sessions.set(session.id, session);
|
|
686
|
+
if (stateRecovery.changed) {
|
|
687
|
+
this._persistSessionState(session.id);
|
|
688
|
+
}
|
|
633
689
|
return session;
|
|
634
690
|
} catch (_err) {
|
|
635
691
|
return null;
|
|
@@ -1413,21 +1469,7 @@ export class WSSessionManager {
|
|
|
1413
1469
|
* Persist current messages for a session.
|
|
1414
1470
|
*/
|
|
1415
1471
|
persistMessages(sessionId) {
|
|
1416
|
-
|
|
1417
|
-
if (!session || !this.db) return;
|
|
1418
|
-
|
|
1419
|
-
try {
|
|
1420
|
-
dbSaveMessages(
|
|
1421
|
-
this.db,
|
|
1422
|
-
sessionId,
|
|
1423
|
-
session.messages,
|
|
1424
|
-
this._serializeSessionMetadata(session),
|
|
1425
|
-
);
|
|
1426
|
-
} catch (_err) {
|
|
1427
|
-
// Non-critical
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
session.lastActivity = new Date().toISOString();
|
|
1472
|
+
this._persistSessionState(sessionId);
|
|
1431
1473
|
}
|
|
1432
1474
|
|
|
1433
1475
|
_prepareSessionWorkspace(projectRoot, sessionId, options = {}) {
|
|
@@ -1462,10 +1504,24 @@ export class WSSessionManager {
|
|
|
1462
1504
|
if (!session || !this.db) return;
|
|
1463
1505
|
|
|
1464
1506
|
try {
|
|
1507
|
+
let persistedMessages = session.messages;
|
|
1508
|
+
if (
|
|
1509
|
+
session.canonicalJsonlSession === true &&
|
|
1510
|
+
Array.isArray(session._canonicalHostSystemPrefix)
|
|
1511
|
+
) {
|
|
1512
|
+
const hostPrefix = normalizeCanonicalHostSystemPrefix(
|
|
1513
|
+
session._canonicalHostSystemPrefix,
|
|
1514
|
+
);
|
|
1515
|
+
const conversation = sanitizePersistedNonSystemMessages(
|
|
1516
|
+
session.messages,
|
|
1517
|
+
{ strict: true },
|
|
1518
|
+
);
|
|
1519
|
+
persistedMessages = [...hostPrefix, ...conversation];
|
|
1520
|
+
}
|
|
1465
1521
|
dbSaveMessages(
|
|
1466
1522
|
this.db,
|
|
1467
1523
|
sessionId,
|
|
1468
|
-
|
|
1524
|
+
persistedMessages,
|
|
1469
1525
|
this._serializeSessionMetadata(session),
|
|
1470
1526
|
);
|
|
1471
1527
|
} catch (_err) {
|
|
@@ -1476,6 +1532,7 @@ export class WSSessionManager {
|
|
|
1476
1532
|
}
|
|
1477
1533
|
|
|
1478
1534
|
_serializeSessionMetadata(session) {
|
|
1535
|
+
const sessionStateJournal = this._ensureSessionStateJournal(session);
|
|
1479
1536
|
return {
|
|
1480
1537
|
version: 1,
|
|
1481
1538
|
sessionType: session.type || "agent",
|
|
@@ -1489,6 +1546,11 @@ export class WSSessionManager {
|
|
|
1489
1546
|
baseUrl: session.baseUrl || null,
|
|
1490
1547
|
hostManagedToolPolicy: session.hostManagedToolPolicy || null,
|
|
1491
1548
|
enabledToolNames: session.enabledToolNames || [],
|
|
1549
|
+
canonicalHostSystemPrefix: Array.isArray(
|
|
1550
|
+
session._canonicalHostSystemPrefix,
|
|
1551
|
+
)
|
|
1552
|
+
? normalizeCanonicalHostSystemPrefix(session._canonicalHostSystemPrefix)
|
|
1553
|
+
: null,
|
|
1492
1554
|
worktreeIsolation: session.worktreeIsolation === true,
|
|
1493
1555
|
worktree: session.worktree || null,
|
|
1494
1556
|
planSnapshot: this._serializePlanManager(session.planManager),
|
|
@@ -1501,9 +1563,54 @@ export class WSSessionManager {
|
|
|
1501
1563
|
? session.patchHistory
|
|
1502
1564
|
: [],
|
|
1503
1565
|
taskGraph: this._serializeTaskGraph(session.taskGraph),
|
|
1566
|
+
// Additive metadata field: old readers ignore it; new readers replay the
|
|
1567
|
+
// contiguous event tail over the checkpoint before resuming a session.
|
|
1568
|
+
sessionState: serializeWsSessionState(sessionStateJournal),
|
|
1504
1569
|
};
|
|
1505
1570
|
}
|
|
1506
1571
|
|
|
1572
|
+
_bindSessionStateJournal(session, journal) {
|
|
1573
|
+
if (!session || typeof session !== "object") return;
|
|
1574
|
+
Object.defineProperty(session, "_sessionStateJournal", {
|
|
1575
|
+
value: journal || createWsSessionState(),
|
|
1576
|
+
enumerable: false,
|
|
1577
|
+
writable: true,
|
|
1578
|
+
configurable: true,
|
|
1579
|
+
});
|
|
1580
|
+
Object.defineProperty(session, "_recordSessionStateEvent", {
|
|
1581
|
+
value: (type, payload = {}) =>
|
|
1582
|
+
this.recordSessionStateEvent(session.id, type, payload),
|
|
1583
|
+
enumerable: false,
|
|
1584
|
+
writable: false,
|
|
1585
|
+
configurable: true,
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
_ensureSessionStateJournal(session) {
|
|
1590
|
+
if (!session?._sessionStateJournal) {
|
|
1591
|
+
this._bindSessionStateJournal(session, createWsSessionState());
|
|
1592
|
+
}
|
|
1593
|
+
return session._sessionStateJournal;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
/** Record a recovery event and atomically persist it with session messages. */
|
|
1597
|
+
recordSessionStateEvent(sessionId, type, payload = {}) {
|
|
1598
|
+
const session = this.sessions.get(sessionId);
|
|
1599
|
+
if (!session) return null;
|
|
1600
|
+
const journal = this._ensureSessionStateJournal(session);
|
|
1601
|
+
appendWsSessionStateEvent(journal, type, payload);
|
|
1602
|
+
this._persistSessionState(sessionId);
|
|
1603
|
+
return getWsSessionStateSnapshot(journal);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
/** Detached snapshot suitable for an additive IDE resume payload. */
|
|
1607
|
+
getSessionStateSnapshot(sessionId) {
|
|
1608
|
+
const session = this.sessions.get(sessionId);
|
|
1609
|
+
if (!session) return null;
|
|
1610
|
+
const journal = this._ensureSessionStateJournal(session);
|
|
1611
|
+
return getWsSessionStateSnapshot(journal);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1507
1614
|
_hydratePendingPatches(list) {
|
|
1508
1615
|
const map = new Map();
|
|
1509
1616
|
if (Array.isArray(list)) {
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crash-safe, backward-compatible state journal for IDE/WebSocket sessions.
|
|
3
|
+
*
|
|
4
|
+
* The journal is stored inside the existing session metadata row, so messages
|
|
5
|
+
* and recovery state are committed by the same database UPDATE. A compact
|
|
6
|
+
* checkpoint plus a bounded, revision-ordered event tail makes hydration
|
|
7
|
+
* deterministic without changing any existing WebSocket frame.
|
|
8
|
+
*
|
|
9
|
+
* Plan data is deliberately opaque here. This module only preserves an
|
|
10
|
+
* optional `planSnapshot` field for callers that own Plan persistence.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export const WS_SESSION_STATE_SCHEMA = "chainlesschain.ws-session-state";
|
|
14
|
+
export const WS_SESSION_STATE_VERSION = 1;
|
|
15
|
+
export const DEFAULT_WS_SESSION_STATE_MAX_EVENTS = 64;
|
|
16
|
+
|
|
17
|
+
const RUN_STATUSES = new Set(["idle", "running", "interrupted"]);
|
|
18
|
+
const APPROVAL_STATUSES = new Set(["pending", "interrupted"]);
|
|
19
|
+
|
|
20
|
+
function own(object, key) {
|
|
21
|
+
return Object.prototype.hasOwnProperty.call(object || {}, key);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function cloneJson(value, fallback = null) {
|
|
25
|
+
if (value === undefined) return fallback;
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(JSON.stringify(value));
|
|
28
|
+
} catch {
|
|
29
|
+
return fallback;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function nonNegativeRevision(value, fallback = 0) {
|
|
34
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : fallback;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function timestamp(value, fallback = null) {
|
|
38
|
+
return typeof value === "string" && value.length > 0 ? value : fallback;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeTodoSnapshot(value) {
|
|
42
|
+
if (!value || typeof value !== "object") return null;
|
|
43
|
+
const revision = nonNegativeRevision(value.revision, -1);
|
|
44
|
+
if (revision < 0) return null;
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
...(typeof value.schema === "string" ? { schema: value.schema } : {}),
|
|
48
|
+
...(Number.isSafeInteger(value.version) && value.version >= 0
|
|
49
|
+
? { version: value.version }
|
|
50
|
+
: {}),
|
|
51
|
+
...(typeof value.sessionId === "string" || value.sessionId === null
|
|
52
|
+
? { sessionId: value.sessionId }
|
|
53
|
+
: {}),
|
|
54
|
+
revision,
|
|
55
|
+
todos: Array.isArray(value.todos) ? cloneJson(value.todos, []) : [],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function normalizePendingApproval(value) {
|
|
60
|
+
if (!value || typeof value !== "object") return null;
|
|
61
|
+
const requestId =
|
|
62
|
+
typeof value.requestId === "string" && value.requestId.length > 0
|
|
63
|
+
? value.requestId
|
|
64
|
+
: null;
|
|
65
|
+
if (!requestId) return null;
|
|
66
|
+
|
|
67
|
+
const status = APPROVAL_STATUSES.has(value.status) ? value.status : "pending";
|
|
68
|
+
return {
|
|
69
|
+
requestId,
|
|
70
|
+
status,
|
|
71
|
+
binding:
|
|
72
|
+
typeof value.binding === "string" && value.binding.length > 0
|
|
73
|
+
? value.binding
|
|
74
|
+
: null,
|
|
75
|
+
tool:
|
|
76
|
+
typeof value.tool === "string" && value.tool.length > 0
|
|
77
|
+
? value.tool
|
|
78
|
+
: null,
|
|
79
|
+
risk:
|
|
80
|
+
typeof value.risk === "string" && value.risk.length > 0
|
|
81
|
+
? value.risk
|
|
82
|
+
: null,
|
|
83
|
+
rule:
|
|
84
|
+
typeof value.rule === "string" && value.rule.length > 0
|
|
85
|
+
? value.rule
|
|
86
|
+
: null,
|
|
87
|
+
requestedAt: timestamp(value.requestedAt),
|
|
88
|
+
interruptedAt: timestamp(value.interruptedAt),
|
|
89
|
+
reason:
|
|
90
|
+
typeof value.reason === "string" && value.reason.length > 0
|
|
91
|
+
? value.reason
|
|
92
|
+
: null,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function normalizeRun(value) {
|
|
97
|
+
const source = value && typeof value === "object" ? value : {};
|
|
98
|
+
const status = RUN_STATUSES.has(source.status) ? source.status : "idle";
|
|
99
|
+
return {
|
|
100
|
+
status,
|
|
101
|
+
requestId:
|
|
102
|
+
typeof source.requestId === "string" && source.requestId.length > 0
|
|
103
|
+
? source.requestId
|
|
104
|
+
: null,
|
|
105
|
+
startedAt: timestamp(source.startedAt),
|
|
106
|
+
settledAt: timestamp(source.settledAt),
|
|
107
|
+
interruptedAt: timestamp(source.interruptedAt),
|
|
108
|
+
reason:
|
|
109
|
+
typeof source.reason === "string" && source.reason.length > 0
|
|
110
|
+
? source.reason
|
|
111
|
+
: null,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function normalizeSnapshot(value = {}, options = {}) {
|
|
116
|
+
const source = value && typeof value === "object" ? value : {};
|
|
117
|
+
const snapshot = {
|
|
118
|
+
revision: nonNegativeRevision(source.revision),
|
|
119
|
+
todo: normalizeTodoSnapshot(source.todo),
|
|
120
|
+
pendingApproval: normalizePendingApproval(source.pendingApproval),
|
|
121
|
+
run: normalizeRun(source.run),
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (own(source, "planSnapshot")) {
|
|
125
|
+
snapshot.planSnapshot = cloneJson(source.planSnapshot);
|
|
126
|
+
} else if (own(options, "planSnapshot")) {
|
|
127
|
+
// Migration seam for metadata written before the journal existed. The
|
|
128
|
+
// value remains opaque; Plan persistence/hydration belongs elsewhere.
|
|
129
|
+
snapshot.planSnapshot = cloneJson(options.planSnapshot);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return snapshot;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function matchingRequest(currentRequestId, eventRequestId) {
|
|
136
|
+
return (
|
|
137
|
+
!eventRequestId || !currentRequestId || currentRequestId === eventRequestId
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function applyEvent(snapshot, event) {
|
|
142
|
+
const next = normalizeSnapshot(snapshot);
|
|
143
|
+
const payload =
|
|
144
|
+
event.payload && typeof event.payload === "object" ? event.payload : {};
|
|
145
|
+
|
|
146
|
+
switch (event.type) {
|
|
147
|
+
case "todo.snapshot": {
|
|
148
|
+
const todo = normalizeTodoSnapshot(payload.todo || payload);
|
|
149
|
+
if (!next.todo || (todo && todo.revision >= next.todo.revision)) {
|
|
150
|
+
next.todo = todo;
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
case "approval.requested":
|
|
156
|
+
next.pendingApproval = normalizePendingApproval({
|
|
157
|
+
...payload,
|
|
158
|
+
status: "pending",
|
|
159
|
+
requestedAt: payload.requestedAt || event.at,
|
|
160
|
+
});
|
|
161
|
+
break;
|
|
162
|
+
|
|
163
|
+
case "approval.settled":
|
|
164
|
+
if (
|
|
165
|
+
next.pendingApproval &&
|
|
166
|
+
matchingRequest(next.pendingApproval.requestId, payload.requestId)
|
|
167
|
+
) {
|
|
168
|
+
next.pendingApproval = null;
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
|
|
172
|
+
case "approval.interrupted":
|
|
173
|
+
if (
|
|
174
|
+
next.pendingApproval &&
|
|
175
|
+
matchingRequest(next.pendingApproval.requestId, payload.requestId)
|
|
176
|
+
) {
|
|
177
|
+
next.pendingApproval = normalizePendingApproval({
|
|
178
|
+
...next.pendingApproval,
|
|
179
|
+
status: "interrupted",
|
|
180
|
+
interruptedAt: payload.interruptedAt || event.at,
|
|
181
|
+
reason: payload.reason || "session_recovered",
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
|
|
186
|
+
case "run.started":
|
|
187
|
+
next.run = normalizeRun({
|
|
188
|
+
status: "running",
|
|
189
|
+
requestId: payload.requestId || null,
|
|
190
|
+
startedAt: payload.startedAt || event.at,
|
|
191
|
+
});
|
|
192
|
+
break;
|
|
193
|
+
|
|
194
|
+
case "run.settled":
|
|
195
|
+
// A late completion from an explicitly interrupted turn must not erase
|
|
196
|
+
// the interruption marker. The next run.started event supersedes it.
|
|
197
|
+
if (
|
|
198
|
+
next.run.status === "running" &&
|
|
199
|
+
matchingRequest(next.run.requestId, payload.requestId)
|
|
200
|
+
) {
|
|
201
|
+
next.run = normalizeRun({
|
|
202
|
+
status: "idle",
|
|
203
|
+
settledAt: payload.settledAt || event.at,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
|
|
208
|
+
case "run.interrupted":
|
|
209
|
+
if (matchingRequest(next.run.requestId, payload.requestId)) {
|
|
210
|
+
next.run = normalizeRun({
|
|
211
|
+
...next.run,
|
|
212
|
+
status: "interrupted",
|
|
213
|
+
requestId: payload.requestId || next.run.requestId,
|
|
214
|
+
interruptedAt: payload.interruptedAt || event.at,
|
|
215
|
+
reason: payload.reason || "interrupted",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
break;
|
|
219
|
+
|
|
220
|
+
case "plan.snapshot":
|
|
221
|
+
if (own(payload, "planSnapshot")) {
|
|
222
|
+
next.planSnapshot = cloneJson(payload.planSnapshot);
|
|
223
|
+
}
|
|
224
|
+
break;
|
|
225
|
+
|
|
226
|
+
default:
|
|
227
|
+
// Unknown additive events consume their revision but cannot widen state.
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
next.revision = event.revision;
|
|
232
|
+
return next;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function normalizeEvent(value, expectedRevision) {
|
|
236
|
+
if (!value || typeof value !== "object") return null;
|
|
237
|
+
if (value.revision !== expectedRevision) return null;
|
|
238
|
+
if (typeof value.type !== "string" || value.type.length === 0) return null;
|
|
239
|
+
return {
|
|
240
|
+
revision: expectedRevision,
|
|
241
|
+
type: value.type,
|
|
242
|
+
at: timestamp(value.at),
|
|
243
|
+
payload: cloneJson(value.payload, {}),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Hydrate a journal and replay only its contiguous event prefix. */
|
|
248
|
+
export function hydrateWsSessionState(value, options = {}) {
|
|
249
|
+
const candidate = value && typeof value === "object" ? value : {};
|
|
250
|
+
const schemaSupported =
|
|
251
|
+
(!own(candidate, "schema") ||
|
|
252
|
+
candidate.schema === WS_SESSION_STATE_SCHEMA) &&
|
|
253
|
+
(!own(candidate, "version") ||
|
|
254
|
+
candidate.version === WS_SESSION_STATE_VERSION);
|
|
255
|
+
const source = schemaSupported ? candidate : {};
|
|
256
|
+
const maxEvents =
|
|
257
|
+
Number.isSafeInteger(options.maxEvents) && options.maxEvents > 0
|
|
258
|
+
? options.maxEvents
|
|
259
|
+
: DEFAULT_WS_SESSION_STATE_MAX_EVENTS;
|
|
260
|
+
const checkpointSource =
|
|
261
|
+
source.snapshot && typeof source.snapshot === "object"
|
|
262
|
+
? source.snapshot
|
|
263
|
+
: source.checkpoint && typeof source.checkpoint === "object"
|
|
264
|
+
? source.checkpoint
|
|
265
|
+
: {};
|
|
266
|
+
const checkpoint = normalizeSnapshot(checkpointSource, options);
|
|
267
|
+
let current = normalizeSnapshot(checkpoint);
|
|
268
|
+
const events = [];
|
|
269
|
+
const persistedEvents = Array.isArray(source.events)
|
|
270
|
+
? source.events.slice(0, maxEvents)
|
|
271
|
+
: [];
|
|
272
|
+
|
|
273
|
+
for (const rawEvent of persistedEvents) {
|
|
274
|
+
const event = normalizeEvent(rawEvent, current.revision + 1);
|
|
275
|
+
// A revision gap/corrupt event terminates replay. Applying a later event
|
|
276
|
+
// could skip an interrupt/deny transition and accidentally widen state.
|
|
277
|
+
if (!event) break;
|
|
278
|
+
current = applyEvent(current, event);
|
|
279
|
+
events.push(event);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const journal = {
|
|
283
|
+
schema: WS_SESSION_STATE_SCHEMA,
|
|
284
|
+
version: WS_SESSION_STATE_VERSION,
|
|
285
|
+
snapshot: checkpoint,
|
|
286
|
+
events,
|
|
287
|
+
current,
|
|
288
|
+
maxEvents,
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
compactJournal(journal);
|
|
292
|
+
return journal;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function createWsSessionState(options = {}) {
|
|
296
|
+
const hasPlanSnapshot = own(options, "planSnapshot");
|
|
297
|
+
return hydrateWsSessionState(
|
|
298
|
+
{
|
|
299
|
+
schema: WS_SESSION_STATE_SCHEMA,
|
|
300
|
+
version: WS_SESSION_STATE_VERSION,
|
|
301
|
+
snapshot: {
|
|
302
|
+
revision: 0,
|
|
303
|
+
todo: normalizeTodoSnapshot(options.todo),
|
|
304
|
+
pendingApproval: null,
|
|
305
|
+
run: normalizeRun(),
|
|
306
|
+
...(hasPlanSnapshot
|
|
307
|
+
? { planSnapshot: cloneJson(options.planSnapshot) }
|
|
308
|
+
: {}),
|
|
309
|
+
},
|
|
310
|
+
events: [],
|
|
311
|
+
},
|
|
312
|
+
options,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function compactJournal(journal) {
|
|
317
|
+
while (journal.events.length > journal.maxEvents) {
|
|
318
|
+
const event = journal.events.shift();
|
|
319
|
+
journal.snapshot = applyEvent(journal.snapshot, event);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** Append and apply one monotonic state event. Mutates the in-memory journal. */
|
|
324
|
+
export function appendWsSessionStateEvent(
|
|
325
|
+
journal,
|
|
326
|
+
type,
|
|
327
|
+
payload = {},
|
|
328
|
+
options = {},
|
|
329
|
+
) {
|
|
330
|
+
if (!journal || typeof journal !== "object") return null;
|
|
331
|
+
const event = {
|
|
332
|
+
revision: journal.current.revision + 1,
|
|
333
|
+
type: String(type || "unknown"),
|
|
334
|
+
at: timestamp(options.at, new Date().toISOString()),
|
|
335
|
+
payload: cloneJson(payload, {}),
|
|
336
|
+
};
|
|
337
|
+
journal.current = applyEvent(journal.current, event);
|
|
338
|
+
journal.events.push(event);
|
|
339
|
+
compactJournal(journal);
|
|
340
|
+
return cloneJson(event);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** Update the journal from the separately persisted TODO snapshot. */
|
|
344
|
+
export function syncWsSessionTodo(journal, todoSnapshot, options = {}) {
|
|
345
|
+
const todo = normalizeTodoSnapshot(todoSnapshot);
|
|
346
|
+
if (!journal || !todo) return false;
|
|
347
|
+
const current = journal.current?.todo || null;
|
|
348
|
+
if (current && current.revision >= todo.revision) return false;
|
|
349
|
+
appendWsSessionStateEvent(journal, "todo.snapshot", { todo }, options);
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Convert state that depended on the dead process into an explicit,
|
|
355
|
+
* fail-closed interruption marker. Pending approvals are retained for UI
|
|
356
|
+
* explanation but can no longer be resolved by a stale session-answer.
|
|
357
|
+
*/
|
|
358
|
+
export function recoverWsSessionState(journal, options = {}) {
|
|
359
|
+
if (!journal || typeof journal !== "object") {
|
|
360
|
+
return {
|
|
361
|
+
changed: false,
|
|
362
|
+
runInterrupted: false,
|
|
363
|
+
approvalInterrupted: false,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
const at = timestamp(options.at, new Date().toISOString());
|
|
367
|
+
const reason = options.reason || "process_restart";
|
|
368
|
+
let runInterrupted = false;
|
|
369
|
+
let approvalInterrupted = false;
|
|
370
|
+
|
|
371
|
+
if (journal.current.run?.status === "running") {
|
|
372
|
+
appendWsSessionStateEvent(
|
|
373
|
+
journal,
|
|
374
|
+
"run.interrupted",
|
|
375
|
+
{
|
|
376
|
+
requestId: journal.current.run.requestId,
|
|
377
|
+
interruptedAt: at,
|
|
378
|
+
reason,
|
|
379
|
+
},
|
|
380
|
+
{ at },
|
|
381
|
+
);
|
|
382
|
+
runInterrupted = true;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (journal.current.pendingApproval?.status === "pending") {
|
|
386
|
+
appendWsSessionStateEvent(
|
|
387
|
+
journal,
|
|
388
|
+
"approval.interrupted",
|
|
389
|
+
{
|
|
390
|
+
requestId: journal.current.pendingApproval.requestId,
|
|
391
|
+
interruptedAt: at,
|
|
392
|
+
reason,
|
|
393
|
+
},
|
|
394
|
+
{ at },
|
|
395
|
+
);
|
|
396
|
+
approvalInterrupted = true;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
changed: runInterrupted || approvalInterrupted,
|
|
401
|
+
runInterrupted,
|
|
402
|
+
approvalInterrupted,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/** Current replayed state for IDE consumers (detached JSON-safe copy). */
|
|
407
|
+
export function getWsSessionStateSnapshot(journal) {
|
|
408
|
+
if (!journal || typeof journal !== "object") {
|
|
409
|
+
return {
|
|
410
|
+
schema: WS_SESSION_STATE_SCHEMA,
|
|
411
|
+
version: WS_SESSION_STATE_VERSION,
|
|
412
|
+
...normalizeSnapshot(),
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
schema: WS_SESSION_STATE_SCHEMA,
|
|
417
|
+
version: WS_SESSION_STATE_VERSION,
|
|
418
|
+
...cloneJson(journal.current, normalizeSnapshot()),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** Persistable journal shape; internal `current` and limits stay in memory. */
|
|
423
|
+
export function serializeWsSessionState(journal) {
|
|
424
|
+
const safe = journal || createWsSessionState();
|
|
425
|
+
return {
|
|
426
|
+
schema: WS_SESSION_STATE_SCHEMA,
|
|
427
|
+
version: WS_SESSION_STATE_VERSION,
|
|
428
|
+
snapshot: cloneJson(safe.snapshot, normalizeSnapshot()),
|
|
429
|
+
events: cloneJson(safe.events, []),
|
|
430
|
+
};
|
|
431
|
+
}
|