chainlesschain 0.162.175 → 0.162.177

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 (311) hide show
  1. package/package.json +10 -10
  2. package/src/assets/web-panel/assets/{AIOps-DherW0Rh.js → AIOps-DT-VPWuN.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-CDGfqt2Y.js → ActionButton-DyOreOVN.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-BYhkzdgD.js → Analytics-DslNsKux.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-cEKk1cpb.js → AppLayout-BNJJAYME.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-_aXR3WWc.js → Artifacts-DoaAtEcY.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CYAf-gY7.js → Audit-DPG3WdJ9.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-D4Q20phR.js → BackgroundAgents-C_JO6R5G.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-Cq0Tyk58.js → Backup-Cv3BooyX.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-CPj_CdAv.js → BaseInput-D2uQXWu-.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-BQ-Qq0es.js → Chat-B4Vczyf3.js} +4 -4
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-DE6reP99.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-rke6l3yg.js → Checkbox-qTgk4VeP.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-DiacrIPq.js → Codegen-DlLWcIzj.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-Cx3BY-Up.js → Col-DNO73qp7.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DfmWiQy8.js → Community-vRDNpc4j.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-DdQnpxy5.js → Compact-CEWtIXni.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-lzliOH_r.js → Compliance-KgYC9Fxi.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CYx4Un5k.js → Cowork-zj5wvkpT.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-BGDcPFlo.js → Cron-BLDoCozU.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-0_X5g2ri.js → Crosschain-BYnff4VY.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-C0PEf3Eu.js → DID-C75VFsVm.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-DULQjQ58.js → Dashboard-C4Gutni2.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-DGRHRbW6.js → Dropdown-B5dbO4Kx.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-ChX8XPwB.js → EmailListRenderer-BDipZtHJ.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Dyq6IBNu.js → FamilyGuardDashboard-DmIzt8jj.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-DH1NfAL2.js → Federation-D9QOS-uW.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-BN1I_uNi.js → FormItemContext-B47HRwAm.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-Dq_2UYpe.js → GenericCardRenderer-3R4mXmgV.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-BAy1yvI2.js → Git-DrNXZf1C.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-Db6Q2iSy.js → Governance-BGd7JR98.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-0ub6yHcr.js → Inference-D-2he1ed.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DtLetM36.js → KnowledgeGraph-C0sHIbK5.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-Bcoy7CUd.js → Logs-DiBQO-jL.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BfEMFAXf.js → MarkdownRenderer-DL_5eV2D.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-tt2rzwRR.js → Marketplace-VR59OdrC.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-BhOgX-MU.js → McpTools-DrPzwSd3.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-BJUxeWBL.js → Memory-CBgKALwQ.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BwUY2G9w.js → MobileBridge-pQ9Tn-Mn.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-BLRfHhqi.js → MobileProjects-mASB6jyj.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-B2WMboOm.js → Mtc-DTMgE7HC.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-8pNEXO63.js → MtcAudit-CwTnCtiL.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-C1wm5a_1.js → Multisig-ngm6WU8f.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-C1QpKF7g.js → NLProgramming-4YO4Qww9.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CiQUcxFu.js → Notes-DP1k45mF.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-BblDPJ_x.js → NotificationSettings-r2H8yPKb.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DRjYcPtb.js → OrderTableRenderer-CoM_Zw0J.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-Dp3VKB0X.js → Organization-DrO7jRWS.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Czx161m9.js → Overflow-Cnwu4i4t.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-ClFi6wOs.js → P2P-zi-cDwA_.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-CRweW0lz.js → PdhVaultBrowser-DBEryDv7.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-BzcRoqTn.js → Permissions-lqXK2C3x.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-ChGTpNMd.js → PersonalDataHub-b63T3HJM.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-BMOOpKWA.js → Pipeline-Kep5KAGW.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-D0AZHObH.js → Privacy-CHdvOAh1.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-yYFlZ6Ja.js → ProjectInit-BhKfdwe9.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-DJNVuMQs.js → ProjectSettings-Yavk9K8A.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-Br0rBWH1.js → Projects-CN8E_5Mq.js} +1 -1
  59. package/src/assets/web-panel/assets/Providers-Bfym6SQn.js +1 -0
  60. package/src/assets/web-panel/assets/{QrScannerModal-81NZOitV.js → QrScannerModal-CLSQcrlR.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-Dic2lCbJ.js → QuickAsk-VPa2WKQN.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-Dh9iwViE.js → Recommend-CGXR8jVU.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-Bq9VYEFy.js → RemoteSession-C_20YfMB.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-CxFZ-NbJ.js → Reputation-awZh68UV.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-BVSLcaX2.js → Row-Ctb4oiYN.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-BbC-5DOH.js → RssFeed-D3sdjvUV.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-BfjZ1GCr.js → Search-C-zfA4kT.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-CVjhOV5r.js → Security-D9XlmHA3.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-D45u-Aqe.js → Services-C3UKnr41.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-WkGoK4he.js → Skeleton-BQxagZfF.js} +1 -1
  71. package/src/assets/web-panel/assets/Skills-q6eb6KPZ.js +1 -0
  72. package/src/assets/web-panel/assets/{Sla-Bf4QTF3E.js → Sla-DveENvek.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-DVpZdbMN.js → SpeechSettings-BFjXLNgF.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-DEEojoMS.js → SyncSettings-BpHgbaP9.js} +2 -2
  75. package/src/assets/web-panel/assets/Tasks-DvepRb8-.js +1 -0
  76. package/src/assets/web-panel/assets/{Templates-Bzl-9wY5.js → Templates-DEiaoyve.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-0khxUOB8.js → Tenant-ITg774nY.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-DWGYhAYU.js → Terminal-D3ZBcvnb.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-B0Y4LONc.js → TimelineRenderer-BsLU6JjU.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-DEE07SFh.js → Tokens-Cb5Pquc9.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-BM7xvA5z.js → Trigger-Dwfy-m9N.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-Dv3ijvvZ.js → Trust-CnlSRGG0.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-B1-enmMt.js → UkeySign-Y4TlvKVo.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-DYiIPByd.js → VideoEditing-BpKLtTTO.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-D-y374BQ.js → Wallet-LLBzIq8x.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-HBDjcUhi.js → WebAuthn-BeSRiusP.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-sKn4qTID.js → WorkflowEditor-BK_l1tXO.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-TVYhaBXj.js → chat-DYd-X7Wg.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-DMADxY9v.js → colors-C9uip71j.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-OftcHpO_.js → compact-item-JBrfjUP6.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-DW1zVUSN.js → createContext-D03fbuiq.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-B8LQ8leS.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-CaUathNp.js → hasIn-DLQOR6l-.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-Cmdv6Dev.js → index-5YN7F5bO.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-iDmgT2Dg.js → index-6GuLRpBQ.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-77kgkUgY.js → index-B0X76LjX.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CM8W07sc.js → index-B46YcgsV.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-C9JoaYSs.js → index-BNCdRmWf.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CpDtN0dl.js → index-BYv-LWdy.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CZXYI5LU.js → index-Ba-Ps_AJ.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DWVZ3T0t.js → index-BcBZwlnG.js} +1 -1
  102. package/src/assets/web-panel/assets/{index--5UvwqH_.js → index-BdH9l8wl.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BBNg08cD.js → index-BfOs_bmg.js} +3 -3
  104. package/src/assets/web-panel/assets/index-Bh8BPtqR.js +1 -0
  105. package/src/assets/web-panel/assets/{index-s-EnkUCn.js → index-BnQgRjsm.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Lr0MO-GU.js → index-BpYKLrUT.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B-19mFhn.js → index-BuNFCZCx.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CeCIYYG7.js → index-ByHO3SqZ.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-hXq7QRA_.js → index-C2zmOsyD.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-8YelceUU.js → index-CChQBM1d.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-B52Ztt8Q.js → index-CChaISMb.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DKjqFpYa.js → index-CILVyK3b.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DAfT3EMd.js → index-CL0ixCxk.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-sm1sGIxW.js → index-CS63r8h3.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DHi634g9.js → index-CVZWiUE_.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BEzLLVYd.js → index-Ce0Bf1YF.js} +1 -1
  117. package/src/assets/web-panel/assets/index-CeVrHLzZ.js +1 -0
  118. package/src/assets/web-panel/assets/{index-pSVCBsG4.js → index-Ck17i0OD.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-GUKz4Pv9.js → index-Cpn19c9k.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CKNdnJRo.js → index-CqWk1FLG.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Bg-7FpaL.js → index-CwoDpKzW.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DYwwUKDg.js → index-D-1SqEpQ.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DZdT8nio.js → index-D4UY5Z60.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BvRw4Fna.js → index-D8KvuDz0.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-kdMl_0zL.js → index-DNMAAUVk.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CK-p7Hji.js → index-DYLZY4uh.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DXcqvWBp.js → index-DaokIHt3.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BFVbDpqg.js → index-Q7mmL6w4.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BJ1PjRY3.js → index-SO6lgMg0.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BSp1Ofvx.js → index-iahNhZwm.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BupSWDcl.js → index-l08afIbV.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D63QVAiJ.js → index-rIxxfdTN.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-DdcKTlQR.js → initDefaultProps-CG9z1c6w.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-CL-hTBQj.js → motion-B1Ky7Zff.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-qYSAkxNG.js → move-DME8X2P1.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-ZwRSZtRP.js → mtc-parser-cuJYSWB7.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-Bld-8ftI.js → omit-UI_EE0uZ.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-DGHs-a9Z.js → pickAttrs-BIoE_Z_V.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-DRQPMyI5.js → placementArrow-B48-9HKN.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-C5MJG_Yo.js → responsiveObserve-D7x_GDgD.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-VZ-fpZCD.js → slide-DjzC5_7m.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-9ocfCyHk.js → statusUtils-C6BIymhh.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-B6QAwAN5.js → styleChecker-B4dGrdD9.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-Bq1twor3.js → useFlexGapSupport-Bken4aNE.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DKZq96tz.js → useFs-BMlqECqv.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-D0RmRVXf.js → usePersonalDataHub-DLdrfmMa.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-D6kEj_tt.js → vnode-CKJQmxa0.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-2yfasoQf.js → zoom-CaZunuau.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 +83 -5
  152. package/src/commands/agent.js +42 -4
  153. package/src/commands/background-session.js +11 -4
  154. package/src/commands/batch.js +79 -27
  155. package/src/commands/changelog.js +15 -4
  156. package/src/commands/config.js +70 -5
  157. package/src/commands/context.js +36 -2
  158. package/src/commands/eval.js +21 -6
  159. package/src/commands/hook.js +2 -2
  160. package/src/commands/hub.js +19 -1
  161. package/src/commands/loop.js +9 -2
  162. package/src/commands/memory.js +21 -6
  163. package/src/commands/permissions.js +57 -1
  164. package/src/commands/plugin.js +2 -0
  165. package/src/commands/review.js +23 -11
  166. package/src/commands/routine.js +31 -8
  167. package/src/commands/session.js +11 -3
  168. package/src/commands/skill.js +2 -0
  169. package/src/commands/statusline.js +3 -3
  170. package/src/commands/team.js +20 -7
  171. package/src/commands/update.js +65 -10
  172. package/src/data/changelog.json +293 -51
  173. package/src/gateways/terminal/PtyManager.js +8 -1
  174. package/src/gateways/ws/message-dispatcher.js +3 -0
  175. package/src/gateways/ws/session-protocol.js +26 -0
  176. package/src/gateways/ws/ws-agent-handler.js +107 -69
  177. package/src/gateways/ws/ws-server.js +74 -2
  178. package/src/harness/background-task-manager.js +18 -6
  179. package/src/harness/background-task-worker.js +11 -18
  180. package/src/harness/jsonl-session-store.js +13 -6
  181. package/src/harness/mcp-client.js +195 -2
  182. package/src/harness/worktree-isolator.js +18 -21
  183. package/src/lib/__tests__/personal-data-hub-aichat-wizard.test.js +15 -0
  184. package/src/lib/agent-coordinator.js +4 -0
  185. package/src/lib/agent-core.js +5 -0
  186. package/src/lib/agent-ipc-bus.js +44 -5
  187. package/src/lib/agent-sandbox.js +16 -2
  188. package/src/lib/agent-schedule-store.js +55 -0
  189. package/src/lib/agent-team/team-worktree.js +28 -6
  190. package/src/lib/agent-tool-admission.js +104 -0
  191. package/src/lib/agent-worktree.js +20 -19
  192. package/src/lib/api-key-helper.js +10 -2
  193. package/src/lib/async-hook-supervisor.cjs +135 -15
  194. package/src/lib/async-hook-supervisor.js +47 -0
  195. package/src/lib/background-agent-supervisor.js +35 -12
  196. package/src/lib/background-task-manager.js +5 -1
  197. package/src/lib/changelog.js +76 -11
  198. package/src/lib/channels/channel-manager.js +6 -0
  199. package/src/lib/channels/telegram-channel.js +11 -1
  200. package/src/lib/channels/webhook-channel.js +15 -3
  201. package/src/lib/checkpoint-store.js +10 -2
  202. package/src/lib/chrome-connector.js +6 -2
  203. package/src/lib/claude-code-bridge.js +24 -8
  204. package/src/lib/cli-anything-bridge.js +82 -25
  205. package/src/lib/cloud/bundle.js +12 -2
  206. package/src/lib/code-review.js +32 -8
  207. package/src/lib/computer-use/control-backend.js +18 -3
  208. package/src/lib/context-breakdown.js +46 -0
  209. package/src/lib/context-source-ledger.js +43 -0
  210. package/src/lib/diff-review-follow-up.js +154 -0
  211. package/src/lib/doctor-checkup.js +18 -6
  212. package/src/lib/downloader.js +17 -3
  213. package/src/lib/ensure-utf8.js +11 -3
  214. package/src/lib/eval/tasks.js +25 -31
  215. package/src/lib/event-runtime-producer.js +58 -0
  216. package/src/lib/event-runtime-store.js +354 -0
  217. package/src/lib/event-runtime-worker.js +109 -0
  218. package/src/lib/execution-backend.js +72 -21
  219. package/src/lib/git-integration.js +19 -4
  220. package/src/lib/hook-manager.js +6 -15
  221. package/src/lib/hook-runner.cjs +88 -8
  222. package/src/lib/hook-runner.js +55 -0
  223. package/src/lib/hooks-v2-producers.js +15 -0
  224. package/src/lib/hooks-v2-runtime.js +70 -21
  225. package/src/lib/host-adb-bridge.js +129 -24
  226. package/src/lib/ide-context.js +128 -1
  227. package/src/lib/interaction-adapter.js +39 -0
  228. package/src/lib/json-schema-validate.js +190 -36
  229. package/src/lib/lan-pairing-preflight.js +13 -2
  230. package/src/lib/lsp/__tests__/benchmark.test.js +61 -0
  231. package/src/lib/lsp/__tests__/lsp-client.test.js +6 -1
  232. package/src/lib/lsp/__tests__/lsp-server-registry.test.js +18 -0
  233. package/src/lib/lsp/benchmark.js +20 -4
  234. package/src/lib/lsp/lsp-client.js +22 -3
  235. package/src/lib/lsp/lsp-manager.js +4 -0
  236. package/src/lib/lsp/lsp-server-registry.js +11 -2
  237. package/src/lib/mcp-oauth.js +8 -2
  238. package/src/lib/npm-invocation.js +29 -0
  239. package/src/lib/orchestrator.js +8 -2
  240. package/src/lib/packer/pack-update-applier.js +14 -2
  241. package/src/lib/packer/pkg-runner.js +13 -2
  242. package/src/lib/packer/precheck.js +33 -16
  243. package/src/lib/packer/smoke-runner.js +18 -5
  244. package/src/lib/packer/web-panel-builder.js +16 -2
  245. package/src/lib/paths.js +1 -1
  246. package/src/lib/permission-rules.cjs +5 -1
  247. package/src/lib/personal-data-hub-aichat-wizard.js +22 -6
  248. package/src/lib/personal-data-hub-wiring.js +27 -3
  249. package/src/lib/plan-mode.js +204 -11
  250. package/src/lib/plugin-monitor-supervisor.js +48 -6
  251. package/src/lib/plugin-runtime/bin.js +45 -0
  252. package/src/lib/plugin-runtime/hooks.js +14 -1
  253. package/src/lib/plugin-runtime/install.js +11 -3
  254. package/src/lib/plugin-runtime/lsp.js +3 -0
  255. package/src/lib/plugin-runtime/mcp.js +8 -1
  256. package/src/lib/plugin-runtime/monitors.js +4 -0
  257. package/src/lib/plugin-runtime/plugin-options.js +74 -7
  258. package/src/lib/plugin-runtime/signature.js +45 -0
  259. package/src/lib/pr-create.js +21 -8
  260. package/src/lib/pr-link-ledger.js +15 -3
  261. package/src/lib/process-execution-broker/credential-agent.js +23 -3
  262. package/src/lib/process-execution-broker/index.js +315 -38
  263. package/src/lib/process-execution-broker/patch-child-process.js +2 -3
  264. package/src/lib/process-manager.js +18 -3
  265. package/src/lib/publish-workspace.js +14 -6
  266. package/src/lib/repl-bang-memorize.js +10 -2
  267. package/src/lib/repl-goal.js +19 -2
  268. package/src/lib/secret-store.js +159 -0
  269. package/src/lib/service-manager.js +64 -29
  270. package/src/lib/settings-hook-events.cjs +74 -9
  271. package/src/lib/settings-hook-events.js +20 -0
  272. package/src/lib/settings-hooks.cjs +4 -0
  273. package/src/lib/side-effect-ledger.js +22 -0
  274. package/src/lib/skill-packs/generator.js +18 -9
  275. package/src/lib/skill-process-broker.js +71 -0
  276. package/src/lib/slash-commands.js +10 -2
  277. package/src/lib/status-line.cjs +28 -7
  278. package/src/lib/status-line.js +33 -0
  279. package/src/lib/sub-agent-context.js +5 -0
  280. package/src/lib/sub-agent-profiles.js +4 -0
  281. package/src/lib/turn-context.js +14 -8
  282. package/src/lib/unattended-action-policy.js +83 -0
  283. package/src/lib/update-notice.js +6 -2
  284. package/src/lib/with-file-lock.js +7 -0
  285. package/src/lib/ws-server.js +1 -0
  286. package/src/repl/agent-repl.js +60 -23
  287. package/src/repl/clipboard-copy.js +8 -2
  288. package/src/repl/pr-comments.js +30 -7
  289. package/src/runtime/agent-core.js +554 -118
  290. package/src/runtime/agent-runtime.js +1 -0
  291. package/src/runtime/coding-agent-contract-shared.cjs +52 -0
  292. package/src/runtime/coding-agent-policy.cjs +20 -0
  293. package/src/runtime/diagnostics.js +73 -8
  294. package/src/runtime/headless-runner.js +88 -10
  295. package/src/runtime/headless-stream.js +343 -21
  296. package/src/runtime/mcp-config.js +16 -1
  297. package/src/runtime/session-slash-commands.js +506 -0
  298. package/src/skills/video-editing/extractors/audio-extractor.js +13 -5
  299. package/src/skills/video-editing/extractors/video-extractor.js +13 -3
  300. package/src/skills/video-editing/media-process.js +20 -0
  301. package/src/skills/video-editing/render/audio-mix.js +13 -3
  302. package/src/skills/video-editing/render/ffmpeg-concat.js +7 -2
  303. package/src/skills/video-editing/render/ffmpeg-extract.js +7 -2
  304. package/src/workers/background-agent-worker.js +6 -2
  305. package/src/assets/web-panel/assets/ChatBubbleRenderer-Ci97vNTs.js +0 -1
  306. package/src/assets/web-panel/assets/Providers-zZRhNtSu.js +0 -1
  307. package/src/assets/web-panel/assets/Skills-D_jvQZcj.js +0 -1
  308. package/src/assets/web-panel/assets/Tasks-DFwxpLIk.js +0 -1
  309. package/src/assets/web-panel/assets/devWarning-CLOk6rdE.js +0 -1
  310. package/src/assets/web-panel/assets/index-CSgUfMTM.js +0 -1
  311. package/src/assets/web-panel/assets/index-DliSZvAP.js +0 -1
@@ -8,17 +8,19 @@
8
8
  * thin re-export shim for backwards compatibility.
9
9
  */
10
10
 
11
- import { spawn } from "child_process";
11
+ import { executionBroker } from "../lib/process-execution-broker/index.js";
12
+ import { AsyncLocalStorage } from "node:async_hooks";
12
13
  import { EventEmitter } from "events";
13
14
  import { pathToFileURL } from "url";
14
15
  import { safeJsonParse } from "../lib/safe-json.js";
16
+ import { EventRuntimeProducer } from "../lib/event-runtime-producer.js";
15
17
 
16
18
  /**
17
19
  * Injectable dependencies — overridable from tests.
18
20
  * `fetch` defaults to the global fetch (Node 18+).
19
21
  */
20
22
  export const _deps = {
21
- spawn,
23
+ spawn: executionBroker.spawn.bind(executionBroker),
22
24
  fetch: (...args) => globalThis.fetch(...args),
23
25
  // Backoff sleep seam (tests override with a no-op so retries don't wait).
24
26
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
@@ -166,6 +168,178 @@ export class MCPClient extends EventEmitter {
166
168
  options && Array.isArray(options.roots) && options.roots.length > 0
167
169
  ? options.roots.map(String)
168
170
  : null;
171
+ // MCP elicitation (server -> client `elicitation/create`). The handler is
172
+ // intentionally injected by the host (REPL, headless, or Desktop bridge)
173
+ // so the transport layer never reads stdin or invents an approval policy.
174
+ this._elicitationHandler =
175
+ typeof options.elicitationHandler === "function"
176
+ ? options.elicitationHandler
177
+ : null;
178
+ this._elicitationHandlers = new Map();
179
+ this._elicitationContext = new AsyncLocalStorage();
180
+ this._elicitationTimeoutMs =
181
+ Number(options.elicitationTimeoutMs) > 0
182
+ ? Number(options.elicitationTimeoutMs)
183
+ : 180000;
184
+ this._pendingElicitations = new Map();
185
+ this._runtimeProducer = options.eventRuntimeStore
186
+ ? new EventRuntimeProducer({ store: options.eventRuntimeStore, emitter: this })
187
+ : null;
188
+ }
189
+
190
+ /** Install or clear the host-side MCP elicitation resolver. */
191
+ setElicitationHandler(handler, options = {}) {
192
+ const sessionId = options.sessionId;
193
+ if (sessionId != null && sessionId !== "") {
194
+ const key = String(sessionId);
195
+ if (typeof handler === "function") this._elicitationHandlers.set(key, handler);
196
+ else this._elicitationHandlers.delete(key);
197
+ } else {
198
+ this._elicitationHandler =
199
+ typeof handler === "function" ? handler : null;
200
+ }
201
+ if (Number(options.timeoutMs) > 0) {
202
+ this._elicitationTimeoutMs = Number(options.timeoutMs);
203
+ }
204
+ }
205
+
206
+ /** Run an async agent turn with a session-scoped elicitation route. */
207
+ withElicitationContext(sessionId, fn) {
208
+ if (typeof fn !== "function") return Promise.resolve(undefined);
209
+ return this._elicitationContext.run(
210
+ sessionId == null ? null : String(sessionId),
211
+ fn,
212
+ );
213
+ }
214
+
215
+ /** Remove a session route when a WS session is closed. */
216
+ clearElicitationHandler(sessionId) {
217
+ if (sessionId == null || sessionId === "") return false;
218
+ return this._elicitationHandlers.delete(String(sessionId));
219
+ }
220
+
221
+ /** Resolve a pending server elicitation by its server/request id pair. */
222
+ respondElicitation(serverName, requestId, response) {
223
+ const key = this._elicitationKey(serverName, requestId);
224
+ const pending = this._pendingElicitations.get(key);
225
+ if (!pending) return false;
226
+ pending.resolve(this._normalizeElicitationResponse(response));
227
+ try {
228
+ this._runtimeProducer?.store.acknowledgeInbox(
229
+ `mcp-elicitation:${this._elicitationKey(serverName, requestId)}`,
230
+ { response: this._normalizeElicitationResponse(response) },
231
+ );
232
+ } catch {}
233
+ return true;
234
+ }
235
+
236
+ /** Cancel a pending server elicitation. */
237
+ cancelElicitation(serverName, requestId) {
238
+ return this.respondElicitation(serverName, requestId, {
239
+ action: "cancel",
240
+ });
241
+ }
242
+
243
+ _elicitationKey(serverName, requestId) {
244
+ return `${String(serverName)}:${String(requestId)}`;
245
+ }
246
+
247
+ _normalizeElicitationResponse(response) {
248
+ const action = String(response?.action || "decline").toLowerCase();
249
+ const safeAction = new Set(["accept", "decline", "cancel"]).has(action)
250
+ ? action
251
+ : "decline";
252
+ const normalized = { action: safeAction };
253
+ if (safeAction === "accept" && response?.content !== undefined) {
254
+ normalized.content = response.content;
255
+ }
256
+ return normalized;
257
+ }
258
+
259
+ async _resolveElicitation(serverName, requestId, params = {}) {
260
+ const key = this._elicitationKey(serverName, requestId);
261
+ try {
262
+ this._runtimeProducer?.publish(
263
+ { type: "mcp_elicitation", server: serverName, requestId, params },
264
+ { origin: "mcp", id: `mcp-elicitation:${key}` },
265
+ );
266
+ } catch (error) {
267
+ this.emit("elicitation-error", { server: serverName, requestId, error });
268
+ return { action: "cancel" };
269
+ }
270
+ const contextSessionId = this._elicitationContext.getStore();
271
+ const handler =
272
+ (contextSessionId && this._elicitationHandlers.get(contextSessionId)) ||
273
+ this._elicitationHandler;
274
+ if (handler) {
275
+ try {
276
+ const direct = await handler({
277
+ server: serverName,
278
+ requestId,
279
+ ...params,
280
+ });
281
+ if (direct !== undefined) {
282
+ const response = this._normalizeElicitationResponse(direct);
283
+ try {
284
+ this._runtimeProducer?.store.acknowledgeInbox(
285
+ `mcp-elicitation:${key}`,
286
+ { response },
287
+ );
288
+ } catch {}
289
+ return response;
290
+ }
291
+ } catch (error) {
292
+ this.emit("elicitation-error", {
293
+ server: serverName,
294
+ requestId,
295
+ error,
296
+ });
297
+ return { action: "cancel" };
298
+ }
299
+ }
300
+
301
+ // An event-driven host may answer later through respondElicitation().
302
+ // No handler/listener means fail closed with decline rather than leaving a
303
+ // server request hanging indefinitely.
304
+ if (this.listenerCount("elicitation-request") === 0) {
305
+ try {
306
+ this._runtimeProducer?.store.acknowledgeInbox(
307
+ `mcp-elicitation:${key}`,
308
+ { response: { action: "decline" } },
309
+ );
310
+ } catch {}
311
+ return { action: "decline" };
312
+ }
313
+ return new Promise((resolve) => {
314
+ const timeout = setTimeout(() => {
315
+ this._pendingElicitations.delete(key);
316
+ this.emit("elicitation-timeout", { server: serverName, requestId });
317
+ try {
318
+ this._runtimeProducer?.store.fail(
319
+ "inbox",
320
+ `mcp-elicitation:${key}`,
321
+ "elicitation timeout",
322
+ { retryDelayMs: 0, maxAttempts: 1 },
323
+ );
324
+ } catch {}
325
+ resolve({ action: "cancel" });
326
+ }, this._elicitationTimeoutMs);
327
+ timeout.unref?.();
328
+ this._pendingElicitations.set(key, {
329
+ resolve: (response) => {
330
+ clearTimeout(timeout);
331
+ this._pendingElicitations.delete(key);
332
+ resolve(this._normalizeElicitationResponse(response));
333
+ },
334
+ });
335
+ this.emit("elicitation-request", {
336
+ server: serverName,
337
+ requestId,
338
+ ...params,
339
+ respond: (response) => this.respondElicitation(serverName, requestId, response),
340
+ cancel: () => this.cancelElicitation(serverName, requestId),
341
+ });
342
+ });
169
343
  }
170
344
 
171
345
  /**
@@ -320,6 +494,13 @@ export class MCPClient extends EventEmitter {
320
494
  ...this._agentIdentityEnv(),
321
495
  ...(config.env || {}),
322
496
  },
497
+ origin: config.origin || `mcp:server:${name}`,
498
+ policy: config.policy || "allow",
499
+ scope: config.scope || "mcp",
500
+ shell: false,
501
+ pluginId: config.pluginId,
502
+ pluginVersion: config.pluginVersion,
503
+ pluginSource: config.pluginSource,
323
504
  });
324
505
 
325
506
  entry.process = proc;
@@ -510,6 +691,12 @@ export class MCPClient extends EventEmitter {
510
691
  const entry = this.servers.get(name);
511
692
  if (!entry) return false;
512
693
 
694
+ for (const [key, pending] of this._pendingElicitations) {
695
+ if (key.startsWith(`${String(name)}:`)) {
696
+ pending.resolve({ action: "cancel" });
697
+ }
698
+ }
699
+
513
700
  if (entry.process) {
514
701
  entry.process.kill();
515
702
  }
@@ -1125,6 +1312,12 @@ export class MCPClient extends EventEmitter {
1125
1312
  this._sendResponse(serverName, id, {});
1126
1313
  return;
1127
1314
  }
1315
+ if (method === "elicitation/create") {
1316
+ this._resolveElicitation(serverName, id, msg.params || {})
1317
+ .then((response) => this._sendResponse(serverName, id, response))
1318
+ .catch(() => this._sendResponse(serverName, id, { action: "cancel" }));
1319
+ return;
1320
+ }
1128
1321
  this._sendResponse(serverName, id, undefined, {
1129
1322
  code: -32601,
1130
1323
  message: `method not found: ${method}`,
@@ -7,7 +7,6 @@
7
7
  * Feature-flag gated: WORKTREE_ISOLATION
8
8
  */
9
9
 
10
- import { execSync } from "node:child_process";
11
10
  import { existsSync, rmSync, symlinkSync, mkdirSync } from "node:fs";
12
11
  import { resolve, dirname } from "node:path";
13
12
  import {
@@ -93,10 +92,10 @@ export function removeWorktree(repoDir, worktreePath, options = {}) {
93
92
  let branch = null;
94
93
  if (deleteBranch) {
95
94
  try {
96
- branch = execSync("git rev-parse --abbrev-ref HEAD", {
97
- cwd: worktreePath,
98
- encoding: "utf-8",
99
- }).trim();
95
+ branch = gitExecArgs(
96
+ ["rev-parse", "--abbrev-ref", "HEAD"],
97
+ worktreePath,
98
+ ).trim();
100
99
  } catch (_e) {
101
100
  // Can't determine branch, skip branch deletion.
102
101
  }
@@ -215,11 +214,7 @@ function _worktreeCleanupState(repoDir, wt) {
215
214
 
216
215
  // Dirty state: separate tracked modifications from untracked files.
217
216
  try {
218
- const porcelain = execSync("git status --porcelain", {
219
- cwd: wt.path,
220
- encoding: "utf-8",
221
- maxBuffer: 64 * 1024 * 1024,
222
- });
217
+ const porcelain = gitExecArgs(["status", "--porcelain"], wt.path);
223
218
  const lines = porcelain.split("\n").filter((l) => l.trim().length > 0);
224
219
  state.untracked = lines.some((l) => l.startsWith("??"));
225
220
  state.uncommitted = lines.some((l) => !l.startsWith("??"));
@@ -393,8 +388,16 @@ export function mergeWorktree(repoDir, branchName, options = {}) {
393
388
  };
394
389
  } catch (err) {
395
390
  const errMsg = err.message || String(err);
396
- if (errMsg.includes("CONFLICT") || errMsg.includes("conflict")) {
397
- const conflictPaths = _collectConflictFiles(repoDir);
391
+ // Git can report merge conflicts on stdout while execSync's Error.message
392
+ // contains only the failed command (and localized installations need not
393
+ // include the English word "conflict"). The unmerged index is the
394
+ // authoritative signal.
395
+ const conflictPaths = _collectConflictFiles(repoDir);
396
+ if (
397
+ conflictPaths.length > 0 ||
398
+ errMsg.includes("CONFLICT") ||
399
+ errMsg.includes("conflict")
400
+ ) {
398
401
  if (conflictPaths.length === 0) {
399
402
  conflictPaths.push(..._extractConflictPathsFromMessage(errMsg));
400
403
  }
@@ -690,15 +693,9 @@ function _fileStatus(repoDir, base, branch, filePath) {
690
693
 
691
694
  function _hasUncommittedChanges(worktreePath) {
692
695
  try {
693
- const output = execSync("git status --porcelain", {
694
- cwd: worktreePath,
695
- encoding: "utf-8",
696
- // A worktree with many dirty files produces >1 MB of porcelain output and
697
- // would ENOBUFS on the default cap — the catch below then reports "clean"
698
- // (fail-OPEN), which could let a caller discard a worktree full of
699
- // uncommitted work. Give it the same 64 MB headroom as gitExec.
700
- maxBuffer: 64 * 1024 * 1024,
701
- });
696
+ // gitExecArgs carries the shared 64 MB maxBuffer, avoiding a fail-open
697
+ // "clean" result when a large worktree exceeds Node's default output cap.
698
+ const output = gitExecArgs(["status", "--porcelain"], worktreePath);
702
699
  return output.trim().length > 0;
703
700
  } catch (_e) {
704
701
  return false;
@@ -91,6 +91,21 @@ describe("createVendorAdapterBridge (cli)", () => {
91
91
  expect(r.userId).toBe("userToken");
92
92
  });
93
93
 
94
+ it("attaches a validated session to the registry runtime adapter", async () => {
95
+ const runtimeAdapter = { setSession: vi.fn() };
96
+ const bridge = createVendorAdapterBridge({
97
+ specs: [fakeSpec("deepseek", async () => ({ ok: true }))],
98
+ runtimeAdapter,
99
+ _httpClientFactory: () => ({}),
100
+ });
101
+
102
+ await bridge.registerVendor("deepseek", { userToken: "abc" });
103
+
104
+ expect(runtimeAdapter.setSession).toHaveBeenCalledOnce();
105
+ expect(runtimeAdapter.setSession.mock.calls[0][0]).toBe("deepseek");
106
+ expect(runtimeAdapter.setSession.mock.calls[0][1].get("userToken")).toBe("abc");
107
+ });
108
+
94
109
  it("UNKNOWN_VENDOR for unrecognized", async () => {
95
110
  const bridge = createVendorAdapterBridge({
96
111
  specs: [fakeSpec("deepseek", async () => ({ ok: true }))],
@@ -281,6 +281,8 @@ export const ROLE_TOOL_WHITELIST = {
281
281
  "read_file",
282
282
  "write_file",
283
283
  "edit_file",
284
+ "delete_file",
285
+ "move_file",
284
286
  "run_shell",
285
287
  "search_files",
286
288
  "list_dir",
@@ -297,6 +299,8 @@ export const ROLE_TOOL_WHITELIST = {
297
299
  "read_file",
298
300
  "write_file",
299
301
  "edit_file",
302
+ "delete_file",
303
+ "move_file",
300
304
  "run_shell",
301
305
  "search_files",
302
306
  "list_dir",
@@ -13,6 +13,8 @@ export {
13
13
  getAgentToolDescriptors,
14
14
  getCachedPython,
15
15
  getEnvironmentInfo,
16
+ _environmentProcessDeps,
17
+ _agentToolProcessDeps,
16
18
  getBaseSystemPrompt,
17
19
  buildSystemPrompt,
18
20
  executeTool,
@@ -29,6 +31,9 @@ export {
29
31
  killBackgroundShellTask,
30
32
  killAllBackgroundShellTasks,
31
33
  killAllBackgroundShellTasksSync,
34
+ _backgroundProcessDeps,
35
+ _runBackgroundTaskkill,
36
+ _gitProcessDeps,
32
37
  reapIdleBackgroundShellTasks,
33
38
  _ingestSearchOutput,
34
39
  writeFileVerified,
@@ -11,6 +11,13 @@
11
11
 
12
12
  import { EventEmitter } from "node:events";
13
13
  import crypto from "node:crypto";
14
+ import { EventRuntimeProducer } from "./event-runtime-producer.js";
15
+ import { EventRuntimeStore } from "./event-runtime-store.js";
16
+ import executionBroker from "./process-execution-broker/index.js";
17
+
18
+ export const _deps = {
19
+ spawn: executionBroker.spawn.bind(executionBroker),
20
+ };
14
21
 
15
22
  /**
16
23
  * @typedef {Object} InteractionRequest
@@ -27,8 +34,11 @@ import crypto from "node:crypto";
27
34
  */
28
35
 
29
36
  class AgentIPCBus extends EventEmitter {
30
- constructor() {
37
+ constructor({ runtimeStore = null } = {}) {
31
38
  super();
39
+ this._runtimeProducer = runtimeStore
40
+ ? new EventRuntimeProducer({ store: runtimeStore, emitter: this })
41
+ : null;
32
42
  /** @type {Map<string, {req: InteractionRequest, resolve: Function, reject: Function, timer?: NodeJS.Timeout}>} */
33
43
  this._pendingRequests = new Map();
34
44
  /** @type {Map<string, Function>} resolvers by agentId */
@@ -58,6 +68,10 @@ class AgentIPCBus extends EventEmitter {
58
68
  this.emit("agent:unregistered", { agentId });
59
69
  }
60
70
 
71
+ isAgentRegistered(agentId) {
72
+ return this._agentResolvers.has(agentId);
73
+ }
74
+
61
75
  /**
62
76
  * Child agent calls this to request human interaction
63
77
  * @param {string} agentId
@@ -91,6 +105,19 @@ class AgentIPCBus extends EventEmitter {
91
105
  }, fullReq.timeoutMs);
92
106
 
93
107
  this._pendingRequests.set(requestId, { req: fullReq, resolve, reject, timer });
108
+ try {
109
+ this._runtimeProducer?.publish(
110
+ { type: "interaction_request", request: fullReq },
111
+ { origin: "agent-ipc", id: `interaction:${requestId}` },
112
+ );
113
+ } catch (error) {
114
+ // Durable publication is fail-closed for configured runtime mode: do
115
+ // not leave a request that cannot be recovered after a crash.
116
+ this._pendingRequests.delete(requestId);
117
+ clearTimeout(timer);
118
+ reject(error);
119
+ return;
120
+ }
94
121
  this.emit("request", fullReq);
95
122
  });
96
123
  }
@@ -106,6 +133,10 @@ class AgentIPCBus extends EventEmitter {
106
133
  if (entry.timer) clearTimeout(entry.timer);
107
134
  this._pendingRequests.delete(requestId);
108
135
  entry.resolve(response);
136
+ try {
137
+ const id = `interaction:${requestId}`;
138
+ this._runtimeProducer?.store.acknowledgeInbox(id, { response });
139
+ } catch {}
109
140
  this.emit("request:resolved", { requestId, response });
110
141
  return true;
111
142
  }
@@ -144,12 +175,12 @@ class AgentIPCBus extends EventEmitter {
144
175
  * @returns {Promise<{process: ChildProcess, agentId: string}>}
145
176
  */
146
177
  async spawnAgentProcess(command, args = [], options = {}) {
147
- const { spawn } = await import("node:child_process");
148
178
  const agentId = options.agentId || crypto.randomUUID();
149
179
  const heartbeatMs = options.heartbeatMs || 30000;
150
180
 
151
181
  return new Promise((resolve, reject) => {
152
- const child = spawn(command, args, {
182
+ const spawnOptions = options.spawnOptions || {};
183
+ const child = _deps.spawn(command, args, {
153
184
  stdio: ["pipe", "pipe", "pipe"],
154
185
  env: {
155
186
  ...process.env,
@@ -158,7 +189,11 @@ class AgentIPCBus extends EventEmitter {
158
189
  CHAINLESSCHAIN_IPC_PROTOCOL: "jsonrpc-stdio-v1",
159
190
  ...options.env,
160
191
  },
161
- ...options.spawnOptions,
192
+ ...spawnOptions,
193
+ origin: spawnOptions.origin || "agent-ipc:subagent",
194
+ policy: spawnOptions.policy || "allow",
195
+ scope: spawnOptions.scope || "agent-ipc",
196
+ shell: spawnOptions.shell === true,
162
197
  });
163
198
 
164
199
  let buffer = "";
@@ -326,6 +361,10 @@ class AgentIPCBus extends EventEmitter {
326
361
  }
327
362
 
328
363
  // Singleton
329
- const ipcBus = new AgentIPCBus();
364
+ const ipcBus = new AgentIPCBus(
365
+ process.env.CC_EVENT_RUNTIME_DURABLE === "1"
366
+ ? { runtimeStore: new EventRuntimeStore() }
367
+ : {},
368
+ );
330
369
  export default ipcBus;
331
370
  export { AgentIPCBus };
@@ -1,10 +1,12 @@
1
1
  /** OS-isolated shell execution for the coding agent. */
2
- import { spawnSync } from "node:child_process";
3
2
  import path from "node:path";
4
3
  import { proxyEnv } from "./sandbox-egress-proxy.js";
4
+ import executionBroker from "./process-execution-broker/index.js";
5
5
 
6
6
  export const DEFAULT_SANDBOX_IMAGE = "node:22-bookworm-slim";
7
- export const _deps = { spawnSync };
7
+ export const _deps = {
8
+ spawnSync: (...args) => executionBroker.spawnSync(...args),
9
+ };
8
10
 
9
11
  function stringList(value) {
10
12
  if (!Array.isArray(value)) return [];
@@ -126,6 +128,10 @@ export function executeSandboxedShell(command, sandbox, options = {}) {
126
128
  }
127
129
  args.push(sandbox.image, "sh", "-lc", String(command || ""));
128
130
  const result = _deps.spawnSync("docker", args, {
131
+ origin: "agent-sandbox:docker",
132
+ scope: "sandbox",
133
+ policy: "allow",
134
+ shell: false,
129
135
  encoding: "utf8",
130
136
  timeout: options.timeout,
131
137
  maxBuffer: options.maxBuffer,
@@ -189,6 +195,10 @@ function executeBubblewrapShell(command, sandbox, options, hostCwd, policy) {
189
195
  ? { ...(options.env || {}), ...proxyEnv(egress.port, "127.0.0.1") }
190
196
  : options.env;
191
197
  const result = _deps.spawnSync("bwrap", args, {
198
+ origin: "agent-sandbox:bubblewrap",
199
+ scope: "sandbox",
200
+ policy: "allow",
201
+ shell: false,
192
202
  cwd: hostCwd,
193
203
  env: bwrapEnv,
194
204
  encoding: "utf8",
@@ -244,6 +254,10 @@ export function probeSandboxAvailability(sandbox, deps = _deps) {
244
254
  ? ["bwrap", ["--version"]]
245
255
  : ["docker", ["version", "--format", "{{.Server.Version}}"]];
246
256
  const result = deps.spawnSync(probeArgs[0], probeArgs[1], {
257
+ origin: "agent-sandbox:probe",
258
+ scope: "sandbox",
259
+ policy: "allow",
260
+ shell: false,
247
261
  encoding: "utf8",
248
262
  timeout: 10000,
249
263
  windowsHide: true,
@@ -27,6 +27,7 @@ import { isEntryExpired, effectiveFireAt } from "./schedule-planner.js";
27
27
  import { SUBAGENT_PERMISSION_MODES } from "./subagent-contract.js";
28
28
  import { parseGoalCondition } from "./goal-condition-engine.js";
29
29
  import { normalizeActionClass } from "./unattended-action-policy.js";
30
+ import { withFileLock } from "./with-file-lock.js";
30
31
 
31
32
  export const SCHEDULE_KINDS = Object.freeze(["wakeup", "cron", "monitor"]);
32
33
 
@@ -155,6 +156,10 @@ function isSchedulableStatus(entry) {
155
156
  : entry.status === "active";
156
157
  }
157
158
 
159
+ function hasLiveExecutionLease(entry, now) {
160
+ return Number(entry.executionLease?.expiresAt) > Number(now);
161
+ }
162
+
158
163
  /** Statuses an entry can never leave — safe to prune. */
159
164
  export const TERMINAL_STATUSES = Object.freeze([
160
165
  "fired",
@@ -487,6 +492,7 @@ export class AgentScheduleStore {
487
492
  // (defense-in-depth: retireExpired need not have run first).
488
493
  if (isEntryExpired(entry, now)) return false;
489
494
  if (!isSchedulableStatus(entry)) return false;
495
+ if (hasLiveExecutionLease(entry, now)) return false;
490
496
  // The effective fire time applies the entry's OWN deterministic jitter
491
497
  // (`entry.jitterMs`, default 0 → base fire time, byte-identical to the
492
498
  // prior `dueAt`/`nextAt` comparison). A jittered entry fires slightly
@@ -496,6 +502,52 @@ export class AgentScheduleStore {
496
502
  });
497
503
  }
498
504
 
505
+ /**
506
+ * Atomically claim due entries for one agenda runner. The lease is retained
507
+ * on the durable entry, so a second process cannot fire the same item while
508
+ * the first is working; an abandoned process becomes reclaimable after the
509
+ * lease expires. This is deliberately separate from `due()` for backwards
510
+ * compatibility with read-only callers and injected test stores.
511
+ */
512
+ claimDue(atMs = null, leaseMs = 120000) {
513
+ const now = atMs != null ? Number(atMs) : this._now();
514
+ const ttl = Math.max(1000, Number(leaseMs) || 120000);
515
+ this._ensureDir();
516
+ return withFileLock(path.join(this.dir, ".agenda-claims"), () => {
517
+ const claimed = [];
518
+ for (const kind of SCHEDULE_KINDS) {
519
+ const entries = this._readAll(kind);
520
+ let changed = false;
521
+ for (const entry of entries) {
522
+ if (!isSchedulableStatus(entry) || isEntryExpired(entry, now)) continue;
523
+ if (hasLiveExecutionLease(entry, now)) continue;
524
+ const fireAt = effectiveFireAt(entry);
525
+ if (fireAt == null || fireAt > now) continue;
526
+ entry.executionLease = {
527
+ owner: `${process.pid}:${randomUUID()}`,
528
+ claimedAt: now,
529
+ expiresAt: now + ttl,
530
+ };
531
+ claimed.push({ ...entry, executionLease: { ...entry.executionLease } });
532
+ changed = true;
533
+ }
534
+ if (changed) this._writeAll(kind, entries);
535
+ }
536
+ return claimed;
537
+ }, { failIfUnavailable: true });
538
+ }
539
+
540
+ /** Release a failed/aborted claim so a later runner can retry it. */
541
+ releaseClaim(id) {
542
+ for (const kind of SCHEDULE_KINDS) {
543
+ const updated = this._mutate(kind, id, (entry) => {
544
+ delete entry.executionLease;
545
+ });
546
+ if (updated) return updated;
547
+ }
548
+ return null;
549
+ }
550
+
499
551
  /**
500
552
  * Retire every schedulable entry whose `expiresAt` has passed (status →
501
553
  * "expired", stamping `expiredAt`), across all kinds, so it never fires again.
@@ -632,6 +684,9 @@ export class AgentScheduleStore {
632
684
  const entry = entries.find((e) => e.id === id);
633
685
  if (!entry) return null;
634
686
  fn(entry);
687
+ // Every lifecycle mutation settles the claim before the write becomes
688
+ // visible, including a recurring cron/monitor entry that remains active.
689
+ delete entry.executionLease;
635
690
  this._writeAll(kind, entries);
636
691
  return entry;
637
692
  }
@@ -23,7 +23,12 @@ import {
23
23
  } from "../../harness/worktree-isolator.js";
24
24
  import { isGitRepo as _isGitRepo } from "../../lib/git-integration.js";
25
25
  import { normalizeSparsePaths } from "../../lib/worktree-sparse.js";
26
- import { execFileSync, spawn } from "node:child_process";
26
+ import executionBroker from "../../lib/process-execution-broker/index.js";
27
+
28
+ export const _processDeps = {
29
+ spawn: executionBroker.spawn.bind(executionBroker),
30
+ execFileSync: executionBroker.execFileSync.bind(executionBroker),
31
+ };
27
32
 
28
33
  /**
29
34
  * Resolve the sparse-checkout / dependency-symlink options for a task, letting a
@@ -49,7 +54,14 @@ function resolveWorktreeOptions(task, defaults) {
49
54
 
50
55
  function defaultRunShell(command, cwd) {
51
56
  return new Promise((resolve, reject) => {
52
- const child = spawn(command, { cwd, shell: true, env: process.env });
57
+ const child = _processDeps.spawn(command, [], {
58
+ cwd,
59
+ shell: true,
60
+ env: process.env,
61
+ origin: "team-worktree:task-command",
62
+ policy: "allow",
63
+ scope: "team-worktree",
64
+ });
53
65
  let err = "";
54
66
  child.stderr?.on("data", (d) => (err += d.toString("utf8")));
55
67
  child.on("error", (e) => reject(new Error(e.message)));
@@ -61,12 +73,22 @@ function defaultRunShell(command, cwd) {
61
73
  });
62
74
  }
63
75
 
76
+ function runTeamGit(args, cwd) {
77
+ return _processDeps.execFileSync("git", args, {
78
+ cwd,
79
+ stdio: "ignore",
80
+ origin: "team-worktree:commit",
81
+ policy: "allow",
82
+ scope: "team-worktree",
83
+ shell: false,
84
+ });
85
+ }
86
+
64
87
  /** Stage + commit everything in a worktree. Returns true if a commit was made. */
65
88
  function defaultCommit(worktreePath, message) {
66
- execFileSync("git", ["add", "-A"], { cwd: worktreePath, stdio: "ignore" });
89
+ runTeamGit(["add", "-A"], worktreePath);
67
90
  try {
68
- execFileSync(
69
- "git",
91
+ runTeamGit(
70
92
  [
71
93
  "-c",
72
94
  "user.email=team@chainlesschain.local",
@@ -76,7 +98,7 @@ function defaultCommit(worktreePath, message) {
76
98
  "-m",
77
99
  message,
78
100
  ],
79
- { cwd: worktreePath, stdio: "ignore" },
101
+ worktreePath,
80
102
  );
81
103
  return true;
82
104
  } catch {