chainlesschain 0.163.8 → 0.165.1

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 (240) hide show
  1. package/package.json +7 -4
  2. package/scripts/macos-mcp-launcher-build.mjs +317 -0
  3. package/scripts/macos-mcp-launcher-postinstall.sh +54 -0
  4. package/scripts/macos-mcp-launcher-preinstall.sh +61 -0
  5. package/src/assets/web-panel/.build-hash +1 -1
  6. package/src/assets/web-panel/assets/{AIOps-CavM7W3k.js → AIOps-CYxFDrcm.js} +1 -1
  7. package/src/assets/web-panel/assets/{ActionButton-CUvZGabQ.js → ActionButton-CPBtdiU2.js} +1 -1
  8. package/src/assets/web-panel/assets/{Analytics-DeBnWnbe.js → Analytics-Dayq7Dp7.js} +3 -3
  9. package/src/assets/web-panel/assets/{AppLayout-DbI5kxvd.js → AppLayout-6U7bPb2s.js} +4 -4
  10. package/src/assets/web-panel/assets/{Artifacts-D93bigKm.js → Artifacts-D2Iqg_3D.js} +1 -1
  11. package/src/assets/web-panel/assets/{Audit-CBNvfUZa.js → Audit-CdoOB7WV.js} +1 -1
  12. package/src/assets/web-panel/assets/{BackgroundAgents-CJLyh_YU.js → BackgroundAgents-DrnAVB8w.js} +1 -1
  13. package/src/assets/web-panel/assets/{Backup-D_0yoehD.js → Backup-CesMi5cy.js} +1 -1
  14. package/src/assets/web-panel/assets/{BaseInput-TJxmbObW.js → BaseInput-DaX9ptax.js} +1 -1
  15. package/src/assets/web-panel/assets/{Chat-CE0dto1q.js → Chat-OrwOPxDS.js} +5 -5
  16. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CZ5-yYiD.js → ChatBubbleRenderer-CvPZFRxM.js} +1 -1
  17. package/src/assets/web-panel/assets/{Checkbox-Ck1RVIVo.js → Checkbox-DiHbcysC.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-qpoyf6DK.js → Codegen-CbgCkaBJ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-DUyPWjIE.js → Col-Du5hgrh4.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-D_S1BemS.js → Community-DsPOBW-p.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-BjxeK-Gc.js → Compact-BwHIFkNC.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-Dd3CEJsu.js → Compliance-BvrDQSgd.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-B4QG7-ad.js → Cowork-gwAE6arx.js} +3 -3
  24. package/src/assets/web-panel/assets/{Cron-BtZnids5.js → Cron-DW-0l4uF.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-CHPfCBFD.js → Crosschain-j-NsLesB.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-DT8DCEOK.js → DID-CibVEBOI.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-COQab5_U.js → Dashboard-W4z9vSfe.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-BY1PsQuU.js → Dropdown-DAcrkKxG.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-CsuAuaq3.js → EmailListRenderer-6GXah-89.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BqpinD-j.js → FamilyGuardDashboard-TKDCq8Ku.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-D0gkfQFv.js → Federation-BXBLlcNm.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-BqgE-axK.js → FormItemContext-BoJJBkG3.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-D0hJ2KvC.js → GenericCardRenderer-okjsl46v.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-CmMB0uPY.js → Git-Rvs7i6nn.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-BnVEI0Dk.js → Governance-CHSUxXXS.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-YTAMihRP.js → Inference-V3BlfxsB.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-0ax7VHop.js → KnowledgeGraph-C_NlD4aR.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-B9TRcokL.js → Logs-Nn4LNxhA.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-SVAgNnkt.js +1 -0
  40. package/src/assets/web-panel/assets/{Marketplace-B6CSuD_H.js → Marketplace-DEmjFcg-.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-CjMXvOVU.js → McpTools-DlopXipI.js} +3 -3
  42. package/src/assets/web-panel/assets/{Memory-CXRF71U3.js → Memory-BG3ynX60.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-DQgNp_oS.js → MobileBridge-DeRzWOfT.js} +1 -1
  44. package/src/assets/web-panel/assets/{MobileProjects-DlPwcCbq.js → MobileProjects-Tyd3o2A0.js} +1 -1
  45. package/src/assets/web-panel/assets/{Mtc-6hDTRPe1.js → Mtc-BoPbOv1C.js} +2 -2
  46. package/src/assets/web-panel/assets/{MtcAudit-BlVHU1DS.js → MtcAudit-un-rTHp7.js} +2 -2
  47. package/src/assets/web-panel/assets/{Multisig-DP9pzEbV.js → Multisig-Ds0oveEB.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-Djorh8EV.js → NLProgramming-D09JI-yY.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-oLZ-JkUg.js → Notes-DTRhiKJn.js} +4 -4
  50. package/src/assets/web-panel/assets/{NotificationSettings-Cg288VH3.js → NotificationSettings-BAnnNCim.js} +1 -1
  51. package/src/assets/web-panel/assets/OrderTableRenderer-Yr-1fSYi.js +1 -0
  52. package/src/assets/web-panel/assets/{Organization-CBdo-bHs.js → Organization-S9EAolpj.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-qZdnVumx.js → Overflow-CqcLKqJ8.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-D4kNfGNQ.js → P2P-Dw0F9EcU.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-DnlMemM7.js → PdhVaultBrowser-BDRwUevN.js} +5 -5
  56. package/src/assets/web-panel/assets/{Permissions-DHucHFBJ.js → Permissions-D3_c3nKB.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-BovlOIPR.js → PersonalDataHub-CxdMV98x.js} +4 -4
  58. package/src/assets/web-panel/assets/{Pipeline-Deru3CQY.js → Pipeline-CgjNQFLI.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-Bq5oTTbU.js → Privacy-BHghIIht.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-BfzxVWAz.js → ProjectInit-De6pqaKM.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-CKC6XBtH.js → ProjectSettings-Bg4ZkNYQ.js} +2 -2
  62. package/src/assets/web-panel/assets/Projects-CP8vPvzs.js +1 -0
  63. package/src/assets/web-panel/assets/{Providers-C_jx4TWX.js → Providers-CUnHbNf-.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-BdIHeVGC.js → QrScannerModal-DA2f3Wts.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-BwZwAcPD.js → QuickAsk-C1FeHDqp.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-BNTt0L-m.js → Recommend-B5guaUMN.js} +1 -1
  67. package/src/assets/web-panel/assets/RemoteSession-PDADSds3.js +5 -0
  68. package/src/assets/web-panel/assets/{Reputation-D6VpG32B.js → Reputation-eTlRhgvK.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-D_74OvQQ.js → Row-D7AqSGY0.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-BTr0a-BQ.js → RssFeed-JiuLm4Ja.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-C3MCFoNU.js → Search-CwruahW3.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-CqW9aJ6s.js → Security-CdhqaR0b.js} +4 -4
  73. package/src/assets/web-panel/assets/{Services-Cw5GNaHF.js → Services-CU0ZIlcT.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-DZps5qeY.js → Skeleton-CtduPfAb.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-C6NQ1znT.js → Skills-CROXm6AY.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-DFP8G2Sh.js → Sla-DiZiABFD.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-ESt6MB6v.js → SpeechSettings-B5pLcXUw.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-BHnN98aM.js → SyncSettings-DBX6J1Oe.js} +2 -2
  79. package/src/assets/web-panel/assets/{Tasks-NiSCaOf-.js → Tasks-BLyRJd3-.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-DsKFhW2C.js → Templates-BKa5qzmk.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-CQ0E0ze5.js → Tenant-DnD3a4M6.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-D_rBu_by.js → Terminal-DEitQeTb.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-D3Vm4Po4.js → TimelineRenderer-CjHggTDq.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-DNTxRH6q.js → Tokens-CsGT7sfl.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-Dgprz1ms.js → Trigger-yr4rFDrW.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-B2rUnVBD.js → Trust-CLvu1Sjp.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-DEGQNc6_.js → UkeySign-B0m7yUP8.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-jlsAx3Eh.js → VideoEditing-C0VvP4lc.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-8jQih1ij.js → Wallet-kllBAG1M.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-XLeO6xIB.js → WebAuthn-C54UQNLx.js} +3 -3
  91. package/src/assets/web-panel/assets/{WorkflowEditor-De_dgDAG.js → WorkflowEditor-B7y8qSvU.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-CI19R_Sk.js → chat-DRzTh8CJ.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-DsYTOYN3.js → colors-BSMwSnCx.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-CM3-t_Qr.js → compact-item-BTg602H5.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-BjxlBRZv.js → createContext-B6EGsdvp.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-Dol8jLEP.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-RDaWX_4i.js → hasIn-DDKqJ8RD.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-nAH87mQP.js → index-323Hhxxt.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DIUnNOjG.js → index-BICn3Qh1.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DyIWssPC.js → index-BMuGd2--.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DX7tuWVJ.js → index-BRqt4fO-.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cd3euP_4.js → index-BVTAl5N2.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-Cpqm1qfU.js → index-BsR7Y2NG.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DcPFB8qJ.js → index-Bx044qDj.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Cd9OJhHG.js → index-C1GFfuqN.js} +1 -1
  106. package/src/assets/web-panel/assets/index-C3QX5tOl.js +1 -0
  107. package/src/assets/web-panel/assets/{index-B-Z8eFUJ.js → index-C66NpWy5.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BlT2VAEK.js → index-CDTvsX8t.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Dsij_KsN.js → index-CGNklbP6.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-C4gkzxkw.js → index-CHfLYwcW.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-D6sm5-Xq.js → index-CNxg61YP.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-C0yx-jtQ.js → index-CQlHFN_g.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CjTrRt13.js → index-CUEU48o1.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-B7WBt_4T.js → index-CWGax2QW.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-IeiL9RKc.js → index-C_JzIXUq.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CnHH7bnD.js → index-Cf23wVMs.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BKJr-TZp.js → index-CmAInUkX.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CiECJi-Q.js → index-CmAubjDi.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BCnZyIMA.js → index-Co7oM1qr.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Drlnwr8F.js → index-CwPUZbMI.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-9R6BxAr0.js → index-D1naNj4U.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BGqqJfL0.js → index-D5CsBShd.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Bi46Xu4u.js → index-DBT3YeQ9.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DXXtGMUJ.js → index-DLdrHhML.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-B0PMlbMs.js → index-Dc77VR3H.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D0zcy54x.js → index-Dfv2lKu3.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BN6UPD2E.js → index-DiW0sDkz.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Dxge-xWl.js → index-DpTHIRfZ.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CyOGN6a5.js → index-DqFXGzzC.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-B9O9YyWE.js → index-XFe-1twF.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BHNYPO0b.js → index-p2HYn89m.js} +3 -3
  132. package/src/assets/web-panel/assets/{index-CVQMeiwH.js → index-pqfux8vZ.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-pPL66J85.js → index-rjzk9z1Q.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-DoQA0-52.js → index-th2ihDc0.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-B5REnKt8.js → index-u2YVld9W.js} +1 -1
  136. package/src/assets/web-panel/assets/index-xYybvEic.js +1 -0
  137. package/src/assets/web-panel/assets/{initDefaultProps-BTL88of4.js → initDefaultProps-C3Djh9fa.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-BUB2pXwB.js → motion-BzqFmbSe.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-egCkkOMm.js → move-DJo_FFaW.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-DAWhZC8c.js → mtc-parser-_tlTiBvk.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-CvOxe_6d.js → omit-CB2ZyxUg.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-DZUSLZ5Z.js → pickAttrs-DZwIFD4u.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-BERxGNMY.js → placementArrow-DxZY0dgk.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-DZuDfZ9n.js → responsiveObserve-CzzGt7GL.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-DO756sgI.js → slide-BaIK52yz.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-BRQlIz28.js → statusUtils-3-FHu9_M.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-DCSfdybl.js → styleChecker-BkRT68fK.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-LD8dWVve.js → useFlexGapSupport-DHYwKS-s.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-BX9uW2LK.js → useFs-DVVUhV8p.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-BPSlPuHU.js → usePersonalDataHub-CkrFKAzQ.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-BmOILC9V.js → vnode-BuBLFz0c.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-1iKowKGg.js → zoom-kpm6LtXU.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/command-help-index.json +3 -3
  155. package/src/commands/cowork.js +418 -41
  156. package/src/commands/permissions.js +240 -16
  157. package/src/commands/plugin.js +1293 -56
  158. package/src/commands/session-location.js +276 -0
  159. package/src/commands/session.js +2 -0
  160. package/src/data/changelog.json +28 -2
  161. package/src/gateways/ws/action-protocol.js +316 -17
  162. package/src/gateways/ws/message-dispatcher.js +30 -0
  163. package/src/gateways/ws/remote-session-protocol.js +565 -35
  164. package/src/gateways/ws/ws-agent-handler.js +12 -0
  165. package/src/gateways/ws/ws-approval-gate.js +5 -0
  166. package/src/gateways/ws/ws-server.js +510 -50
  167. package/src/harness/jsonl-session-store.js +341 -24
  168. package/src/harness/mcp-client.js +8 -1
  169. package/src/harness/remote-session-audit.js +2 -0
  170. package/src/harness/remote-session-crypto.js +929 -21
  171. package/src/harness/remote-session-registry.js +945 -24
  172. package/src/harness/session-list-index.js +233 -14
  173. package/src/lib/agent-authority.js +6 -0
  174. package/src/lib/auto-mode-config.js +98 -3
  175. package/src/lib/background-agent-keeper-client.js +248 -0
  176. package/src/lib/background-agent-keeper-protocol.js +291 -0
  177. package/src/lib/background-agent-supervisor.js +1357 -85
  178. package/src/lib/background-interaction-resolver.js +12 -0
  179. package/src/lib/background-session-transport.js +23 -6
  180. package/src/lib/background-turn-bootstrap-protocol.js +190 -0
  181. package/src/lib/cowork-workflow.js +1188 -76
  182. package/src/lib/delivery-production-adapter.js +38 -2
  183. package/src/lib/dynamic-workflow-facade.js +1736 -0
  184. package/src/lib/execution-location-contract.js +551 -0
  185. package/src/lib/execution-location-runtime.js +130 -0
  186. package/src/lib/ide-context.js +94 -6
  187. package/src/lib/mcp-stdio-package-materialization.js +166 -14
  188. package/src/lib/packer/pkg-config-generator.js +2 -0
  189. package/src/lib/permission-authority.js +77 -0
  190. package/src/lib/permission-side-effect-center.js +495 -0
  191. package/src/lib/plugin-runtime/install.js +514 -13
  192. package/src/lib/plugin-runtime/marketplace-artifact-readback.js +900 -0
  193. package/src/lib/plugin-runtime/marketplace-catalog.js +1181 -0
  194. package/src/lib/plugin-runtime/marketplace-impact.js +408 -0
  195. package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1567 -0
  196. package/src/lib/plugin-security.js +60 -3
  197. package/src/lib/process-execution-broker/index.js +1095 -57
  198. package/src/lib/process-execution-broker/linux-bwrap-descriptor-launch.js +375 -0
  199. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +90 -16
  200. package/src/lib/process-execution-broker/linux-generic-bwrap.js +159 -18
  201. package/src/lib/process-execution-broker/macos-mcp-launcher-contract.js +278 -0
  202. package/src/lib/process-execution-broker/macos-mcp-launcher-protocol.json +23 -0
  203. package/src/lib/process-execution-broker/macos-mcp-launcher.c +1497 -0
  204. package/src/lib/process-execution-broker/mcp-fd-entry-bootstrap.js +37 -0
  205. package/src/lib/process-execution-broker/platform-sandbox.js +2330 -279
  206. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  207. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  208. package/src/lib/process-execution-broker/windows-sandbox.cs +50 -4
  209. package/src/lib/public-ide-capability-manifest.js +3 -0
  210. package/src/lib/remote-approval-bridge.js +777 -34
  211. package/src/lib/remote-control.js +2 -0
  212. package/src/lib/remote-membership-authority.js +632 -0
  213. package/src/lib/remote-membership-coordinator.js +2973 -0
  214. package/src/lib/remote-membership-host-store.js +1799 -0
  215. package/src/lib/runtime-provenance-ledger.js +171 -46
  216. package/src/lib/scoped-permission-store.js +369 -0
  217. package/src/lib/session-anti-rollback-anchor.js +262 -5
  218. package/src/lib/shell-approval.js +169 -6
  219. package/src/lib/sub-agent-context.js +4 -0
  220. package/src/lib/with-file-lock.js +73 -38
  221. package/src/lib/workflow-definition-contract.js +149 -0
  222. package/src/repl/agent-repl.js +111 -36
  223. package/src/repl/chat-repl.js +2 -0
  224. package/src/repl/clipboard-image.js +735 -16
  225. package/src/repl/ide-status.js +1 -1
  226. package/src/repl/prompt-interactions.js +59 -6
  227. package/src/repl/remote-approval.js +29 -7
  228. package/src/runtime/agent-core.js +1182 -155
  229. package/src/runtime/headless-runner.js +52 -30
  230. package/src/runtime/headless-stream.js +51 -28
  231. package/src/workers/background-agent-keeper.js +689 -0
  232. package/src/workers/background-agent-worker.js +365 -13
  233. package/src/workers/background-turn-bootstrap.js +170 -0
  234. package/src/assets/web-panel/assets/MarkdownRenderer-yySN5YNM.js +0 -1
  235. package/src/assets/web-panel/assets/OrderTableRenderer-Bb1O3Ofr.js +0 -1
  236. package/src/assets/web-panel/assets/Projects-CMolGhae.js +0 -1
  237. package/src/assets/web-panel/assets/RemoteSession-Biyn2-Ro.js +0 -5
  238. package/src/assets/web-panel/assets/devWarning-S1ld2LwU.js +0 -1
  239. package/src/assets/web-panel/assets/index-Drz6yq0m.js +0 -1
  240. package/src/assets/web-panel/assets/index-uWiUs7S_.js +0 -1
@@ -0,0 +1,689 @@
1
+ /**
2
+ * Independent lifetime keeper for one detached background-agent worker.
3
+ *
4
+ * The launcher starts this process as a sibling of the worker. The worker must
5
+ * arm an exact turn identity over the private local channel and receive the
6
+ * keeper's durable acknowledgement before releasing Agent main. If the worker
7
+ * disappears, this process remains outside the worker's process group / Job
8
+ * and retires the armed process trees before exiting.
9
+ */
10
+
11
+ import net from "node:net";
12
+ import { readFileSync, unlinkSync, writeSync } from "node:fs";
13
+ import { resolve } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ import { createNdjsonReader } from "../lib/background-session-transport.js";
16
+ import {
17
+ isBackgroundProcessTreeExecutionAlive,
18
+ isProcessAlive,
19
+ isSameProcess,
20
+ mutateBackgroundAgentState,
21
+ readBackgroundAgentState,
22
+ removeJobFile,
23
+ stopBackgroundAgentChildTree,
24
+ } from "../lib/background-agent-supervisor.js";
25
+ import {
26
+ BACKGROUND_AGENT_KEEPER_ARM,
27
+ BACKGROUND_AGENT_KEEPER_ARMED,
28
+ BACKGROUND_AGENT_KEEPER_CLEANUP_CONFIRM_TIMEOUT_MS,
29
+ BACKGROUND_AGENT_KEEPER_HELLO,
30
+ BACKGROUND_AGENT_KEEPER_PERSIST_RETRY_TIMEOUT_MS,
31
+ BACKGROUND_AGENT_KEEPER_PROTOCOL_VERSION,
32
+ BACKGROUND_AGENT_KEEPER_READY,
33
+ BACKGROUND_AGENT_KEEPER_RETIRE,
34
+ BACKGROUND_AGENT_KEEPER_RETIRED,
35
+ BACKGROUND_AGENT_KEEPER_STATE_LOCK_TIMEOUT_MS,
36
+ cleanupBackgroundAgentKeeperPipeDirectory,
37
+ createBackgroundAgentKeeperMessage,
38
+ normalizeBackgroundAgentKeeperHello,
39
+ normalizeBackgroundAgentKeeperTurn,
40
+ sameBackgroundAgentKeeperTurn,
41
+ } from "../lib/background-agent-keeper-protocol.js";
42
+
43
+ const STARTUP_TIMEOUT_MS = 30_000;
44
+ const HEARTBEAT_INTERVAL_MS = 1_000;
45
+ const KEEPER_PERSIST_RETRY_DELAY_MS = 50;
46
+
47
+ function delay(ms) {
48
+ return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
49
+ }
50
+
51
+ /**
52
+ * Retry a cleanup-critical state mutation after a worker is hard-killed.
53
+ *
54
+ * A worker can disappear while it owns the cross-process state lock. Windows
55
+ * may keep that terminated PID observable briefly, so the strict lock cannot
56
+ * immediately prove the owner dead and correctly fails closed. The keeper is
57
+ * the only remaining owner of the armed turn; it must wait for that transient
58
+ * fence instead of turning a lock exception into an unhandled rejection and
59
+ * exiting with the durable projection still `armed`.
60
+ */
61
+ export async function retryKeeperPersistence(operation, options = {}) {
62
+ const now = options.now || Date.now;
63
+ const sleep = options.sleep || delay;
64
+ const timeoutMs = Math.max(
65
+ 0,
66
+ Number(
67
+ options.timeoutMs ?? BACKGROUND_AGENT_KEEPER_PERSIST_RETRY_TIMEOUT_MS,
68
+ ),
69
+ );
70
+ const retryDelayMs = Math.max(
71
+ 1,
72
+ Number(options.retryDelayMs ?? KEEPER_PERSIST_RETRY_DELAY_MS),
73
+ );
74
+ const deadline = now() + timeoutMs;
75
+ let attempts = 0;
76
+ let error = null;
77
+ for (;;) {
78
+ attempts += 1;
79
+ try {
80
+ return { result: operation(), error: null, attempts };
81
+ } catch (cause) {
82
+ error = cause;
83
+ }
84
+ const remaining = deadline - now();
85
+ if (remaining <= 0) return { result: null, error, attempts };
86
+ await sleep(Math.min(retryDelayMs, remaining));
87
+ }
88
+ }
89
+
90
+ function writeMessage(socket, message) {
91
+ if (!socket || socket.destroyed) return false;
92
+ try {
93
+ socket.write(`${JSON.stringify(message)}\n`);
94
+ return true;
95
+ } catch {
96
+ return false;
97
+ }
98
+ }
99
+
100
+ export function keeperWorkerIdentityAlive(
101
+ workerPid,
102
+ workerStartedAt,
103
+ probe = isSameProcess,
104
+ ) {
105
+ return probe(Number(workerPid), Number(workerStartedAt));
106
+ }
107
+
108
+ function reportKeeperPersistenceFailure(stage, error) {
109
+ try {
110
+ writeSync(
111
+ 2,
112
+ `[background-agent-keeper] ${stage} persistence failed: ${error?.stack || error?.message || String(error)}\n`,
113
+ );
114
+ } catch {
115
+ // The inherited diagnostic handle may already be closed.
116
+ }
117
+ }
118
+
119
+ export function runBackgroundAgentKeeperHeartbeat({
120
+ finishing,
121
+ workerSocket,
122
+ authenticatedHello,
123
+ authenticatedWorkerStartedAt,
124
+ armedTurn,
125
+ persistKeeper,
126
+ finishForWorkerDisconnect,
127
+ now = Date.now,
128
+ workerIdentityAlive = keeperWorkerIdentityAlive,
129
+ reportPersistenceFailure = reportKeeperPersistenceFailure,
130
+ }) {
131
+ if (finishing) return false;
132
+ if (
133
+ workerSocket &&
134
+ authenticatedHello &&
135
+ armedTurn &&
136
+ !workerIdentityAlive(
137
+ authenticatedHello.workerPid,
138
+ authenticatedWorkerStartedAt,
139
+ )
140
+ ) {
141
+ // A Windows named-pipe handle can remain open after its worker dies if a
142
+ // platform helper retained a duplicate. Socket EOF alone is therefore
143
+ // not a sufficient lifetime signal. The generation-bound worker PID and
144
+ // its durable launch anchor provide an independent identity fence.
145
+ finishForWorkerDisconnect();
146
+ workerSocket.destroy();
147
+ return false;
148
+ }
149
+ try {
150
+ persistKeeper({ keeperHeartbeatAt: now() });
151
+ return true;
152
+ } catch (error) {
153
+ try {
154
+ reportPersistenceFailure("heartbeat", error);
155
+ } catch {
156
+ // Diagnostics are best effort and must not escape the timer callback.
157
+ }
158
+ return false;
159
+ }
160
+ }
161
+
162
+ function stateOwnsTurn(state, turn) {
163
+ return Boolean(
164
+ state &&
165
+ state.id === turn.id &&
166
+ state.workerGeneration === turn.workerGeneration &&
167
+ state.turnLaunchResolution?.token === turn.turnLaunchToken &&
168
+ Number(state.turnLaunchResolution?.attempt) === turn.attempt &&
169
+ state.turnLaunchResolution?.outcome === "spawned" &&
170
+ Number(state.agentPid) === turn.agentPid &&
171
+ Number(state.agentStartedAt) === turn.agentStartedAt &&
172
+ Number(state.agentRuntimePid) === turn.agentRuntimePid &&
173
+ Number(state.agentRuntimeStartedAt) === turn.agentRuntimeStartedAt,
174
+ );
175
+ }
176
+
177
+ function persistKeeperTurn(job, turn, patch) {
178
+ return mutateBackgroundAgentState(
179
+ job.id,
180
+ (current) => {
181
+ if (
182
+ !stateOwnsTurn(current, turn) ||
183
+ Number(current.keeperPid) !== process.pid
184
+ ) {
185
+ return null;
186
+ }
187
+ return { ...current, ...patch };
188
+ },
189
+ { timeoutMs: BACKGROUND_AGENT_KEEPER_STATE_LOCK_TIMEOUT_MS },
190
+ );
191
+ }
192
+
193
+ export function stopBackgroundAgentKeeperTurnTrees(targets, options = {}) {
194
+ const platform = options.platform || process.platform;
195
+ const processAlive = options.isProcessAlive || isProcessAlive;
196
+ const processTreeExecutionAlive =
197
+ options.isProcessTreeExecutionAlive ||
198
+ isBackgroundProcessTreeExecutionAlive;
199
+ const stopTree = options.stopProcessTree || stopBackgroundAgentChildTree;
200
+ const failures = [];
201
+ let precedingTreeStopSucceeded = false;
202
+ for (const target of targets) {
203
+ // On Windows the wrapper's taskkill /T also retires its runtime child.
204
+ // Avoid starting a second bounded taskkill for a target the first tree
205
+ // operation already proved absent; under 20-way churn that redundant
206
+ // command can consume the keeper's entire cleanup SLO. Never skip the
207
+ // first root merely because its leader exited: POSIX descendants can keep
208
+ // executing in the leader's process group and still require a group kill.
209
+ if (precedingTreeStopSucceeded && !processAlive(target.pid)) continue;
210
+ try {
211
+ stopTree(target.pid, {
212
+ signal: "SIGKILL",
213
+ expectedStartedAt: target.startedAt,
214
+ processGroup: target.processGroup,
215
+ strictIdentity: true,
216
+ allowDirectFallback: false,
217
+ });
218
+ precedingTreeStopSucceeded = true;
219
+ } catch (error) {
220
+ // A detached POSIX group can keep executing after its leader exits. Do
221
+ // not discard the strict stop failure from a leader-only liveness probe;
222
+ // the group-aware residual check preserves the actionable identity
223
+ // reason while descendants remain executable.
224
+ const leaderAlive = processAlive(target.pid);
225
+ const residualAlive =
226
+ leaderAlive ||
227
+ (platform !== "win32" &&
228
+ target.processGroup === true &&
229
+ processTreeExecutionAlive(target.pid, target.startedAt));
230
+ if (residualAlive) {
231
+ failures.push(error?.message || String(error));
232
+ }
233
+ }
234
+ }
235
+ return failures;
236
+ }
237
+
238
+ async function cleanupTurn(job, turn, reason) {
239
+ const requestedAt = Date.now();
240
+ const requestedPersistence = await retryKeeperPersistence(() =>
241
+ persistKeeperTurn(job, turn, {
242
+ turnKeeperStatus: "cleanup-requested",
243
+ turnKeeperCleanupReason: reason,
244
+ turnKeeperCleanupRequestedAt: requestedAt,
245
+ }),
246
+ );
247
+
248
+ const targets = [
249
+ {
250
+ pid: turn.agentPid,
251
+ startedAt: turn.agentStartedAt,
252
+ processGroup: true,
253
+ },
254
+ {
255
+ pid: turn.agentRuntimePid,
256
+ startedAt: turn.agentRuntimeStartedAt,
257
+ processGroup: false,
258
+ },
259
+ ].filter(
260
+ (target, index, values) =>
261
+ values.findIndex((candidate) => candidate.pid === target.pid) === index,
262
+ );
263
+ const failures = stopBackgroundAgentKeeperTurnTrees(targets);
264
+
265
+ const deadline =
266
+ Date.now() + BACKGROUND_AGENT_KEEPER_CLEANUP_CONFIRM_TIMEOUT_MS;
267
+ let alive = targets.filter((target) =>
268
+ isBackgroundProcessTreeExecutionAlive(target.pid, target.startedAt),
269
+ );
270
+ while (alive.length > 0 && Date.now() < deadline) {
271
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, 10));
272
+ alive = targets.filter((target) =>
273
+ isBackgroundProcessTreeExecutionAlive(target.pid, target.startedAt),
274
+ );
275
+ }
276
+ const confirmed = alive.length === 0 && failures.length === 0;
277
+ let finalPersistenceAttempts = 0;
278
+ const finalPersistence = await retryKeeperPersistence(() => {
279
+ finalPersistenceAttempts += 1;
280
+ return persistKeeperTurn(job, turn, {
281
+ turnKeeperStatus: confirmed ? "retired" : "cleanup-unconfirmed",
282
+ turnKeeperCleanupReason: reason,
283
+ turnKeeperCleanupRequestedAt: requestedAt,
284
+ turnKeeperCleanupConfirmedAt: confirmed ? Date.now() : null,
285
+ turnKeeperCleanupError:
286
+ failures.length > 0
287
+ ? failures.join("; ").slice(0, 1_000)
288
+ : alive.length > 0
289
+ ? `process tree still executable: ${alive.map(({ pid }) => pid).join(",")}`
290
+ : null,
291
+ turnKeeperPersistenceRetries:
292
+ Math.max(0, requestedPersistence.attempts - 1) +
293
+ Math.max(0, finalPersistenceAttempts - 1),
294
+ });
295
+ });
296
+ const persistenceError = finalPersistence.error
297
+ ? finalPersistence.error?.message || String(finalPersistence.error)
298
+ : finalPersistence.result?.applied === false
299
+ ? "keeper cleanup state no longer owned"
300
+ : null;
301
+ if (persistenceError) failures.push(persistenceError);
302
+ return {
303
+ confirmed: confirmed && !persistenceError,
304
+ failures,
305
+ alive,
306
+ persistenceError,
307
+ };
308
+ }
309
+
310
+ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
311
+ const job = JSON.parse(readFileSync(jobFile, "utf8"));
312
+ removeJobFile(jobFile);
313
+ const startedAt = Date.now();
314
+ const claim = mutateBackgroundAgentState(job.id, (current) => {
315
+ if (
316
+ !current ||
317
+ current.status !== "running" ||
318
+ current.workerGeneration !== job.workerGeneration ||
319
+ current.keeperGeneration !== job.keeperGeneration
320
+ ) {
321
+ return null;
322
+ }
323
+ return {
324
+ ...current,
325
+ keeperPid: process.pid,
326
+ keeperStartedAt: startedAt,
327
+ keeperStatus: "listening",
328
+ keeperHeartbeatAt: startedAt,
329
+ };
330
+ });
331
+ if (!claim.applied) return { status: "unclaimed" };
332
+
333
+ if (process.platform !== "win32") {
334
+ try {
335
+ unlinkSync(job.pipePath);
336
+ } catch {
337
+ // Usually ENOENT; the generation-bound endpoint is not reusable.
338
+ }
339
+ }
340
+
341
+ let workerSocket = null;
342
+ let candidateSocket = null;
343
+ let authenticatedHello = null;
344
+ let authenticatedWorkerStartedAt = null;
345
+ let armedTurn = null;
346
+ let finishing = false;
347
+ let finishResolve;
348
+ const finished = new Promise((resolvePromise) => {
349
+ finishResolve = resolvePromise;
350
+ });
351
+
352
+ const persistKeeper = (patch) =>
353
+ mutateBackgroundAgentState(job.id, (current) => {
354
+ if (
355
+ !current ||
356
+ current.workerGeneration !== job.workerGeneration ||
357
+ current.keeperGeneration !== job.keeperGeneration ||
358
+ Number(current.keeperPid) !== process.pid
359
+ ) {
360
+ return null;
361
+ }
362
+ return { ...current, ...patch };
363
+ });
364
+
365
+ const finishForWorkerDisconnect = () => {
366
+ if (finishing) return false;
367
+ finishing = true;
368
+ const active = armedTurn;
369
+ void (async () => {
370
+ let cleanup;
371
+ try {
372
+ cleanup = active
373
+ ? await cleanupTurn(job, active, "worker-disconnected")
374
+ : { confirmed: true, failures: [], alive: [] };
375
+ } catch (error) {
376
+ cleanup = {
377
+ confirmed: false,
378
+ failures: [error?.message || String(error)],
379
+ alive: [],
380
+ };
381
+ }
382
+ // Closing the keeper must never be skipped because one final metadata
383
+ // update throws. Retry the critical status write, retain the failure in
384
+ // the log, and always settle the server promise without an unhandled
385
+ // rejection.
386
+ const persisted = await retryKeeperPersistence(() =>
387
+ persistKeeper({
388
+ keeperStatus: active
389
+ ? cleanup.confirmed
390
+ ? "worker-disconnected"
391
+ : "cleanup-unconfirmed"
392
+ : "closed",
393
+ keeperEndedAt: Date.now(),
394
+ ...(cleanup.failures.length > 0
395
+ ? {
396
+ keeperError: cleanup.failures.join("; ").slice(0, 1_000),
397
+ }
398
+ : {}),
399
+ }),
400
+ );
401
+ if (persisted.error) {
402
+ try {
403
+ writeSync(
404
+ 2,
405
+ `[background-agent-keeper] final state persistence failed: ${persisted.error?.stack || persisted.error?.message || String(persisted.error)}\n`,
406
+ );
407
+ } catch {
408
+ // The inherited diagnostic handle may already be closed.
409
+ }
410
+ }
411
+ server.close(() => finishResolve({ status: "closed" }));
412
+ })();
413
+ return true;
414
+ };
415
+
416
+ const server = net.createServer((socket) => {
417
+ let authenticated = false;
418
+ if (workerSocket || candidateSocket) {
419
+ socket.destroy();
420
+ return;
421
+ }
422
+ candidateSocket = socket;
423
+ const helloTimer = setTimeout(() => socket.destroy(), 5_000);
424
+ helloTimer.unref?.();
425
+ socket.on(
426
+ "data",
427
+ createNdjsonReader(
428
+ (message) => {
429
+ if (
430
+ message?.protocolVersion !==
431
+ BACKGROUND_AGENT_KEEPER_PROTOCOL_VERSION
432
+ ) {
433
+ socket.destroy();
434
+ return;
435
+ }
436
+ if (!authenticated) {
437
+ if (message.type !== BACKGROUND_AGENT_KEEPER_HELLO) {
438
+ socket.destroy();
439
+ return;
440
+ }
441
+ let hello;
442
+ try {
443
+ hello = normalizeBackgroundAgentKeeperHello(message);
444
+ } catch {
445
+ socket.destroy();
446
+ return;
447
+ }
448
+ const current = readBackgroundAgentState(job.id);
449
+ if (
450
+ hello.id !== job.id ||
451
+ hello.workerGeneration !== job.workerGeneration ||
452
+ hello.token !== job.token ||
453
+ Number(current?.workerClaimedPid) !== hello.workerPid ||
454
+ current?.keeperGeneration !== job.keeperGeneration ||
455
+ Number(current?.keeperPid) !== process.pid
456
+ ) {
457
+ socket.destroy();
458
+ return;
459
+ }
460
+ authenticated = true;
461
+ authenticatedHello = hello;
462
+ authenticatedWorkerStartedAt = Number(current.startedAt);
463
+ workerSocket = socket;
464
+ clearTimeout(helloTimer);
465
+ persistKeeper({ keeperStatus: "ready", keeperReadyAt: Date.now() });
466
+ writeMessage(
467
+ socket,
468
+ createBackgroundAgentKeeperMessage(
469
+ BACKGROUND_AGENT_KEEPER_READY,
470
+ {
471
+ id: job.id,
472
+ workerGeneration: job.workerGeneration,
473
+ keeperPid: process.pid,
474
+ },
475
+ ),
476
+ );
477
+ return;
478
+ }
479
+
480
+ if (
481
+ message.type !== BACKGROUND_AGENT_KEEPER_ARM &&
482
+ message.type !== BACKGROUND_AGENT_KEEPER_RETIRE
483
+ ) {
484
+ return;
485
+ }
486
+ let turn;
487
+ try {
488
+ turn = normalizeBackgroundAgentKeeperTurn(message);
489
+ } catch (error) {
490
+ writeMessage(
491
+ socket,
492
+ createBackgroundAgentKeeperMessage(
493
+ "background-agent-keeper-error",
494
+ {
495
+ requestId: message.requestId,
496
+ code: "BACKGROUND_AGENT_KEEPER_INVALID_TURN",
497
+ message: error?.message || String(error),
498
+ },
499
+ ),
500
+ );
501
+ return;
502
+ }
503
+
504
+ if (message.type === BACKGROUND_AGENT_KEEPER_ARM) {
505
+ const durableTurnState = readBackgroundAgentState(job.id);
506
+ if (
507
+ armedTurn ||
508
+ durableTurnState?.status !== "running" ||
509
+ !stateOwnsTurn(durableTurnState, turn)
510
+ ) {
511
+ writeMessage(
512
+ socket,
513
+ createBackgroundAgentKeeperMessage(
514
+ "background-agent-keeper-error",
515
+ {
516
+ requestId: message.requestId,
517
+ code: "BACKGROUND_AGENT_KEEPER_ARM_REJECTED",
518
+ message: "keeper arm did not match the durable turn",
519
+ },
520
+ ),
521
+ );
522
+ return;
523
+ }
524
+ armedTurn = turn;
525
+ const armedAt = Date.now();
526
+ const armed = persistKeeperTurn(job, turn, {
527
+ turnKeeperStatus: "armed",
528
+ turnKeeperPid: process.pid,
529
+ turnKeeperArmedAt: armedAt,
530
+ });
531
+ if (!armed.applied) {
532
+ armedTurn = null;
533
+ writeMessage(
534
+ socket,
535
+ createBackgroundAgentKeeperMessage(
536
+ "background-agent-keeper-error",
537
+ {
538
+ requestId: message.requestId,
539
+ code: "BACKGROUND_AGENT_KEEPER_ARM_REJECTED",
540
+ message: "keeper arm persistence was rejected",
541
+ },
542
+ ),
543
+ );
544
+ return;
545
+ }
546
+ writeMessage(
547
+ socket,
548
+ createBackgroundAgentKeeperMessage(
549
+ BACKGROUND_AGENT_KEEPER_ARMED,
550
+ { requestId: message.requestId, ...turn },
551
+ ),
552
+ );
553
+ return;
554
+ }
555
+
556
+ if (!sameBackgroundAgentKeeperTurn(turn, armedTurn)) {
557
+ writeMessage(
558
+ socket,
559
+ createBackgroundAgentKeeperMessage(
560
+ "background-agent-keeper-error",
561
+ {
562
+ requestId: message.requestId,
563
+ code: "BACKGROUND_AGENT_KEEPER_RETIRE_REJECTED",
564
+ message: "keeper retire binding mismatch",
565
+ },
566
+ ),
567
+ );
568
+ return;
569
+ }
570
+ void cleanupTurn(job, turn, "turn-exited").then((result) => {
571
+ if (!result.confirmed) {
572
+ writeMessage(
573
+ socket,
574
+ createBackgroundAgentKeeperMessage(
575
+ "background-agent-keeper-error",
576
+ {
577
+ requestId: message.requestId,
578
+ code: "BACKGROUND_AGENT_KEEPER_CLEANUP_UNCONFIRMED",
579
+ message: "keeper could not confirm process-tree cleanup",
580
+ },
581
+ ),
582
+ );
583
+ return;
584
+ }
585
+ armedTurn = null;
586
+ writeMessage(
587
+ socket,
588
+ createBackgroundAgentKeeperMessage(
589
+ BACKGROUND_AGENT_KEEPER_RETIRED,
590
+ { requestId: message.requestId, ...turn },
591
+ ),
592
+ );
593
+ });
594
+ },
595
+ () => socket.destroy(),
596
+ ),
597
+ );
598
+
599
+ const disconnected = () => {
600
+ clearTimeout(helloTimer);
601
+ if (candidateSocket === socket) candidateSocket = null;
602
+ if (workerSocket !== socket || finishing) return;
603
+ finishForWorkerDisconnect();
604
+ };
605
+ socket.once("close", disconnected);
606
+ socket.once("error", disconnected);
607
+ });
608
+
609
+ const heartbeat = setInterval(
610
+ () =>
611
+ runBackgroundAgentKeeperHeartbeat({
612
+ finishing,
613
+ workerSocket,
614
+ authenticatedHello,
615
+ authenticatedWorkerStartedAt,
616
+ armedTurn,
617
+ persistKeeper,
618
+ finishForWorkerDisconnect,
619
+ }),
620
+ HEARTBEAT_INTERVAL_MS,
621
+ );
622
+ heartbeat.unref?.();
623
+ const startupTimer = setTimeout(
624
+ () => {
625
+ if (workerSocket || finishing) return;
626
+ finishing = true;
627
+ persistKeeper({
628
+ keeperStatus: "startup-timeout",
629
+ keeperEndedAt: Date.now(),
630
+ });
631
+ server.close(() => finishResolve({ status: "startup-timeout" }));
632
+ },
633
+ Number(options.startupTimeoutMs) || STARTUP_TIMEOUT_MS,
634
+ );
635
+ startupTimer.unref?.();
636
+
637
+ await new Promise((resolvePromise, reject) => {
638
+ server.once("error", reject);
639
+ server.listen(job.pipePath, () => {
640
+ server.removeListener("error", reject);
641
+ resolvePromise();
642
+ });
643
+ });
644
+ const result = await finished;
645
+ clearInterval(heartbeat);
646
+ clearTimeout(startupTimer);
647
+ if (process.platform !== "win32") {
648
+ try {
649
+ unlinkSync(job.pipePath);
650
+ } catch {
651
+ // Already removed or never materialized.
652
+ }
653
+ try {
654
+ cleanupBackgroundAgentKeeperPipeDirectory(job.pipePath);
655
+ } catch {
656
+ // Another keeper can still own a sibling socket in the shared namespace.
657
+ }
658
+ }
659
+ return {
660
+ ...result,
661
+ authenticatedWorkerPid: authenticatedHello?.workerPid || null,
662
+ };
663
+ }
664
+
665
+ function reportFatal(error) {
666
+ try {
667
+ writeSync(
668
+ 2,
669
+ `[background-agent-keeper] fatal: ${error?.stack || error?.message || String(error)}\n`,
670
+ );
671
+ } catch {
672
+ // The inherited diagnostic handle may already be closed.
673
+ }
674
+ process.exit(1);
675
+ }
676
+
677
+ const invokedPath = process.argv[1] ? resolve(process.argv[1]) : null;
678
+ const modulePath = resolve(fileURLToPath(import.meta.url));
679
+ if (
680
+ invokedPath !== null &&
681
+ (process.platform === "win32"
682
+ ? invokedPath.toLowerCase() === modulePath.toLowerCase()
683
+ : invokedPath === modulePath)
684
+ ) {
685
+ runBackgroundAgentKeeper(process.argv[2]).then(
686
+ () => process.exit(0),
687
+ reportFatal,
688
+ );
689
+ }