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.
Files changed (337) hide show
  1. package/README.md +61 -11
  2. package/bin/chainlesschain.js +7 -52
  3. package/completions/_cc +209 -0
  4. package/completions/cc.bash +18 -0
  5. package/completions/cc.fish +13 -0
  6. package/completions/cc.ps1 +226 -0
  7. package/package.json +10 -2
  8. package/src/assets/web-panel/assets/{AIOps-Brr1LVcE.js → AIOps-DBLkaTje.js} +1 -1
  9. package/src/assets/web-panel/assets/{ActionButton-DQU74DKG.js → ActionButton-BAy-Qebo.js} +1 -1
  10. package/src/assets/web-panel/assets/{Analytics-CYp9178y.js → Analytics-Dfb1Mlau.js} +3 -3
  11. package/src/assets/web-panel/assets/{AppLayout-DW4SCYsW.js → AppLayout-BbFkN8m0.js} +5 -5
  12. package/src/assets/web-panel/assets/{Artifacts-CYlD_ed1.js → Artifacts-kM4-cEEH.js} +1 -1
  13. package/src/assets/web-panel/assets/{Audit-CWl1rZOi.js → Audit-BSQfYWf3.js} +1 -1
  14. package/src/assets/web-panel/assets/{BackgroundAgents-BOR-LPsa.js → BackgroundAgents-CWDbI_fG.js} +1 -1
  15. package/src/assets/web-panel/assets/{Backup-DLpG7j1X.js → Backup-BxLPbq_Q.js} +1 -1
  16. package/src/assets/web-panel/assets/{BaseInput-B35K9wdr.js → BaseInput-DuIbdrp6.js} +1 -1
  17. package/src/assets/web-panel/assets/{Chat-CwUJb9gB.js → Chat-NEmjGahU.js} +5 -5
  18. package/src/assets/web-panel/assets/{ChatBubbleRenderer-1v6aMS1m.js → ChatBubbleRenderer-DfJEo-5_.js} +1 -1
  19. package/src/assets/web-panel/assets/{Checkbox-pi3-tPNW.js → Checkbox-CaoeG3Ns.js} +1 -1
  20. package/src/assets/web-panel/assets/{Codegen-FyNF35eL.js → Codegen-BFf_g3xY.js} +1 -1
  21. package/src/assets/web-panel/assets/{Col-tGt_JDEr.js → Col-CNOd8mdQ.js} +1 -1
  22. package/src/assets/web-panel/assets/{Community-CC7LonQX.js → Community-BhpDqz-i.js} +1 -1
  23. package/src/assets/web-panel/assets/{Compact-BGXrHHjE.js → Compact-CQ42T7Z6.js} +1 -1
  24. package/src/assets/web-panel/assets/{Compliance-DSW0TBZ8.js → Compliance-DI5PcLMN.js} +1 -1
  25. package/src/assets/web-panel/assets/{Cowork-Bhk7rbvE.js → Cowork-wYYZe0wR.js} +3 -3
  26. package/src/assets/web-panel/assets/{Cron-DxyiQ2NZ.js → Cron-Dlq03DD_.js} +2 -2
  27. package/src/assets/web-panel/assets/{Crosschain-B2ZkxHok.js → Crosschain-BzqS8PP3.js} +1 -1
  28. package/src/assets/web-panel/assets/{DID-yOJV99eJ.js → DID-It55Yem7.js} +2 -2
  29. package/src/assets/web-panel/assets/{Dashboard-CKsttz7D.js → Dashboard-9HkH6oxn.js} +2 -2
  30. package/src/assets/web-panel/assets/{Dropdown-CVFldXSL.js → Dropdown-00e2YWRB.js} +1 -1
  31. package/src/assets/web-panel/assets/{EmailListRenderer-Bufv638J.js → EmailListRenderer-By-ugRDL.js} +1 -1
  32. package/src/assets/web-panel/assets/{FamilyGuardDashboard-4ncjoIjL.js → FamilyGuardDashboard-C396yu5m.js} +1 -1
  33. package/src/assets/web-panel/assets/{Federation-DsknEDoX.js → Federation-BME5sbZY.js} +1 -1
  34. package/src/assets/web-panel/assets/{FormItemContext-Cqe5jWXA.js → FormItemContext-B4TEZCr-.js} +1 -1
  35. package/src/assets/web-panel/assets/{GenericCardRenderer-NAJaFKxc.js → GenericCardRenderer-_9CX9ieY.js} +1 -1
  36. package/src/assets/web-panel/assets/{Git-CszKQpnl.js → Git-BrS6WVSr.js} +2 -2
  37. package/src/assets/web-panel/assets/{Governance-BXGodXi6.js → Governance-BMdCWnKF.js} +1 -1
  38. package/src/assets/web-panel/assets/{Inference-CkoYuG2c.js → Inference-Bcp3vzcA.js} +1 -1
  39. package/src/assets/web-panel/assets/{KnowledgeGraph-DFlEr_T0.js → KnowledgeGraph-DlLuqGY5.js} +1 -1
  40. package/src/assets/web-panel/assets/{Logs-CVaENddA.js → Logs-D-W_6x09.js} +2 -2
  41. package/src/assets/web-panel/assets/MarkdownRenderer-DElGPxCZ.js +1 -0
  42. package/src/assets/web-panel/assets/{Marketplace-CDlLT0fw.js → Marketplace-Dzn2guvI.js} +1 -1
  43. package/src/assets/web-panel/assets/{McpTools-DdwOyszN.js → McpTools-uPict-KL.js} +6 -6
  44. package/src/assets/web-panel/assets/{Memory-DKQnkKuG.js → Memory-BCw43kOb.js} +2 -2
  45. package/src/assets/web-panel/assets/{MobileBridge-DGseqlzJ.js → MobileBridge-DmV8Wv7F.js} +1 -1
  46. package/src/assets/web-panel/assets/{MobileProjects-COBbEOlj.js → MobileProjects-C-HaCmSM.js} +1 -1
  47. package/src/assets/web-panel/assets/{Mtc-O61Vwyli.js → Mtc-C6jxJ34Y.js} +5 -5
  48. package/src/assets/web-panel/assets/{MtcAudit-Dw2SyasS.js → MtcAudit-Za8ExwhQ.js} +5 -5
  49. package/src/assets/web-panel/assets/{Multisig-hSu_l8kf.js → Multisig-KKDbuEgb.js} +3 -3
  50. package/src/assets/web-panel/assets/{NLProgramming-CX1Epurr.js → NLProgramming-Dj97zbCk.js} +1 -1
  51. package/src/assets/web-panel/assets/{Notes-R9xX8o8_.js → Notes-DBXvMraU.js} +4 -4
  52. package/src/assets/web-panel/assets/{NotificationSettings-jtSNLCuK.js → NotificationSettings-DOxITLYl.js} +1 -1
  53. package/src/assets/web-panel/assets/OrderTableRenderer-7DVjZwMM.js +1 -0
  54. package/src/assets/web-panel/assets/{Organization-Pu2WO-U3.js → Organization-D1i73mjb.js} +4 -4
  55. package/src/assets/web-panel/assets/{Overflow-BjnqUyFp.js → Overflow-kW6Q3rvg.js} +1 -1
  56. package/src/assets/web-panel/assets/{P2P-jjkvz44c.js → P2P-C-v_8p2v.js} +2 -2
  57. package/src/assets/web-panel/assets/PdhVaultBrowser-tUbbY64W.js +7 -0
  58. package/src/assets/web-panel/assets/{Permissions-D4yDpivd.js → Permissions-DkHxEZ58.js} +4 -4
  59. package/src/assets/web-panel/assets/{PersonalDataHub-0qsL6iH1.js → PersonalDataHub-DWuMPGrK.js} +5 -5
  60. package/src/assets/web-panel/assets/{Pipeline-Cx7Ybrk2.js → Pipeline-30IYXWWm.js} +1 -1
  61. package/src/assets/web-panel/assets/{Privacy-PbfR_M_V.js → Privacy-DZNQ4RrH.js} +1 -1
  62. package/src/assets/web-panel/assets/{ProjectInit-CyV6Gsub.js → ProjectInit-De2MCElf.js} +2 -2
  63. package/src/assets/web-panel/assets/{ProjectSettings-C7KZ2Wdk.js → ProjectSettings-5uHk9wsC.js} +2 -2
  64. package/src/assets/web-panel/assets/{Projects-DQDjO-yK.js → Projects-XjDfrFLb.js} +1 -1
  65. package/src/assets/web-panel/assets/{Providers-DyFVBBMX.js → Providers-Bgwj6gcE.js} +1 -1
  66. package/src/assets/web-panel/assets/{QrScannerModal-CzB1BC9_.js → QrScannerModal-gKql9umv.js} +1 -1
  67. package/src/assets/web-panel/assets/{QuickAsk-Bk636mC1.js → QuickAsk-BWvmwkh7.js} +1 -1
  68. package/src/assets/web-panel/assets/{Recommend-DoWxHIT3.js → Recommend-Uok4hZPd.js} +1 -1
  69. package/src/assets/web-panel/assets/{RemoteSession-oOVIv9wq.js → RemoteSession-DLJzTk9a.js} +3 -3
  70. package/src/assets/web-panel/assets/{Reputation-BucoReGG.js → Reputation-ZVwIPMSO.js} +1 -1
  71. package/src/assets/web-panel/assets/{Row-DzmOdJU7.js → Row-CN04HxWR.js} +1 -1
  72. package/src/assets/web-panel/assets/{RssFeed-C1tK7KGU.js → RssFeed-BdSrEG4g.js} +3 -3
  73. package/src/assets/web-panel/assets/{Search-CSnInOqk.js → Search-5PwVd0IR.js} +1 -1
  74. package/src/assets/web-panel/assets/{Security-dlejr15l.js → Security-DdBo4zjN.js} +3 -3
  75. package/src/assets/web-panel/assets/{Services-BioPZuNh.js → Services-D3letU7M.js} +2 -2
  76. package/src/assets/web-panel/assets/{Skeleton-DWMibwpC.js → Skeleton--Jb83f1v.js} +1 -1
  77. package/src/assets/web-panel/assets/{Skills-BAAnx1B2.js → Skills-Bg-CulHX.js} +1 -1
  78. package/src/assets/web-panel/assets/{Sla-jWNp4hNO.js → Sla-7QOa7S-X.js} +1 -1
  79. package/src/assets/web-panel/assets/{SpeechSettings-y5kqKha-.js → SpeechSettings-DQwppjRw.js} +1 -1
  80. package/src/assets/web-panel/assets/{SyncSettings-B9GNsc3e.js → SyncSettings-dNPQGL-s.js} +2 -2
  81. package/src/assets/web-panel/assets/{Tasks-BDBYKRen.js → Tasks-Ggs-Z48q.js} +1 -1
  82. package/src/assets/web-panel/assets/{Templates-DRiE5eaN.js → Templates-78TYHgRO.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tenant-BGlAvExF.js → Tenant-xHwwv9vO.js} +1 -1
  84. package/src/assets/web-panel/assets/{Terminal-B8uzrbNY.js → Terminal-BJXSJfm4.js} +2 -2
  85. package/src/assets/web-panel/assets/{TimelineRenderer-Df48iveY.js → TimelineRenderer-fLbjRhJw.js} +1 -1
  86. package/src/assets/web-panel/assets/{Tokens-CcmHndN6.js → Tokens-oMSBSBmB.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trigger-DPkWxmQ2.js → Trigger-CfnY_wX_.js} +1 -1
  88. package/src/assets/web-panel/assets/{Trust-DTb5a5AV.js → Trust-AdyTgYKO.js} +1 -1
  89. package/src/assets/web-panel/assets/{UkeySign-CBmg5vch.js → UkeySign-C5K_81dS.js} +1 -1
  90. package/src/assets/web-panel/assets/{VideoEditing-B0mZrz4O.js → VideoEditing-vcGVDqki.js} +1 -1
  91. package/src/assets/web-panel/assets/{Wallet-Bxs7_mpd.js → Wallet-hhOc3hlY.js} +4 -4
  92. package/src/assets/web-panel/assets/{WebAuthn-C8x3cMxU.js → WebAuthn-CVloC11s.js} +3 -3
  93. package/src/assets/web-panel/assets/{WorkflowEditor-DPrNznrP.js → WorkflowEditor-CYeE32R5.js} +1 -1
  94. package/src/assets/web-panel/assets/{chat-D3bYWu7S.js → chat-CNROR5bN.js} +1 -1
  95. package/src/assets/web-panel/assets/{colors-DY-PwMu4.js → colors-Cjx-cnwU.js} +1 -1
  96. package/src/assets/web-panel/assets/{compact-item-LDk10Kw6.js → compact-item-DOIfelvD.js} +1 -1
  97. package/src/assets/web-panel/assets/{createContext-C-2MZnFq.js → createContext-DfA7UZ8u.js} +1 -1
  98. package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +1 -0
  99. package/src/assets/web-panel/assets/{hasIn-BQIBjcnB.js → hasIn-BMSCzcfM.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CQVb2dWp.js → index-5-HXgOX5.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BdXnYMCZ.js → index-AWg-0M1S.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cx6D_dx2.js → index-B4iYzpkL.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-_HRO_cIu.js → index-B5Udeb_6.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DlRYqt3S.js → index-B7gf77CJ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BrnRO9yE.js → index-BEMwBBHk.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-VCh5zNJo.js → index-BM1y_9hM.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BRI3ngNg.js → index-BUonmKSE.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-C3GNvExG.js → index-BbQ2eQ8H.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DSvo_bp0.js → index-BemJ2u_E.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-L6qixyLc.js → index-BolaOQxx.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BI4KgcYC.js → index-BxOk9vkG.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CY4KnaO9.js → index-By61ojUx.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-L3smcoQX.js → index-C2K9SNVX.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BX2b9Orc.js → index-C3L5gdiD.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Cz4b6Z_7.js → index-C7xn0Nub.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-COG61sRv.js → index-CAnvhwg6.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C7FC8cP2.js → index-CEKMcOyU.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BN-xdSWZ.js → index-CNC5onm4.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DY2FIrmz.js → index-CVL27kXC.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CiTvBAr-.js → index-CVm1_i3t.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DhRt9cYJ.js → index-CdYBye8B.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B5pgtZnY.js → index-ClNMrqEY.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-7xKu7Fns.js → index-Cs6l7uRu.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D4K1weKc.js → index-CshQbilz.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-k7C_oDCO.js → index-Cyweh0Hy.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D3Oq-DLL.js → index-DBpWXFed.js} +3 -3
  127. package/src/assets/web-panel/assets/{index-DYyLJPlF.js → index-DHtpHZAQ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Dh-tkpKQ.js → index-DLmn0BoK.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-8JC9BJa1.js → index-DPAWBrMf.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Dd2Km97U.js → index-DjEsxO5h.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-KFZO_lcx.js → index-FBc_CJBd.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DTIZpPtG.js → index-Icdoqmzt.js} +1 -1
  133. package/src/assets/web-panel/assets/index-Q2IajL54.js +1 -0
  134. package/src/assets/web-panel/assets/index-bRkHgOMc.js +1 -0
  135. package/src/assets/web-panel/assets/{index-lmm9p-jm.js → index-dCd8cSUq.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-HasPt4-h.js → index-m2vG9gG_.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-CaslYch6.js → index-mTpHoEzr.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-CbR6ROOa.js → index-ti_lnTWl.js} +1 -1
  139. package/src/assets/web-panel/assets/{initDefaultProps-zQPSHf2C.js → initDefaultProps-Bu8k7H1Y.js} +1 -1
  140. package/src/assets/web-panel/assets/{motion-CKCStGVk.js → motion-s3iVK41k.js} +1 -1
  141. package/src/assets/web-panel/assets/{move-BufVDGYF.js → move-B6B-E5Jz.js} +1 -1
  142. package/src/assets/web-panel/assets/{mtc-parser-DcaFxy5D.js → mtc-parser-BiYgTidA.js} +1 -1
  143. package/src/assets/web-panel/assets/{omit-EScpkGdo.js → omit-pU1RdUDA.js} +1 -1
  144. package/src/assets/web-panel/assets/{pickAttrs-72hG_qfT.js → pickAttrs-Cdoq_SeM.js} +1 -1
  145. package/src/assets/web-panel/assets/{placementArrow-CMFWiH20.js → placementArrow-DIuvAn2K.js} +1 -1
  146. package/src/assets/web-panel/assets/{responsiveObserve-DKQHS41p.js → responsiveObserve-8w16YR2D.js} +1 -1
  147. package/src/assets/web-panel/assets/{slide-QVAFHTIQ.js → slide-VZ01cZe3.js} +1 -1
  148. package/src/assets/web-panel/assets/{statusUtils-DIz38e2G.js → statusUtils-lh8YMN8Z.js} +1 -1
  149. package/src/assets/web-panel/assets/{styleChecker-zK5cyLby.js → styleChecker-CmDyreec.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFlexGapSupport-DhZngk62.js → useFlexGapSupport-hiJWMU5y.js} +1 -1
  151. package/src/assets/web-panel/assets/{useFs-BvV2cvkg.js → useFs-Thnu-Iaj.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-CRmxb9l3.js → usePersonalDataHub-LBdOxjwY.js} +1 -1
  153. package/src/assets/web-panel/assets/{vnode-CgQIbobP.js → vnode-Cg6lFY_X.js} +1 -1
  154. package/src/assets/web-panel/assets/{zoom-D4XrrO0R.js → zoom-DVfsR1YO.js} +1 -1
  155. package/src/assets/web-panel/index.html +1 -1
  156. package/src/cli-help.js +181 -0
  157. package/src/command-help-index.json +181 -0
  158. package/src/command-manifest.json +1107 -175
  159. package/src/command-surface-policy.js +197 -0
  160. package/src/commands/agent.js +98 -33
  161. package/src/commands/artifacts.js +400 -2
  162. package/src/commands/background-session.js +41 -3
  163. package/src/commands/checkpoint-managed.js +1059 -0
  164. package/src/commands/checkpoint-restore-recovery.js +1476 -0
  165. package/src/commands/checkpoint.js +950 -22
  166. package/src/commands/compact.js +101 -25
  167. package/src/commands/config.js +506 -20
  168. package/src/commands/doctor.js +4 -1
  169. package/src/commands/hub.js +1 -1
  170. package/src/commands/mcp.js +688 -24
  171. package/src/commands/pack.js +16 -2
  172. package/src/commands/plugin.js +20 -12
  173. package/src/commands/session-mcp-recovery.js +218 -0
  174. package/src/commands/session.js +470 -110
  175. package/src/commands/setup.js +36 -10
  176. package/src/commands/skill.js +181 -110
  177. package/src/commands/status.js +21 -5
  178. package/src/commands/team-distributed.js +3324 -0
  179. package/src/commands/team.js +1130 -74
  180. package/src/commands/update.js +68 -6
  181. package/src/constants.js +11 -0
  182. package/src/data/changelog.json +26 -0
  183. package/src/gateways/ws/action-protocol.js +10 -0
  184. package/src/gateways/ws/session-protocol.js +555 -27
  185. package/src/gateways/ws/ws-agent-handler.js +541 -14
  186. package/src/gateways/ws/ws-session-gateway.js +129 -22
  187. package/src/gateways/ws/ws-session-state.js +431 -0
  188. package/src/harness/background-task-manager.js +233 -36
  189. package/src/harness/jsonl-session-store.js +2551 -167
  190. package/src/harness/mcp-client.js +802 -61
  191. package/src/harness/prompt-compressor.js +227 -27
  192. package/src/harness/session-index.js +89 -38
  193. package/src/harness/session-list-index.js +254 -0
  194. package/src/harness/structured-handoff.js +410 -0
  195. package/src/lazy-dispatch.js +747 -84
  196. package/src/lib/__tests__/logger.test.js +50 -2
  197. package/src/lib/advisor-runtime.js +1047 -0
  198. package/src/lib/agent-core.js +2 -0
  199. package/src/lib/agent-sandbox.js +120 -26
  200. package/src/lib/agent-session-export.js +20 -0
  201. package/src/lib/agent-team/task-lease.js +271 -3
  202. package/src/lib/agent-team/team-adjudication.js +1501 -0
  203. package/src/lib/agent-team/team-budget.js +4 -1
  204. package/src/lib/agent-team/team-control-store.js +582 -0
  205. package/src/lib/agent-team/team-distributed-queue.js +5246 -0
  206. package/src/lib/agent-team/team-process-checkpoint.js +211 -0
  207. package/src/lib/agent-team/team-runner.js +543 -24
  208. package/src/lib/agent-team/team-worktree.js +1403 -95
  209. package/src/lib/artifact-store.js +131 -8
  210. package/src/lib/background-agent-supervisor.js +185 -13
  211. package/src/lib/background-interaction-journal.js +37 -9
  212. package/src/lib/background-launch-profile.js +1058 -0
  213. package/src/lib/checkpoint-restore-already-completed-controller.js +902 -0
  214. package/src/lib/checkpoint-restore-orchestrator.js +987 -0
  215. package/src/lib/checkpoint-restore-partial-rollback-controller.js +1953 -0
  216. package/src/lib/checkpoint-restore-recovery-controller.js +689 -0
  217. package/src/lib/checkpoint-restore-recovery.js +884 -0
  218. package/src/lib/checkpoint-restore-retention.js +126 -0
  219. package/src/lib/checkpoint-restore-saga.js +5079 -0
  220. package/src/lib/checkpoint-restore-session-recovery.js +1178 -0
  221. package/src/lib/checkpoint-restore-workspace-target-verifier.js +270 -0
  222. package/src/lib/checkpoint-store.js +1688 -97
  223. package/src/lib/checkpoint-timeline-authority.js +522 -0
  224. package/src/lib/checkpoint-timeline.js +642 -0
  225. package/src/lib/config-keys.js +18 -119
  226. package/src/lib/config-manager.js +446 -27
  227. package/src/lib/config-redaction.js +57 -0
  228. package/src/lib/config-schema.js +853 -0
  229. package/src/lib/cowork-mcp-tools.js +5 -0
  230. package/src/lib/cowork-task-runner.js +458 -63
  231. package/src/lib/delivery-coordinator.js +1234 -0
  232. package/src/lib/delivery-evidence.js +450 -0
  233. package/src/lib/doctor-checkup.js +199 -14
  234. package/src/lib/executable-path.js +34 -0
  235. package/src/lib/fatal-handler.js +16 -0
  236. package/src/lib/file-checkpoint.js +4909 -186
  237. package/src/lib/file-lines.js +125 -0
  238. package/src/lib/headless-config-command.js +16 -5
  239. package/src/lib/hook-runner.cjs +139 -38
  240. package/src/lib/hooks-v2-workspace-context.js +10 -0
  241. package/src/lib/impacted-gate-selector.js +424 -0
  242. package/src/lib/interaction-adapter.js +62 -0
  243. package/src/lib/jsonl-session-store.js +1 -35
  244. package/src/lib/logger.js +66 -14
  245. package/src/lib/managed-checkpoint-render.js +60 -0
  246. package/src/lib/managed-tool-checkpoint.js +194 -0
  247. package/src/lib/mcp-call-ledger-store.js +1069 -0
  248. package/src/lib/mcp-call-ledger.js +747 -0
  249. package/src/lib/mcp-client.js +5 -0
  250. package/src/lib/mcp-conflict-scheduler.js +468 -0
  251. package/src/lib/mcp-effect-contract.js +102 -0
  252. package/src/lib/mcp-headers-helper-trust.js +145 -0
  253. package/src/lib/mcp-headers-helper.js +616 -0
  254. package/src/lib/mcp-host-recovery-runtime.js +131 -0
  255. package/src/lib/mcp-ledger-recovery-admission.js +1065 -0
  256. package/src/lib/mcp-recovery-adjudication.js +443 -0
  257. package/src/lib/output-context.js +86 -0
  258. package/src/lib/packer/file-identity.js +170 -0
  259. package/src/lib/packer/index.js +3 -0
  260. package/src/lib/packer/native-update-state.js +277 -0
  261. package/src/lib/packer/pack-update-applier.js +1983 -136
  262. package/src/lib/packer/pack-update-checker.js +34 -0
  263. package/src/lib/packer/pack-update-downloader.js +875 -86
  264. package/src/lib/packer/pack-update-signature.js +86 -0
  265. package/src/lib/packer/pkg-config-generator.js +6 -0
  266. package/src/lib/paths.js +65 -10
  267. package/src/lib/plan-mode.js +831 -14
  268. package/src/lib/plan-persistence.js +540 -0
  269. package/src/lib/plugin-runtime/hooks.js +227 -69
  270. package/src/lib/plugin-runtime/plugin-options.js +189 -44
  271. package/src/lib/pr-automation-policy.js +146 -8
  272. package/src/lib/process-execution-broker/credential-agent.js +7 -0
  273. package/src/lib/process-execution-broker/index.js +491 -13
  274. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
  275. package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
  276. package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
  277. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  278. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  279. package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
  280. package/src/lib/process-execution-broker/workspace-transaction.js +4459 -0
  281. package/src/lib/project-instructions.js +165 -29
  282. package/src/lib/project-mcp-trust.js +67 -0
  283. package/src/lib/public-ide-capability-manifest.js +272 -0
  284. package/src/lib/public-ide-capability-validation.js +351 -0
  285. package/src/lib/secret-store.js +267 -43
  286. package/src/lib/secure-file-identity.js +433 -0
  287. package/src/lib/secure-fs.js +1089 -0
  288. package/src/lib/security-audit.js +58 -0
  289. package/src/lib/service-manager.js +34 -13
  290. package/src/lib/session-budget-runtime.js +1452 -0
  291. package/src/lib/session-host-snapshot.js +388 -0
  292. package/src/lib/session-message-provenance.js +429 -0
  293. package/src/lib/session-projection.js +804 -0
  294. package/src/lib/session-resource-budget.js +1495 -0
  295. package/src/lib/settings-hooks.cjs +138 -22
  296. package/src/lib/settings-loader.cjs +25 -2
  297. package/src/lib/side-effect-ledger-store.js +45 -23
  298. package/src/lib/skill-execution-identity.js +217 -0
  299. package/src/lib/skill-loader.js +299 -18
  300. package/src/lib/sub-agent-context.js +236 -28
  301. package/src/lib/sub-agent-registry.js +5 -0
  302. package/src/lib/subagent-contract.js +94 -0
  303. package/src/lib/todo-manager.js +185 -18
  304. package/src/lib/todo-persistence.js +417 -0
  305. package/src/lib/turn-binding-store.js +41 -23
  306. package/src/lib/turn-binding.js +160 -2
  307. package/src/lib/with-file-lock.js +165 -24
  308. package/src/lib/ws-chat-handler.js +21 -0
  309. package/src/program-base.js +18 -0
  310. package/src/repl/advisor-command.js +103 -0
  311. package/src/repl/agent-repl.js +2175 -146
  312. package/src/repl/btw-command.js +93 -10
  313. package/src/repl/clipboard-image.js +119 -0
  314. package/src/repl/config-summary.js +64 -108
  315. package/src/repl/prompt-editor.js +233 -0
  316. package/src/repl/prompt-interactions.js +344 -0
  317. package/src/repl/prompt-stash.js +354 -0
  318. package/src/repl/prompt-suggestions.js +312 -0
  319. package/src/repl/repl-keybindings.js +227 -0
  320. package/src/repl/session-recap.js +217 -0
  321. package/src/repl/slash-command-registry.js +22 -0
  322. package/src/repl/terminal-layout.js +88 -0
  323. package/src/runtime/agent-core.js +1630 -447
  324. package/src/runtime/coding-agent-contract-shared.cjs +13 -1
  325. package/src/runtime/coding-agent-managed-tool-policy.cjs +7 -2
  326. package/src/runtime/diagnostics.js +32 -13
  327. package/src/runtime/headless-runner.js +310 -118
  328. package/src/runtime/headless-stream.js +356 -95
  329. package/src/runtime/mcp-config.js +148 -14
  330. package/src/runtime/policies/agent-policy.js +14 -0
  331. package/src/runtime/status-diagnostics-lite.js +137 -0
  332. package/src/assets/web-panel/assets/MarkdownRenderer-7iWGwZ-r.js +0 -1
  333. package/src/assets/web-panel/assets/OrderTableRenderer-BU2Yqq5H.js +0 -1
  334. package/src/assets/web-panel/assets/PdhVaultBrowser-69ca_WeR.js +0 -7
  335. package/src/assets/web-panel/assets/devWarning-B4dXIJqD.js +0 -1
  336. package/src/assets/web-panel/assets/index-C-C7DaG6.js +0 -1
  337. package/src/assets/web-panel/assets/index-DkfWzWtd.js +0 -1
@@ -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._fireHook("PlanRejected", { planId: this.currentPlan.id, reason });
579
- return this.exitPlanMode({ savePlan: true, reason: reason || "rejected" });
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
  }