chainlesschain 0.162.174 → 0.162.176

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 (281) hide show
  1. package/package.json +10 -10
  2. package/src/assets/web-panel/assets/{AIOps-Dcl-X1Hw.js → AIOps-BDLu5Elz.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-ClMh9HzT.js → ActionButton-DH5us-CK.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DPj0rSQ5.js → Analytics-BZ0u8IFG.js} +3 -3
  5. package/src/assets/web-panel/assets/AppLayout-Duu8xR14.js +9 -0
  6. package/src/assets/web-panel/assets/{Artifacts-L8781rE8.js → Artifacts-Br2YBbh_.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CffB6WIP.js → Audit-C3C6a482.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-BkLhayyY.js → BackgroundAgents-CzHvebcj.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BQnwFI78.js → Backup-DZNHYDFd.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-xuUGHQch.js → BaseInput-VRiC4fIx.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-feQXwWrg.js → Chat-Cm08WWAD.js} +5 -5
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-CGHYrugQ.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-DVaIiUGW.js → Checkbox-DtByk7d7.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-D2kEnzeU.js → Codegen-CphXXOfD.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-azDoyusS.js → Col-Hz86TnLR.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-OZdnPWM5.js → Community-HP03ltpD.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-VDMvBN-C.js → Compact-B_N1mqQz.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CPJ_zgQ1.js → Compliance-CVztRpi8.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-iRBwcsOA.js → Cowork-wUVOyZih.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-DrxrspUz.js → Cron-DW5TLJKw.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-D-rkAZXF.js → Crosschain-CrAFGhWH.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-5Pp1Xnmr.js → DID-pA-oD8ke.js} +2 -2
  23. package/src/assets/web-panel/assets/Dashboard-CXrd5gAw.js +3 -0
  24. package/src/assets/web-panel/assets/{Dropdown-BWj_E0Ie.js → Dropdown-EweJcsek.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-BT-wgSEh.js → EmailListRenderer-C9xVLPG4.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-1PzH23pa.js → FamilyGuardDashboard-CDcEEqlu.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-BxMIXGC6.js → Federation-DtZda9A_.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-CyJobBln.js → FormItemContext-CiYdcVz3.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-YkjFuQTN.js → GenericCardRenderer-DA13uwA0.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-C2a98Wp_.js → Git-DzRhZD1O.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-DKL3Fl84.js → Governance-D5RMgOSb.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-BJ2cQC_3.js → Inference-BgrPfSNk.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DBAsOngt.js → KnowledgeGraph-DzjnD571.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-COt8g9LO.js → Logs-BhWzVDtN.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-3Lfht1Fd.js → MarkdownRenderer-BDape9cx.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-C9Zk9Pb2.js → Marketplace-DJtL5GV7.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-DDWDFWPO.js → McpTools-CZcQygI7.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-BRmVye_o.js → Memory-DUWSDUUu.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-CppBbWpD.js → MobileBridge-BTwq2WKY.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-NZvvfDME.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-CcWpeGa6.js → Mtc-BiGugEQm.js} +6 -6
  42. package/src/assets/web-panel/assets/{MtcAudit-BcllEq0K.js → MtcAudit-DY7a6gUe.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-BFD1CarL.js → Multisig-BQbDu_w7.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-QNXXYvjz.js → NLProgramming-CM2wXTLv.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-07YwgQVy.js → Notes-DI6gGs1v.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-bOccEInE.js → NotificationSettings-BsZOLWAB.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DlRi29WL.js → OrderTableRenderer-C48mu09U.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-CBaezzEr.js → Organization-Cds0_tXr.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Bl5NohFO.js → Overflow-WunYMD87.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-CLMal9Aw.js → P2P-Bo6j4DDv.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-CGO0NITk.js → PdhVaultBrowser-Bh2XPD5f.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-CPSw48E2.js → Permissions-EMvMMC8N.js} +3 -3
  53. package/src/assets/web-panel/assets/{PersonalDataHub-CDM5n134.js → PersonalDataHub-AX3ypUDV.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-BdsqG7WV.js → Pipeline-BOx8HOgL.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-B6L4E-M7.js → Privacy-tBeTVQVM.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-DXPyZKRs.js → ProjectInit-CVYg1W4M.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-BrCC3xYT.js → ProjectSettings-Dvt5I7y1.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-BYmuHC2q.js → Projects-Cc7FC9-e.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-CbCe2eUl.js → Providers-Ct8xqY-E.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DkukgCrR.js → QrScannerModal-Cni8Ooja.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-D-htOrV8.js → QuickAsk-BJVLKdxr.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-Bm7SIfzu.js → Recommend-DPG8Rbbm.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C9rP-iMc.js → RemoteSession-DsdR7dCt.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-kGlUgbsl.js → Reputation-Csg1YM-b.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-PBupbbUA.js → Row-D9DoFoEf.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-njlwKUGK.js → RssFeed-BsvKrXMS.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-DLdEmgBw.js → Search-DFUStfgQ.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-SXGPSHdu.js → Security-ILg1zxdK.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-CuFGnNsb.js → Services-Dd41PCaY.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-CtUC0mDa.js → Skeleton-Bzo1cN80.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-P1qgOVFn.js → Skills-BDHh17P0.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-CsppE8YV.js → Sla-jl7UTiRY.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-Cq1oG56m.js → SpeechSettings-BePa-RF6.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-DdKW6V-C.js → SyncSettings-DiqqDKGZ.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-BCc9san5.js → Tasks-DYQ_6lxj.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-CcHC1dMA.js → Templates-JiDeI06L.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-CA4PqU22.js → Tenant-hhgI6zwS.js} +1 -1
  78. package/src/assets/web-panel/assets/Terminal-CjTZPa37.js +3 -0
  79. package/src/assets/web-panel/assets/{TimelineRenderer-DHWTSvOy.js → TimelineRenderer-B8uB5Llk.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-Ddz-P_yh.js → Tokens-C3b_6r2z.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-ePmrmLZ1.js → Trigger-tIP7HKVe.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DmuK1CCE.js → Trust-vjoc11vJ.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CqqBuRrT.js → UkeySign-DUPBnGuH.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-BGlR3uWM.js → VideoEditing-CBvhUY5T.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-Ba_Qx6Ga.js → Wallet-DUaQ0uA5.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-29neXKt2.js → WebAuthn-DllSivjE.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-lD0lR0UQ.js → WorkflowEditor-xc8D3uO6.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-B3stH01u.js → chat-BVicU7oA.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-Czly56MN.js → colors-DJzLmhLm.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-C0TkbkAJ.js → compact-item-BvYRfThE.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-BJmpSlf1.js → createContext-DGXxCBKR.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-BT46Y_QI.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-D1q-zw7u.js → hasIn-SBDJB1y4.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DV-twjNO.js → index-1SdDS6lR.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-B-kTvMq7.js → index-9gdI7uUW.js} +1 -1
  96. package/src/assets/web-panel/assets/index-B7nuIywP.js +1 -0
  97. package/src/assets/web-panel/assets/{index-Bm_-RKal.js → index-BJZf9DhV.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CQftlZn-.js → index-BO1itZaY.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-Dw3L_smW.js → index-BTrBGBJs.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-C7sFKQ53.js → index-BYqz4Wrr.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DlvsSuBC.js → index-Bf-ctERC.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-ChESIe5r.js → index-CKyEivf8.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DaqEHaCg.js → index-CdGY9P8a.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DZeoKL6t.js → index-Cf-O_g_8.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-XWV8JWMc.js → index-CloCgBUR.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CpMIdPJ0.js → index-CoJAO1Lm.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CgS8XUBU.js → index-Crk5NTb0.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DmUta2lw.js → index-Cs2rmLFt.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Dpz_7sf-.js → index-CtwqO5qo.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DRcQsPez.js → index-D7HimK0M.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BEt2-3x5.js → index-DCdRAgjy.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CHvPlzWJ.js → index-DCqRS6da.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BDHXx65P.js → index-DHVLJ8q0.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-K4YaPDOn.js → index-DKrvvWm4.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C1pOqU7D.js → index-DMarmfrv.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DAhrn9EJ.js → index-DRfiXB_p.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-cPjWjvmW.js → index-Dm_77jgR.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-C27TnOoT.js → index-DnjPdvR6.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-upY9rkGZ.js → index-DonCnfMr.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CbvNJUwu.js → index-DvUTDYXX.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BVwJ3jht.js → index-FxZzRyE1.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DIF6XHIi.js → index-MjC4G09d.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-B8KqsomG.js → index-VMfwIH5G.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-8GKykbS4.js → index-YJkzy8ZF.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DpGs96FC.js → index-d5BVd-cL.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DO8xOwxi.js → index-e4fXH_WO.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Cu_4HaUS.js → index-hxTODbM1.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Cf7Gr3NG.js → index-lpaGoTH3.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DBqAm8es.js → index-wcCL-miV.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-ZDUhX41Z.js → index-xfluRkCT.js} +1 -1
  131. package/src/assets/web-panel/assets/index-ycwAfC-i.js +1 -0
  132. package/src/assets/web-panel/assets/{index-z8ATo2aQ.js → index-z1ZylsLq.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-QWEXi6-t.js → initDefaultProps-C1zO1KnY.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BfrQwvV1.js → motion-CWLtEGmC.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-Dn7x0H9t.js → move-DzD_C3Vm.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-F61D_TgE.js → mtc-parser-D880PYJl.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-4SQZ39PF.js → omit-CFR_z2ov.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-C7DTe126.js → pickAttrs-srIhgrSC.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-40NeyzDK.js → placementArrow-C1rXpibP.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-BiUJ6qWs.js → responsiveObserve-C30gRcSS.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-CacJTM3f.js → slide-CvtKiMfV.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-K812HB-k.js → statusUtils-DNHO7ITG.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-CKeHA2eM.js → styleChecker-Dt_Sl-g4.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-CKl4FqXF.js → useFlexGapSupport-Cjev42Gh.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DPoiJjSZ.js → useFs-Baxglj2V.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-aHwBspmt.js → usePersonalDataHub-B4NkcLaP.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-DrA-GuCT.js → vnode-mIRkdT7o.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-G3LMswgy.js → zoom-HzI5DlH_.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/auth/npm-auth.js +30 -5
  151. package/src/commands/agenda.js +64 -2
  152. package/src/commands/agent.js +45 -3
  153. package/src/commands/background-session.js +103 -0
  154. package/src/commands/config.js +70 -5
  155. package/src/commands/context.js +36 -2
  156. package/src/commands/hub.js +19 -1
  157. package/src/commands/memory.js +21 -6
  158. package/src/commands/permissions.js +57 -1
  159. package/src/commands/plugin.js +2 -0
  160. package/src/commands/review.js +23 -11
  161. package/src/commands/routine.js +31 -8
  162. package/src/commands/session.js +11 -3
  163. package/src/commands/team.js +20 -7
  164. package/src/commands/update.js +65 -10
  165. package/src/esm-bridge.js +3 -0
  166. package/src/gateways/terminal/PtyManager.js +8 -1
  167. package/src/gateways/ws/message-dispatcher.js +3 -0
  168. package/src/gateways/ws/session-protocol.js +26 -0
  169. package/src/gateways/ws/ws-agent-handler.js +107 -69
  170. package/src/gateways/ws/ws-server.js +92 -4
  171. package/src/harness/jsonl-session-store.js +13 -6
  172. package/src/harness/mcp-client.js +200 -1
  173. package/src/index.js +123 -55
  174. package/src/lazy-dispatch.js +86 -85
  175. package/src/lib/__tests__/personal-data-hub-aichat-wizard.test.js +15 -0
  176. package/src/lib/agent-coordinator.js +4 -0
  177. package/src/lib/agent-ipc-bus.js +32 -2
  178. package/src/lib/agent-sandbox.js +16 -2
  179. package/src/lib/agent-schedule-store.js +55 -0
  180. package/src/lib/agent-tool-admission.js +104 -0
  181. package/src/lib/api-key-helper.js +10 -2
  182. package/src/lib/async-hook-supervisor.cjs +26 -4
  183. package/src/lib/background-interaction-resolver.js +71 -0
  184. package/src/lib/background-session-transport.js +59 -1
  185. package/src/lib/changelog.js +19 -11
  186. package/src/lib/channels/channel-manager.js +6 -0
  187. package/src/lib/channels/telegram-channel.js +11 -1
  188. package/src/lib/channels/webhook-channel.js +15 -3
  189. package/src/lib/checkpoint-store.js +10 -2
  190. package/src/lib/cloud/bundle.js +12 -2
  191. package/src/lib/code-review.js +32 -8
  192. package/src/lib/computer-use/control-backend.js +18 -3
  193. package/src/lib/context-breakdown.js +46 -0
  194. package/src/lib/context-source-ledger.js +43 -0
  195. package/src/lib/crash-reporter.js +6 -0
  196. package/src/lib/diff-review-follow-up.js +154 -0
  197. package/src/lib/downloader.js +17 -3
  198. package/src/lib/ensure-utf8.js +11 -3
  199. package/src/lib/event-runtime-producer.js +58 -0
  200. package/src/lib/event-runtime-store.js +354 -0
  201. package/src/lib/event-runtime-worker.js +109 -0
  202. package/src/lib/execution-backend.js +72 -21
  203. package/src/lib/git-integration.js +19 -4
  204. package/src/lib/hook-manager.js +34 -15
  205. package/src/lib/hook-runner.cjs +48 -3
  206. package/src/lib/hooks-v2-producers.js +15 -0
  207. package/src/lib/hooks-v2-runtime.js +70 -21
  208. package/src/lib/host-adb-bridge.js +111 -21
  209. package/src/lib/ide-context.js +128 -1
  210. package/src/lib/interaction-adapter.js +39 -0
  211. package/src/lib/ipc-attach-protocol.js +98 -0
  212. package/src/lib/json-schema-validate.js +190 -36
  213. package/src/lib/lan-pairing-preflight.js +13 -2
  214. package/src/lib/lsp/__tests__/lsp-server-registry.test.js +18 -0
  215. package/src/lib/lsp/lsp-client.js +18 -1
  216. package/src/lib/lsp/lsp-manager.js +4 -0
  217. package/src/lib/lsp/lsp-server-registry.js +11 -0
  218. package/src/lib/mcp-oauth.js +8 -2
  219. package/src/lib/npm-invocation.js +29 -0
  220. package/src/lib/packer/pkg-runner.js +13 -2
  221. package/src/lib/packer/precheck.js +33 -16
  222. package/src/lib/packer/web-panel-builder.js +16 -2
  223. package/src/lib/permission-rules.cjs +5 -1
  224. package/src/lib/personal-data-hub-aichat-wizard.js +22 -6
  225. package/src/lib/personal-data-hub-wiring.js +27 -3
  226. package/src/lib/plan-mode.js +204 -11
  227. package/src/lib/plugin-monitor-supervisor.js +39 -4
  228. package/src/lib/plugin-runtime/bin.js +45 -0
  229. package/src/lib/plugin-runtime/hooks.js +14 -1
  230. package/src/lib/plugin-runtime/install.js +2 -1
  231. package/src/lib/plugin-runtime/lsp.js +3 -0
  232. package/src/lib/plugin-runtime/mcp.js +8 -1
  233. package/src/lib/plugin-runtime/monitors.js +4 -0
  234. package/src/lib/plugin-runtime/plugin-options.js +74 -7
  235. package/src/lib/plugin-runtime/signature.js +45 -0
  236. package/src/lib/pr-create.js +21 -8
  237. package/src/lib/pr-link-ledger.js +15 -3
  238. package/src/lib/process-execution-broker/credential-agent.js +295 -0
  239. package/src/lib/process-execution-broker/index.js +440 -14
  240. package/src/lib/process-execution-broker/patch-child-process.js +2 -3
  241. package/src/lib/process-execution-broker/platform-sandbox.js +351 -0
  242. package/src/lib/process-manager.js +18 -3
  243. package/src/lib/provenance-api.js +6 -0
  244. package/src/lib/proxy-bootstrap.js +19 -0
  245. package/src/lib/publish-workspace.js +14 -6
  246. package/src/lib/repl-rewind.js +9 -4
  247. package/src/lib/secret-store.js +159 -0
  248. package/src/lib/service-manager.js +64 -29
  249. package/src/lib/session-hooks.js +328 -473
  250. package/src/lib/session-telemetry-middleware.js +6 -0
  251. package/src/lib/settings-hook-events.cjs +61 -1
  252. package/src/lib/settings-hooks.cjs +4 -0
  253. package/src/lib/side-effect-ledger.js +22 -0
  254. package/src/lib/sub-agent-context.js +5 -0
  255. package/src/lib/sub-agent-profiles.js +4 -0
  256. package/src/lib/trace-api.js +6 -0
  257. package/src/lib/turn-context.js +14 -8
  258. package/src/lib/unattended-action-policy.js +83 -0
  259. package/src/lib/update-notice.js +9 -45
  260. package/src/lib/with-file-lock.js +7 -0
  261. package/src/lib/ws-server.js +1 -0
  262. package/src/repl/agent-repl.js +71 -13
  263. package/src/repl/clipboard-copy.js +8 -2
  264. package/src/repl/pr-comments.js +30 -7
  265. package/src/runtime/agent-core.js +439 -87
  266. package/src/runtime/agent-runtime.js +1 -0
  267. package/src/runtime/coding-agent-contract-shared.cjs +52 -0
  268. package/src/runtime/coding-agent-policy.cjs +20 -0
  269. package/src/runtime/diagnostics.js +73 -8
  270. package/src/runtime/headless-runner.js +63 -0
  271. package/src/runtime/headless-stream.js +269 -17
  272. package/src/runtime/mcp-config.js +16 -1
  273. package/src/workers/background-agent-worker.js +80 -1
  274. package/src/assets/web-panel/assets/AppLayout-7iSKDmlI.js +0 -9
  275. package/src/assets/web-panel/assets/ChatBubbleRenderer-DcbUHeGW.js +0 -1
  276. package/src/assets/web-panel/assets/Dashboard-BAKYNavw.js +0 -3
  277. package/src/assets/web-panel/assets/MobileProjects-C4GKn1dH.js +0 -1
  278. package/src/assets/web-panel/assets/Terminal-Fj7ea0In.js +0 -3
  279. package/src/assets/web-panel/assets/devWarning-CDI8Sbaj.js +0 -1
  280. package/src/assets/web-panel/assets/index-96LBR96V.js +0 -1
  281. package/src/assets/web-panel/assets/index-DyQkiW24.js +0 -1
@@ -25,7 +25,7 @@ import pathDefault from "node:path";
25
25
  import { homedir as homedirDefault } from "node:os";
26
26
  import crypto from "node:crypto";
27
27
  import http from "node:http";
28
- import { spawn } from "node:child_process";
28
+ import { executionBroker } from "./process-execution-broker/index.js";
29
29
 
30
30
  export const _deps = {
31
31
  fetch: (...a) => globalThis.fetch(...a),
@@ -35,7 +35,13 @@ export const _deps = {
35
35
  sha256: (s) => crypto.createHash("sha256").update(s).digest(),
36
36
  createServer: (h) => http.createServer(h),
37
37
  openBrowser: defaultOpenBrowser,
38
- spawn: (...a) => spawn(...a),
38
+ spawn: (command, args, options) =>
39
+ executionBroker.spawn(command, args, {
40
+ ...options,
41
+ origin: "mcp:oauth-browser",
42
+ policy: "allow",
43
+ scope: "oauth",
44
+ }),
39
45
  now: () => Date.now(),
40
46
  // Backoff sleep seam (tests override with a no-op so the retry doesn't wait).
41
47
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
@@ -0,0 +1,29 @@
1
+ import path from "node:path";
2
+
3
+ /**
4
+ * Resolve npm without asking a shell to interpret the platform shim.
5
+ *
6
+ * Windows npm installations expose `npm.cmd`, but Node cannot execute a CMD
7
+ * shim with `shell:false` (`spawnSync npm.cmd EINVAL`). The shim delegates to
8
+ * the npm CLI located beside node.exe, so invoke that JavaScript entrypoint
9
+ * through the current Node executable instead.
10
+ */
11
+ export function resolveNpmInvocation({
12
+ platform = process.platform,
13
+ execPath = process.execPath,
14
+ } = {}) {
15
+ if (platform !== "win32") {
16
+ return { command: "npm", prefixArgs: [] };
17
+ }
18
+
19
+ const npmCliPath = path.win32.join(
20
+ path.win32.dirname(execPath),
21
+ "node_modules",
22
+ "npm",
23
+ "bin",
24
+ "npm-cli.js",
25
+ );
26
+ return { command: execPath, prefixArgs: [npmCliPath] };
27
+ }
28
+
29
+ export default resolveNpmInvocation;
@@ -14,8 +14,12 @@
14
14
  import fs from "node:fs";
15
15
  import path from "node:path";
16
16
  import { createRequire } from "node:module";
17
- import { spawnSync } from "node:child_process";
18
17
  import { PackError, EXIT } from "./errors.js";
18
+ import executionBroker from "../process-execution-broker/index.js";
19
+
20
+ export const _deps = {
21
+ spawnSync: (...args) => executionBroker.spawnSync(...args),
22
+ };
19
23
 
20
24
  /**
21
25
  * @param {object} ctx
@@ -47,12 +51,19 @@ export function runPkg(ctx) {
47
51
  ];
48
52
 
49
53
  log(` [pkg] Running: ${pkgBin.runtime} ${args.join(" ")}`);
50
- const res = spawnSync(pkgBin.runtime, args, {
54
+ const res = _deps.spawnSync(pkgBin.runtime, args, {
55
+ origin: "packer:pkg",
56
+ scope: "pack",
57
+ policy: "allow",
58
+ shell: false,
51
59
  cwd: path.dirname(pkgConfigFile),
52
60
  stdio: "inherit",
53
61
  windowsHide: true,
54
62
  });
55
63
 
64
+ if (res.error) {
65
+ throw new PackError(`Failed to start pkg: ${res.error.message}`, EXIT.PKG);
66
+ }
56
67
  if (res.status !== 0) {
57
68
  throw new PackError(
58
69
  `pkg exited with code ${res.status}. See output above for details.`,
@@ -5,9 +5,13 @@
5
5
 
6
6
  import fs from "node:fs";
7
7
  import path from "node:path";
8
- import { execSync } from "node:child_process";
9
8
  import { fileURLToPath } from "node:url";
10
9
  import { PackError, EXIT } from "./errors.js";
10
+ import executionBroker from "../process-execution-broker/index.js";
11
+
12
+ export const _deps = {
13
+ execFileSync: (...args) => executionBroker.execFileSync(...args),
14
+ };
11
15
 
12
16
  const WINDOWS_RESERVED = new Set([
13
17
  "CON",
@@ -139,22 +143,35 @@ export function precheck(ctx) {
139
143
  let gitCommit = null;
140
144
  let dirty = false;
141
145
  let repoRoot = null;
146
+ const gitOptions = (cwd) => ({
147
+ origin: "packer:precheck-git",
148
+ scope: "pack",
149
+ policy: "allow",
150
+ shell: false,
151
+ cwd,
152
+ encoding: "utf-8",
153
+ stdio: ["ignore", "pipe", "ignore"],
154
+ });
142
155
  try {
143
- repoRoot = execSync("git rev-parse --show-toplevel", {
144
- cwd: projectRoot,
145
- encoding: "utf-8",
146
- stdio: ["ignore", "pipe", "ignore"],
147
- }).trim();
148
- gitCommit = execSync("git rev-parse --short HEAD", {
149
- cwd: repoRoot,
150
- encoding: "utf-8",
151
- stdio: ["ignore", "pipe", "ignore"],
152
- }).trim();
153
- const status = execSync("git status --porcelain", {
154
- cwd: repoRoot,
155
- encoding: "utf-8",
156
- stdio: ["ignore", "pipe", "ignore"],
157
- });
156
+ repoRoot = _deps
157
+ .execFileSync(
158
+ "git",
159
+ ["rev-parse", "--show-toplevel"],
160
+ gitOptions(projectRoot),
161
+ )
162
+ .trim();
163
+ gitCommit = _deps
164
+ .execFileSync(
165
+ "git",
166
+ ["rev-parse", "--short", "HEAD"],
167
+ gitOptions(repoRoot),
168
+ )
169
+ .trim();
170
+ const status = _deps.execFileSync(
171
+ "git",
172
+ ["status", "--porcelain"],
173
+ gitOptions(repoRoot),
174
+ );
158
175
  dirty = status.trim().length > 0;
159
176
  } catch (_e) {
160
177
  // Not a git repo — fine, leave the metadata null
@@ -10,8 +10,12 @@
10
10
 
11
11
  import fs from "node:fs";
12
12
  import path from "node:path";
13
- import { spawnSync } from "node:child_process";
14
13
  import { PackError, EXIT } from "./errors.js";
14
+ import executionBroker from "../process-execution-broker/index.js";
15
+
16
+ export const _deps = {
17
+ spawnSync: (...args) => executionBroker.spawnSync(...args),
18
+ };
15
19
 
16
20
  /**
17
21
  * @param {object} ctx
@@ -50,11 +54,21 @@ export function ensureWebPanel(ctx) {
50
54
  if (needsBuild) {
51
55
  log(" [web-panel] Building Vue panel via 'npm run build:web-panel'...");
52
56
  const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
53
- const res = spawnSync(npmCmd, ["run", "build:web-panel"], {
57
+ const res = _deps.spawnSync(npmCmd, ["run", "build:web-panel"], {
58
+ origin: "packer:web-panel-build",
59
+ scope: "pack",
60
+ policy: "allow",
61
+ shell: false,
54
62
  cwd: cliRoot,
55
63
  stdio: "inherit",
56
64
  windowsHide: true,
57
65
  });
66
+ if (res.error) {
67
+ throw new PackError(
68
+ `Failed to start 'npm run build:web-panel': ${res.error.message}`,
69
+ EXIT.WEB_PANEL,
70
+ );
71
+ }
58
72
  if (res.status !== 0) {
59
73
  throw new PackError(
60
74
  `'npm run build:web-panel' exited with code ${res.status}`,
@@ -61,7 +61,7 @@ const TOOL_GROUPS = Object.freeze({
61
61
  grep: ["search_files"],
62
62
  glob: ["search_files"],
63
63
  write: ["write_file"],
64
- edit: ["edit_file", "edit_file_hashed"],
64
+ edit: ["edit_file", "edit_file_hashed", "delete_file", "move_file"],
65
65
  webfetch: ["web_fetch"],
66
66
  websearch: ["web_search"],
67
67
  task: ["spawn_sub_agent"],
@@ -82,6 +82,8 @@ const PATH_TOOLS = new Set([
82
82
  "write_file",
83
83
  "edit_file",
84
84
  "edit_file_hashed",
85
+ "delete_file",
86
+ "move_file",
85
87
  ]);
86
88
  /** Tools whose match target is a URL. */
87
89
  const URL_TOOLS = new Set(["web_fetch"]);
@@ -354,6 +356,8 @@ const SUGGEST_UMBRELLA = Object.freeze({
354
356
  write_file: "Write",
355
357
  edit_file: "Edit",
356
358
  edit_file_hashed: "Edit",
359
+ delete_file: "Edit",
360
+ move_file: "Edit",
357
361
  web_fetch: "WebFetch",
358
362
  web_search: "WebSearch",
359
363
  spawn_sub_agent: "Task",
@@ -141,7 +141,11 @@ export function createAccountsStore({ hubDir }) {
141
141
  * spec.validateCookie(). Kept duplicated so the cli build stays ESM-pure
142
142
  * without reaching into desktop-app-vue.
143
143
  */
144
- export function createVendorAdapterBridge({ specs, _httpClientFactory } = {}) {
144
+ export function createVendorAdapterBridge({
145
+ specs,
146
+ runtimeAdapter,
147
+ _httpClientFactory,
148
+ } = {}) {
145
149
  // DEFAULT_VENDOR_SPECS is shipped as a vendor-keyed object; tests pass an
146
150
  // array. Normalize to an array first so byVendor lookup works either way.
147
151
  const effectiveSpecs = specs ?? _loadAichatModule().DEFAULT_VENDOR_SPECS;
@@ -187,7 +191,15 @@ export function createVendorAdapterBridge({ specs, _httpClientFactory } = {}) {
187
191
  });
188
192
  try {
189
193
  const r = await spec.validateCookie({ httpClient: client, session });
190
- return r || { ok: false, reason: "VALIDATE_RETURNED_NULL" };
194
+ const result = r || { ok: false, reason: "VALIDATE_RETURNED_NULL" };
195
+ if (
196
+ result.ok &&
197
+ runtimeAdapter &&
198
+ typeof runtimeAdapter.setSession === "function"
199
+ ) {
200
+ runtimeAdapter.setSession(vendor, session);
201
+ }
202
+ return result;
191
203
  } catch (err) {
192
204
  return { ok: false, reason: "VALIDATE_THREW", error: err.message };
193
205
  }
@@ -233,6 +245,8 @@ const _wizardsByHubDir = new Map();
233
245
  */
234
246
  export function getAIChatWizard({
235
247
  hubDir,
248
+ accountsStore,
249
+ vendorAdapter,
236
250
  _accountsStore,
237
251
  _vendorAdapter,
238
252
  _deps,
@@ -242,8 +256,10 @@ export function getAIChatWizard({
242
256
  if (!isTest && _wizardsByHubDir.has(hubDir))
243
257
  return _wizardsByHubDir.get(hubDir);
244
258
 
245
- const accountsStore = _accountsStore || createAccountsStore({ hubDir });
246
- const vendorAdapter = _vendorAdapter || createVendorAdapterBridge();
259
+ const effectiveAccountsStore =
260
+ accountsStore || _accountsStore || createAccountsStore({ hubDir });
261
+ const effectiveVendorAdapter =
262
+ vendorAdapter || _vendorAdapter || createVendorAdapterBridge();
247
263
  // Paste-mode is the default for cli. Tests can override via _deps.
248
264
  const deps = _deps || {
249
265
  sessionFactory: () => ({}),
@@ -273,8 +289,8 @@ export function getAIChatWizard({
273
289
  "@chainlesschain/personal-data-hub/adapters/ai-chat-history/wizard-controller",
274
290
  );
275
291
  const wiz = createAIChatWizardController({
276
- accountsStore,
277
- vendorAdapter,
292
+ accountsStore: effectiveAccountsStore,
293
+ vendorAdapter: effectiveVendorAdapter,
278
294
  _deps: deps,
279
295
  });
280
296
  if (!isTest) _wizardsByHubDir.set(hubDir, wiz);
@@ -136,6 +136,7 @@ let LocalVault,
136
136
  AmapAdapter,
137
137
  TelegramAdapter,
138
138
  WhatsAppAdapter,
139
+ AIChatHistoryAdapter,
139
140
  EntityResolver,
140
141
  EntityResolverEmbeddingStage,
141
142
  EntityResolverLLMStage,
@@ -259,6 +260,7 @@ function ensurePdhLoaded() {
259
260
  AmapAdapter,
260
261
  TelegramAdapter,
261
262
  WhatsAppAdapter,
263
+ AIChatHistoryAdapter,
262
264
  EntityResolver,
263
265
  EntityResolverEmbeddingStage,
264
266
  EntityResolverLLMStage,
@@ -587,6 +589,8 @@ async function initHub() {
587
589
  // KuaishouSignBridge.
588
590
  const { createKuaishouCookiesExtension } =
589
591
  await import("@chainlesschain/personal-data-hub/adapters/social-kuaishou-adb");
592
+ const { createWhatsAppBackupExtension } =
593
+ await import("@chainlesschain/personal-data-hub/adapters/messaging-whatsapp");
590
594
  hostAdbBridge = createHostAdbBridge({
591
595
  extensions: {
592
596
  "bilibili.cookies": createBilibiliCookiesExtension(),
@@ -597,6 +601,7 @@ async function initHub() {
597
601
  "toutiao.cookies": createToutiaoCookiesExtension(),
598
602
  "toutiao.account": createToutiaoAccountExtension(),
599
603
  "kuaishou.cookies": createKuaishouCookiesExtension(),
604
+ "whatsapp.backup": createWhatsAppBackupExtension(),
600
605
  },
601
606
  });
602
607
  sda._deps.bridgeProvider = () => hostAdbBridge;
@@ -796,7 +801,9 @@ async function initHub() {
796
801
  WhatsAppAdapter,
797
802
  ]) {
798
803
  try {
799
- const adapter = new Cls();
804
+ const adapter = Cls === WhatsAppAdapter
805
+ ? new Cls({ bridgeProvider: () => hostAdbBridge })
806
+ : new Cls();
800
807
  if (!registry.has(adapter.name)) registry.register(adapter);
801
808
  } catch (_err) {
802
809
  // Continue boot even if one adapter ctor throws
@@ -838,7 +845,18 @@ async function initHub() {
838
845
  // periodic loop still validates persisted cookies so list-aichat-accounts
839
846
  // can surface lastHealth reliably.
840
847
  const aichatAccountsStore = createAIChatAccountsStore({ hubDir });
841
- const aichatVendorAdapter = createAIChatVendorAdapterBridge();
848
+ const aiChatAdapter = new AIChatHistoryAdapter();
849
+ try {
850
+ const persistedAccounts = await aichatAccountsStore.list();
851
+ aiChatAdapter.restoreSessions(persistedAccounts);
852
+ if (!registry.has(aiChatAdapter.name)) registry.register(aiChatAdapter);
853
+ } catch (_err) {
854
+ // Corrupt rows are skipped by restoreSessions; a store/registry failure
855
+ // must not prevent the rest of the hub from booting.
856
+ }
857
+ const aichatVendorAdapter = createAIChatVendorAdapterBridge({
858
+ runtimeAdapter: aiChatAdapter,
859
+ });
842
860
  const createAIChatHealthChecker = await loadAIChatHealthChecker();
843
861
  const aichatHealthChecker = createAIChatHealthChecker({
844
862
  accountsStore: aichatAccountsStore,
@@ -849,7 +867,11 @@ async function initHub() {
849
867
  } catch (_err) {
850
868
  // Continue boot even if checker fails to schedule
851
869
  }
852
- const aichatWizard = getAIChatWizard({ hubDir });
870
+ const aichatWizard = getAIChatWizard({
871
+ hubDir,
872
+ accountsStore: aichatAccountsStore,
873
+ vendorAdapter: aichatVendorAdapter,
874
+ });
853
875
 
854
876
  return {
855
877
  vault,
@@ -864,6 +886,7 @@ async function initHub() {
864
886
  alipayAccountsPath,
865
887
  entityResolver,
866
888
  aichatAccountsStore,
889
+ aiChatAdapter,
867
890
  aichatWizard,
868
891
  aichatHealthChecker,
869
892
  analysisSkillNames: ANALYSIS_SKILL_NAMES,
@@ -896,6 +919,7 @@ async function initHub() {
896
919
  return { ok: false, reason: "NOT_REGISTERED", vendor };
897
920
  }
898
921
  await aichatAccountsStore.delete(vendor);
922
+ aiChatAdapter.clearSession(vendor);
899
923
  try {
900
924
  await aichatWizard.rotateLoginPartition({ vendor });
901
925
  } catch (_err) {
@@ -48,6 +48,8 @@ const READ_TOOLS = new Set([
48
48
  const WRITE_TOOLS = new Set([
49
49
  "write_file",
50
50
  "edit_file",
51
+ "delete_file",
52
+ "move_file",
51
53
  "run_shell",
52
54
  "git",
53
55
  "run_skill",
@@ -66,6 +68,8 @@ const TOOL_RISK_WEIGHTS = {
66
68
  list_skills: 1,
67
69
  write_file: 2,
68
70
  edit_file: 2,
71
+ delete_file: 2,
72
+ move_file: 2,
69
73
  run_skill: 2,
70
74
  run_shell: 3,
71
75
  git: 3,
@@ -91,6 +95,10 @@ export class PlanItem {
91
95
  this.status = data.status || PlanStatus.PENDING;
92
96
  this.result = null;
93
97
  this.error = null;
98
+ this.turn = Number.isInteger(data.turn) && data.turn > 0 ? data.turn : null;
99
+ this.toolUseId = data.toolUseId || null;
100
+ this.startedAt = data.startedAt || null;
101
+ this.completedAt = data.completedAt || null;
94
102
  }
95
103
 
96
104
  /**
@@ -109,12 +117,16 @@ export class PlanItem {
109
117
  */
110
118
  export class ExecutionPlan {
111
119
  constructor(data = {}) {
112
- this.id = data.id || `plan-${Date.now()}`;
120
+ this.id =
121
+ data.id || `plan-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
113
122
  this.title = data.title || "Untitled Plan";
114
123
  this.description = data.description || "";
115
124
  this.goal = data.goal || "";
116
125
  this.items = (data.items || []).map((i) => new PlanItem(i));
117
126
  this.status = data.status || PlanState.ANALYZING;
127
+ this.version =
128
+ Number.isInteger(data.version) && data.version > 0 ? data.version : 1;
129
+ this.revisionOf = data.revisionOf || null;
118
130
  this.createdAt = new Date().toISOString();
119
131
  }
120
132
 
@@ -258,6 +270,7 @@ export class PlanModeManager extends EventEmitter {
258
270
  this.currentPlan = null;
259
271
  this.history = [];
260
272
  this.blockedToolLog = [];
273
+ this.executionLock = null;
261
274
  this._hookDb = null;
262
275
  }
263
276
 
@@ -289,6 +302,7 @@ export class PlanModeManager extends EventEmitter {
289
302
  });
290
303
  this.state = PlanState.ANALYZING;
291
304
  this.blockedToolLog = [];
305
+ this.executionLock = null;
292
306
 
293
307
  this.emit("enter", { plan: this.currentPlan, state: this.state });
294
308
  this._fireHook("PlanModeEnter", { planId: this.currentPlan.id });
@@ -311,6 +325,7 @@ export class PlanModeManager extends EventEmitter {
311
325
  this.state = PlanState.INACTIVE;
312
326
  this.currentPlan = null;
313
327
  this.blockedToolLog = [];
328
+ this.executionLock = null;
314
329
 
315
330
  this.emit("exit", { plan, reason: options.reason || "manual" });
316
331
  return { plan };
@@ -323,6 +338,12 @@ export class PlanModeManager extends EventEmitter {
323
338
  if (!this.currentPlan) {
324
339
  return { error: "No active plan" };
325
340
  }
341
+ if (
342
+ this.state !== PlanState.ANALYZING &&
343
+ this.state !== PlanState.PLAN_READY
344
+ ) {
345
+ return { error: "Approved plan is locked" };
346
+ }
326
347
 
327
348
  const item = this.currentPlan.addItem(itemData);
328
349
  this.emit("item-added", { planId: this.currentPlan.id, item });
@@ -343,6 +364,49 @@ export class PlanModeManager extends EventEmitter {
343
364
  return { plan: this.currentPlan };
344
365
  }
345
366
 
367
+ /**
368
+ * Freeze the current draft and start a fresh plan version. This is the only
369
+ * supported way to replace an awaiting-review plan, so old/new IDE diffs can
370
+ * bind to stable plan IDs instead of guessing from mutable item arrays.
371
+ */
372
+ beginPlanRevision(options = {}) {
373
+ if (!this.currentPlan || !this.isActive()) {
374
+ return { error: "No active plan" };
375
+ }
376
+ if (
377
+ this.state !== PlanState.ANALYZING &&
378
+ this.state !== PlanState.PLAN_READY
379
+ ) {
380
+ return { error: "Approved plan is locked" };
381
+ }
382
+
383
+ const previousPlan = this.currentPlan;
384
+ this.history.push(previousPlan);
385
+ this.currentPlan = new ExecutionPlan({
386
+ title: options.title || previousPlan.title,
387
+ goal: options.goal ?? previousPlan.goal,
388
+ description: options.description ?? previousPlan.description,
389
+ version: previousPlan.version + 1,
390
+ revisionOf: previousPlan.id,
391
+ });
392
+ this.state = PlanState.ANALYZING;
393
+ this.blockedToolLog = [];
394
+ this.executionLock = null;
395
+ const payload = {
396
+ plan: this.currentPlan,
397
+ previousPlan,
398
+ reason: String(options.reason || "revision"),
399
+ };
400
+ this.emit("plan-revised", payload);
401
+ this._fireHook("PlanRevised", {
402
+ planId: this.currentPlan.id,
403
+ previousPlanId: previousPlan.id,
404
+ version: this.currentPlan.version,
405
+ reason: payload.reason,
406
+ });
407
+ return payload;
408
+ }
409
+
346
410
  /**
347
411
  * Approve the plan (or specific items)
348
412
  */
@@ -362,17 +426,140 @@ export class PlanModeManager extends EventEmitter {
362
426
  item.status = PlanStatus.APPROVED;
363
427
  }
364
428
 
429
+ this.executionLock = this._buildExecutionLock(
430
+ approvedItems,
431
+ options.permissionMode,
432
+ );
365
433
  this.state = PlanState.APPROVED;
366
434
  this.currentPlan.status = PlanState.APPROVED;
367
435
  this.emit("plan-approved", {
368
436
  plan: this.currentPlan,
369
437
  approvedCount: approvedItems.length,
438
+ executionLock: this.getExecutionLock(),
370
439
  });
371
440
  this._fireHook("PlanApproved", {
372
441
  planId: this.currentPlan.id,
373
442
  itemCount: approvedItems.length,
443
+ permissionMode: this.executionLock.permissionMode,
444
+ allowedTools: this.executionLock.allowedTools,
445
+ });
446
+ return {
447
+ plan: this.currentPlan,
448
+ approvedCount: approvedItems.length,
449
+ executionLock: this.getExecutionLock(),
450
+ };
451
+ }
452
+
453
+ /** Build the immutable authority envelope used after plan approval. */
454
+ _buildExecutionLock(approvedItems, permissionMode) {
455
+ const approvedItemIds = approvedItems.map((item) => item.id);
456
+ const allowedTools = [
457
+ ...new Set([
458
+ ...READ_TOOLS,
459
+ ...approvedItems.map((item) => item.tool).filter(Boolean),
460
+ ]),
461
+ ].sort();
462
+ return Object.freeze({
463
+ planId: this.currentPlan.id,
464
+ permissionMode: String(permissionMode || "default"),
465
+ approvedItemIds: Object.freeze(approvedItemIds),
466
+ allowedTools: Object.freeze(allowedTools),
467
+ createdAt: new Date().toISOString(),
374
468
  });
375
- return { plan: this.currentPlan, approvedCount: approvedItems.length };
469
+ }
470
+
471
+ /** Return a serializable copy so callers cannot widen the live lock. */
472
+ getExecutionLock() {
473
+ if (!this.executionLock) return null;
474
+ return {
475
+ ...this.executionLock,
476
+ approvedItemIds: [...this.executionLock.approvedItemIds],
477
+ allowedTools: [...this.executionLock.allowedTools],
478
+ };
479
+ }
480
+
481
+ /**
482
+ * Attribute a live tool call to the next approved item for that tool.
483
+ * Read-only/helper calls that were not part of the approved plan remain
484
+ * unbound instead of advancing an unrelated item.
485
+ */
486
+ startPlanItemForTool(toolName, context = {}) {
487
+ if (
488
+ !this.currentPlan ||
489
+ (this.state !== PlanState.APPROVED && this.state !== PlanState.EXECUTING)
490
+ ) {
491
+ return null;
492
+ }
493
+ const tool = String(toolName || "");
494
+ const item = this.currentPlan.items.find(
495
+ (candidate) =>
496
+ candidate.status === PlanStatus.APPROVED && candidate.tool === tool,
497
+ );
498
+ if (!item) return null;
499
+
500
+ item.status = PlanStatus.EXECUTING;
501
+ item.toolUseId = context.toolUseId || null;
502
+ item.turn =
503
+ Number.isInteger(context.turn) && context.turn > 0 ? context.turn : null;
504
+ item.startedAt = String(context.startedAt || new Date().toISOString());
505
+ item.completedAt = null;
506
+ item.result = null;
507
+ item.error = null;
508
+ this.state = PlanState.EXECUTING;
509
+ this.currentPlan.status = PlanState.EXECUTING;
510
+ this.emit("item-executing", { planId: this.currentPlan.id, item });
511
+ this._fireHook("PlanItemExecute", {
512
+ planId: this.currentPlan.id,
513
+ itemId: item.id,
514
+ tool: item.tool,
515
+ toolUseId: item.toolUseId,
516
+ turn: item.turn,
517
+ });
518
+ return item;
519
+ }
520
+
521
+ /** Settle a tool-attributed item and derive the aggregate execution state. */
522
+ settlePlanItem(itemId, options = {}) {
523
+ if (!this.currentPlan || !itemId) return null;
524
+ const item = this.currentPlan.getItem(itemId);
525
+ if (!item || item.status !== PlanStatus.EXECUTING) return null;
526
+
527
+ const success = options.success !== false;
528
+ item.status = success ? PlanStatus.COMPLETED : PlanStatus.FAILED;
529
+ item.completedAt = String(options.completedAt || new Date().toISOString());
530
+ item.result = success ? (options.result ?? null) : null;
531
+ item.error = success ? null : String(options.error || "tool failed");
532
+
533
+ const tracked = this.currentPlan.items.filter((candidate) =>
534
+ [
535
+ PlanStatus.APPROVED,
536
+ PlanStatus.EXECUTING,
537
+ PlanStatus.COMPLETED,
538
+ PlanStatus.FAILED,
539
+ ].includes(candidate.status),
540
+ );
541
+ const allSettled =
542
+ tracked.length > 0 &&
543
+ tracked.every((candidate) =>
544
+ [PlanStatus.COMPLETED, PlanStatus.FAILED].includes(candidate.status),
545
+ );
546
+ if (allSettled) {
547
+ this.state = tracked.some(
548
+ (candidate) => candidate.status === PlanStatus.FAILED,
549
+ )
550
+ ? PlanState.FAILED
551
+ : PlanState.COMPLETED;
552
+ } else {
553
+ this.state = PlanState.EXECUTING;
554
+ }
555
+ this.currentPlan.status = this.state;
556
+ this.emit("item-settled", {
557
+ planId: this.currentPlan.id,
558
+ item,
559
+ success,
560
+ state: this.state,
561
+ });
562
+ return item;
376
563
  }
377
564
 
378
565
  /**
@@ -401,7 +588,10 @@ export class PlanModeManager extends EventEmitter {
401
588
  this.state === PlanState.APPROVED ||
402
589
  this.state === PlanState.EXECUTING
403
590
  ) {
404
- return true;
591
+ const allowed =
592
+ this.executionLock?.allowedTools.includes(toolName) === true;
593
+ if (!allowed) this._recordBlockedTool(toolName, "execution-lock");
594
+ return allowed;
405
595
  }
406
596
 
407
597
  // In analyzing/plan_ready state, only read tools are allowed
@@ -409,11 +599,7 @@ export class PlanModeManager extends EventEmitter {
409
599
 
410
600
  // Block write tools and log
411
601
  if (WRITE_TOOLS.has(toolName)) {
412
- this.blockedToolLog.push({
413
- tool: toolName,
414
- timestamp: new Date().toISOString(),
415
- });
416
- this.emit("tool-blocked", { toolName });
602
+ this._recordBlockedTool(toolName, "planning");
417
603
  return false;
418
604
  }
419
605
 
@@ -421,6 +607,15 @@ export class PlanModeManager extends EventEmitter {
421
607
  return false;
422
608
  }
423
609
 
610
+ _recordBlockedTool(toolName, reason) {
611
+ this.blockedToolLog.push({
612
+ tool: toolName,
613
+ reason,
614
+ timestamp: new Date().toISOString(),
615
+ });
616
+ this.emit("tool-blocked", { toolName, reason });
617
+ }
618
+
424
619
  /**
425
620
  * Generate a text summary of the current plan
426
621
  */
@@ -501,9 +696,7 @@ export class PlanModeManager extends EventEmitter {
501
696
 
502
697
  const allDone = results.every((r) => r.success);
503
698
  this.state = allDone ? PlanState.COMPLETED : PlanState.FAILED;
504
- this.currentPlan.status = allDone
505
- ? PlanState.COMPLETED
506
- : PlanState.FAILED;
699
+ this.currentPlan.status = allDone ? PlanState.COMPLETED : PlanState.FAILED;
507
700
 
508
701
  return { results, success: allDone };
509
702
  }