chainlesschain 0.165.1 → 0.165.2

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 (202) hide show
  1. package/README.md +248 -2
  2. package/package.json +4 -1
  3. package/src/assets/web-panel/assets/{AIOps-CYxFDrcm.js → AIOps-lS1R0Cza.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CPBtdiU2.js → ActionButton-BmlgBPsj.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-Dayq7Dp7.js → Analytics-CNKczv1S.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-6U7bPb2s.js → AppLayout-BuZGJARQ.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-D2Iqg_3D.js → Artifacts-Dm73Sl-O.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-CdoOB7WV.js → Audit-BGMW7A5e.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-DrnAVB8w.js → BackgroundAgents-BQ6M54JR.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-CesMi5cy.js → Backup-DBzGP0bR.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-DaX9ptax.js → BaseInput-D1mLCoOV.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-OrwOPxDS.js → Chat-BUfcN7p8.js} +4 -4
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CvPZFRxM.js → ChatBubbleRenderer-CY5m8ZHg.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-DiHbcysC.js → Checkbox-Du0Yg5Pi.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CbgCkaBJ.js → Codegen-B2RXziHW.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-Du5hgrh4.js → Col-DWhekOrR.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-DsPOBW-p.js → Community-HQhrkon4.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-BwHIFkNC.js → Compact-izHNb4uz.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-BvrDQSgd.js → Compliance-BDwLy41-.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-gwAE6arx.js → Cowork-DFJhLa7K.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-DW-0l4uF.js → Cron-DYY9RG7-.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-j-NsLesB.js → Crosschain-rr7tJTtv.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-CibVEBOI.js → DID-D2sfHRLi.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-W4z9vSfe.js → Dashboard-q_eGdhha.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-DAcrkKxG.js → Dropdown-Cufrh2Bz.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-6GXah-89.js → EmailListRenderer-B-kdv10f.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-TKDCq8Ku.js → FamilyGuardDashboard-D21XjgfG.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-BXBLlcNm.js → Federation-CAwRTLZH.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-BoJJBkG3.js → FormItemContext-vx3daNDF.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-okjsl46v.js → GenericCardRenderer-CRWY2wU4.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Rvs7i6nn.js → Git-Q6SwauyD.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-CHSUxXXS.js → Governance-BJ1q4IQZ.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-V3BlfxsB.js → Inference-CSWjROgD.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-C_NlD4aR.js → KnowledgeGraph-fzuQsWuY.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-Nn4LNxhA.js → Logs-DVF8Mnns.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-BLif4QSg.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-DEmjFcg-.js → Marketplace-BQP7LOA4.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DlopXipI.js → McpTools-BNlJCmNc.js} +4 -4
  39. package/src/assets/web-panel/assets/{Memory-BG3ynX60.js → Memory-DLLg5WQ4.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-DeRzWOfT.js → MobileBridge-CR2skNAF.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-Tyd3o2A0.js → MobileProjects-BxVavObl.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-BoPbOv1C.js → Mtc-B17pgqMI.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-un-rTHp7.js → MtcAudit-4UmV5cIL.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-Ds0oveEB.js → Multisig-DoMEXEcU.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-D09JI-yY.js → NLProgramming-h9DMXFX4.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-DTRhiKJn.js → Notes-Cihn-xX8.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-BAnnNCim.js → NotificationSettings-BE6ZGa3R.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-Yr-1fSYi.js → OrderTableRenderer-CnwojoSI.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-S9EAolpj.js → Organization-B2vpwmjb.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-CqcLKqJ8.js → Overflow-C4z28R0-.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-Dw0F9EcU.js → P2P-BVlFkwhW.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-BDRwUevN.js → PdhVaultBrowser-BXbEdL5B.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-D3_c3nKB.js → Permissions-C28xs4lp.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-CxdMV98x.js → PersonalDataHub-Dgl_rBjM.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-CgjNQFLI.js → Pipeline-3QUIhrKI.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-BHghIIht.js → Privacy-8Gh33OZ5.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-De6pqaKM.js → ProjectInit-BNmg4tZi.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-Bg4ZkNYQ.js → ProjectSettings-vgMfeCf9.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-BhtAVilc.js +1 -0
  60. package/src/assets/web-panel/assets/{Providers-CUnHbNf-.js → Providers-CXoOa8eX.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-DA2f3Wts.js → QrScannerModal-BhfTF0oS.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-C1FeHDqp.js → QuickAsk-C746HEAj.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-B5guaUMN.js → Recommend-BSej8Eo0.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-PDADSds3.js → RemoteSession-f3gmnlG3.js} +3 -3
  65. package/src/assets/web-panel/assets/{Reputation-eTlRhgvK.js → Reputation-CyvybpkN.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-D7AqSGY0.js → Row-DwV2PeYn.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-JiuLm4Ja.js → RssFeed-11GrzjZy.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-CwruahW3.js → Search-BjKXkrAY.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-CdhqaR0b.js → Security-eWiFb9Yk.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-CU0ZIlcT.js → Services-CdCsMAtt.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-CtduPfAb.js → Skeleton-4Q2UTKQ5.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-CROXm6AY.js → Skills-v4rtYrRj.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-DiZiABFD.js → Sla-CRhsFvd_.js} +1 -1
  74. package/src/assets/web-panel/assets/SpeechSettings-DLjKsDVu.js +1 -0
  75. package/src/assets/web-panel/assets/{SyncSettings-DBX6J1Oe.js → SyncSettings-D16zonl7.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-BLyRJd3-.js → Tasks-Xxozz0i_.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-BKa5qzmk.js → Templates-CXJpPtCT.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-DnD3a4M6.js → Tenant-XRV0L1ZF.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-DEitQeTb.js → Terminal-CYuO1i_w.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-CjHggTDq.js → TimelineRenderer-BXqitWsu.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-CsGT7sfl.js → Tokens-BvPrDHIy.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-yr4rFDrW.js → Trigger-DhCrSTf5.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-CLvu1Sjp.js → Trust-DmsuZD6K.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-B0m7yUP8.js → UkeySign-D_yrts5C.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-C0VvP4lc.js → VideoEditing-B01ihPrm.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-kllBAG1M.js → Wallet-DpUjKa-N.js} +3 -3
  87. package/src/assets/web-panel/assets/{WebAuthn-C54UQNLx.js → WebAuthn-CL07yON6.js} +3 -3
  88. package/src/assets/web-panel/assets/{WorkflowEditor-B7y8qSvU.js → WorkflowEditor-CLMflnRr.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-DRzTh8CJ.js → chat-DtUqe8h_.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-BSMwSnCx.js → colors-DPKrgVfy.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-BTg602H5.js → compact-item-BTt2GtOm.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-B6EGsdvp.js → createContext-D_VeUc1J.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-Bj5nUW2v.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DDKqJ8RD.js → hasIn-1kndiylj.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CUEU48o1.js → index-2eyAXE3d.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-rjzk9z1Q.js → index-4bna2_SR.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CmAubjDi.js → index-8nz_fmlj.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Co7oM1qr.js → index-B9KvcS7R.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BMuGd2--.js → index-BAXfRKfG.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BVTAl5N2.js → index-BHpBMWCh.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DpTHIRfZ.js → index-BaZAbw9A.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CWGax2QW.js → index-BepLZwgB.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DiW0sDkz.js → index-Bhudc_Ez.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CGNklbP6.js → index-BlDNVgBt.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-u2YVld9W.js → index-By5n8up9.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-pqfux8vZ.js → index-CHG-KcS4.js} +1 -1
  107. package/src/assets/web-panel/assets/index-CPcM-eqo.js +1 -0
  108. package/src/assets/web-panel/assets/{index-BRqt4fO-.js → index-CX81qSM4.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BsR7Y2NG.js → index-CauoBQkH.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-C_JzIXUq.js → index-CcO04eDn.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CQlHFN_g.js → index-CtF7EN49.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CwPUZbMI.js → index-CzjkKUmR.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-th2ihDc0.js → index-D95OFht7.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CHfLYwcW.js → index-DFIZ_1Sf.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C1GFfuqN.js → index-DG2MZQ3f.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-D1naNj4U.js → index-DJ8vXGOa.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DLdrHhML.js → index-Dbbymxpj.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DBT3YeQ9.js → index-DdkplL9R.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BICn3Qh1.js → index-DfGl8CqG.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DqFXGzzC.js → index-DfKBRdSS.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Bx044qDj.js → index-DsJCDUJG.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-XFe-1twF.js → index-Dwqmtk65.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C66NpWy5.js → index-DxdTdBix.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CmAInUkX.js → index-FzdwcSy2.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Cf23wVMs.js → index-GsoUQ2M7.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D5CsBShd.js → index-brCInrzq.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-323Hhxxt.js → index-dVc-CXY4.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-p2HYn89m.js → index-fk2Rjacw.js} +3 -3
  129. package/src/assets/web-panel/assets/{index-Dfv2lKu3.js → index-l1BETrDb.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CNxg61YP.js → index-m31Ka8hZ.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CDTvsX8t.js → index-ocf06EVQ.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Dc77VR3H.js → index-sDoMHTNA.js} +1 -1
  133. package/src/assets/web-panel/assets/index-xeSyVoRe.js +1 -0
  134. package/src/assets/web-panel/assets/{initDefaultProps-C3Djh9fa.js → initDefaultProps-CvyYJd7z.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BzqFmbSe.js → motion-DYZkhUdz.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DJo_FFaW.js → move-slnJRVT1.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-_tlTiBvk.js → mtc-parser-DVc0CKmy.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-CB2ZyxUg.js → omit-BCXAtHCq.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-DZwIFD4u.js → pickAttrs-DBsemz_K.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-DxZY0dgk.js → placementArrow-BRtkJ-nv.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-CzzGt7GL.js → responsiveObserve-Cebq7EZn.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-BaIK52yz.js → slide-DT7qvId2.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-3-FHu9_M.js → statusUtils-pQiITLkG.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-BkRT68fK.js → styleChecker-c-19BVKQ.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-DHYwKS-s.js → useFlexGapSupport-DsS5-0bE.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-DVVUhV8p.js → useFs-CxGiMe5n.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-CkrFKAzQ.js → usePersonalDataHub-D9btVID5.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BuBLFz0c.js → vnode-CUYFsFEN.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-kpm6LtXU.js → zoom-CXmn_35j.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +1 -1
  152. package/src/commands/cowork.js +280 -5
  153. package/src/commands/plugin.js +819 -64
  154. package/src/commands/session-location.js +318 -7
  155. package/src/constants.js +2 -2
  156. package/src/data/changelog.json +14 -1
  157. package/src/gateways/ws/action-protocol.js +1 -1
  158. package/src/harness/jsonl-session-store.js +638 -2
  159. package/src/harness/prompt-compressor.js +4 -0
  160. package/src/lib/background-agent-keeper-client.js +29 -0
  161. package/src/lib/background-agent-keeper-protocol.js +4 -0
  162. package/src/lib/background-agent-supervisor.js +59 -29
  163. package/src/lib/cowork-task-runner.js +70 -0
  164. package/src/lib/cowork-workflow.js +42 -9
  165. package/src/lib/doctor-checkup.js +34 -10
  166. package/src/lib/dynamic-workflow-draft.js +535 -0
  167. package/src/lib/dynamic-workflow-facade.js +11 -11
  168. package/src/lib/dynamic-workflow-runtime.js +2974 -0
  169. package/src/lib/execution-location-contract.js +111 -7
  170. package/src/lib/execution-location-runtime.js +3 -1
  171. package/src/lib/execution-location-target.js +910 -0
  172. package/src/lib/llm-providers.js +1 -0
  173. package/src/lib/mcp-call-ledger-store.js +35 -0
  174. package/src/lib/mcp-call-ledger.js +30 -0
  175. package/src/lib/plugin-runtime/__tests__/remote-source.test.js +34 -16
  176. package/src/lib/plugin-runtime/install.js +4956 -657
  177. package/src/lib/plugin-runtime/manifest.js +3 -1
  178. package/src/lib/plugin-runtime/marketplace-artifact-readback.js +671 -31
  179. package/src/lib/plugin-runtime/marketplace-catalog.js +68 -2
  180. package/src/lib/plugin-runtime/marketplace-impact.js +208 -15
  181. package/src/lib/plugin-runtime/marketplace-network.js +317 -0
  182. package/src/lib/plugin-runtime/marketplace-pac-worker.js +15 -0
  183. package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +25 -2
  184. package/src/lib/plugin-runtime/marketplace-source-cache.js +359 -0
  185. package/src/lib/plugin-runtime/policy.js +21 -6
  186. package/src/lib/plugin-runtime/publisher-trust.js +332 -0
  187. package/src/lib/plugin-runtime/remote-source.js +323 -27
  188. package/src/lib/plugin-runtime/scopes.js +317 -41
  189. package/src/lib/plugin-runtime/transaction-journal.js +806 -0
  190. package/src/lib/public-ide-capability-manifest.js +1 -1
  191. package/src/lib/remote-membership-coordinator.js +8 -6
  192. package/src/lib/sub-agent-context.js +268 -2
  193. package/src/lib/task-model-selector.js +1 -1
  194. package/src/lib/with-file-lock.js +95 -5
  195. package/src/runtime/agent-core.js +595 -41
  196. package/src/workers/background-agent-keeper.js +40 -10
  197. package/src/assets/web-panel/assets/MarkdownRenderer-SVAgNnkt.js +0 -1
  198. package/src/assets/web-panel/assets/Projects-CP8vPvzs.js +0 -1
  199. package/src/assets/web-panel/assets/SpeechSettings-B5pLcXUw.js +0 -1
  200. package/src/assets/web-panel/assets/devWarning-Dol8jLEP.js +0 -1
  201. package/src/assets/web-panel/assets/index-C3QX5tOl.js +0 -1
  202. package/src/assets/web-panel/assets/index-xYybvEic.js +0 -1
@@ -161,7 +161,7 @@ const DESKTOP_SHORTCUT_KEYS = [
161
161
  export const PUBLIC_IDE_CAPABILITY_MANIFEST = freeze({
162
162
  schema: "chainlesschain.public-ide-capabilities/v1",
163
163
  schemaVersion: 1,
164
- minimumCliVersion: "0.162.47",
164
+ minimumCliVersion: "0.162.190",
165
165
  protocol: buildCompatFixture(),
166
166
  bridge: {
167
167
  schemaVersion: 1,
@@ -8,12 +8,14 @@
8
8
  * with a pinned Ed25519 key; WebSocket delivery is transport only and never an
9
9
  * authority by itself.
10
10
  *
11
- * Integration status: this module is a deployable contract, but the current
12
- * remote-session registry, approval bridge, and agent dispatch paths do not
13
- * call it yet. Cross-host revoke closure therefore remains false until every
14
- * real side-effect entry point performs the online one-shot consume. A
15
- * successful consume linearizes dispatch authority; it cannot undo an external
16
- * effect after that effect has started.
11
+ * Production integration: the Remote Session registry and WebSocket protocol
12
+ * use this coordinator, the client-hosted approval bridge durably adopts and
13
+ * ACKs its signed leases, and Agent shell dispatch consumes the exact one-shot
14
+ * lease online immediately before the side effect. Side-effect paths without
15
+ * that consume boundary reject Remote approval instead of falling back to a
16
+ * cached decision. This provides cross-host revoke fencing under one online,
17
+ * strongly consistent coordinator; it does not claim quorum availability or
18
+ * undo an external effect after a successful consume has linearized dispatch.
17
19
  */
18
20
 
19
21
  import fs from "node:fs";
@@ -31,6 +31,27 @@ const SUMMARY_DIRECT_THRESHOLD = 500; // chars — below this, use result as-is
31
31
  const SUMMARY_SECTION_PATTERN =
32
32
  /^##\s*(Summary|Result|Output|Conclusion|Answer)/im;
33
33
  const TRUNCATE_LENGTH = 500;
34
+ const PROVIDER_CLIENT_REQUEST_ID_RE = /^ccwf_[a-f0-9]{64}$/;
35
+ const PROVIDER_CALL_ID_RE = /^[A-Za-z0-9._:-]{1,256}$/;
36
+ const PROVIDER_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
37
+ const WORKFLOW_EFFECT_ID_RE = /^sha256:[a-f0-9]{64}$/;
38
+ const WORKFLOW_CHILD_EFFECT_PROTOCOL = "cc-workflow-child-effect/v1";
39
+ const WORKFLOW_TOOL_CALL_ID_RE = /^[\x21-\x7e]{1,512}$/;
40
+ const WORKFLOW_TOOL_NAME_RE = /^[A-Za-z0-9._:-]{1,256}$/;
41
+
42
+ function ownDataValue(value, property) {
43
+ if (!value || (typeof value !== "object" && typeof value !== "function")) {
44
+ return undefined;
45
+ }
46
+ try {
47
+ const descriptor = Object.getOwnPropertyDescriptor(value, property);
48
+ return descriptor && Object.hasOwn(descriptor, "value")
49
+ ? descriptor.value
50
+ : undefined;
51
+ } catch {
52
+ return undefined;
53
+ }
54
+ }
34
55
 
35
56
  function runtimeUsageObserverFailure(error, code, message) {
36
57
  const failure =
@@ -132,6 +153,8 @@ export class SubAgentContext {
132
153
  * @param {boolean} [options.memoryEnabled=true] - When false, the child's
133
154
  * context engine suppresses hierarchical-memory recall (contract memory:false)
134
155
  * @param {object} [options.llmOptions] - LLM provider/model/key options
156
+ * @param {string} [options.workflowEffectId] - Durable workflow effect bound
157
+ * to provider request identities for this child run
135
158
  * @param {string} [options.cwd] - Working directory
136
159
  * @param {boolean} [options.useWorktree] - Force worktree isolation (overrides flag)
137
160
  * @param {function} [options.onUsageBoundary] - Synchronous observer for
@@ -207,6 +230,11 @@ export class SubAgentContext {
207
230
  this._runId = null;
208
231
  this._checkpointIds = [];
209
232
  this._toolUseIds = [];
233
+ this._workflowEffectId = options.workflowEffectId ?? null;
234
+ this._providerRequestAttempts = [];
235
+ this._providerRequestReceipts = [];
236
+ this._nestedEffectAttempts = [];
237
+ this._nestedEffectSettlements = [];
210
238
 
211
239
  // ── Isolated state ──────────────────────────────────────────────
212
240
  // Independent message history — never shared with parent
@@ -547,7 +575,9 @@ export class SubAgentContext {
547
575
  }
548
576
  return result;
549
577
  } catch (err) {
550
- if (err?.runtimeLedgerPersistence === true) throw err;
578
+ if (err?.runtimeLedgerPersistence === true || this._workflowEffectId) {
579
+ throw err;
580
+ }
551
581
  // If worktree creation fails (e.g. not a git repo), fall back to direct
552
582
  this.status = "failed";
553
583
  this.completedAt = new Date().toISOString();
@@ -615,6 +645,17 @@ export class SubAgentContext {
615
645
  }
616
646
  options.toolAdmission = this.toolAdmission;
617
647
  options.skillAllowlist = this.skillAllowlist;
648
+ // A caller cannot replace the durable outer effect through loopOptions.
649
+ // agentLoop validates the canonical sha256 form before any provider call.
650
+ if (this._workflowEffectId) {
651
+ options.workflowEffectId = this._workflowEffectId;
652
+ options.onProviderRequestBoundary = (event) => {
653
+ this._recordProviderRequestAttempt(event);
654
+ };
655
+ } else {
656
+ delete options.workflowEffectId;
657
+ delete options.onProviderRequestBoundary;
658
+ }
618
659
  const authority = this._parentAuthority;
619
660
  if (authority.permissionRules) {
620
661
  options.permissionRules = authority.permissionRules;
@@ -651,6 +692,22 @@ export class SubAgentContext {
651
692
  let observeUsageSettlement = null;
652
693
  let observeToolBoundary = null;
653
694
  let observeToolSettlement = null;
695
+ const providerReceiptForSettlement = (event) => {
696
+ for (
697
+ let index = this._providerRequestReceipts.length - 1;
698
+ index >= 0;
699
+ index -= 1
700
+ ) {
701
+ const receipt = this._providerRequestReceipts[index];
702
+ if (
703
+ receipt.callId === event?.callId &&
704
+ (!event?.provider || receipt.provider === event.provider)
705
+ ) {
706
+ return { ...receipt };
707
+ }
708
+ }
709
+ return null;
710
+ };
654
711
  if (strictUsageTelemetry) {
655
712
  // Presence is checked without invoking either observer. In particular,
656
713
  // do not manufacture a null "validation event" that a durable writer
@@ -805,11 +862,29 @@ export class SubAgentContext {
805
862
  }
806
863
  }
807
864
  if (event.type === "tool-executing" && event.tool_use_id) {
865
+ this._recordNestedEffectAttempt(event);
808
866
  const toolUseId = String(event.tool_use_id);
809
867
  if (!this._toolUseIds.includes(toolUseId)) {
810
868
  this._toolUseIds.push(toolUseId);
811
869
  }
812
870
  }
871
+ if (event.type === "provider-request-receipt") {
872
+ this._providerRequestReceipts.push(
873
+ Object.freeze({
874
+ protocol: event.protocol,
875
+ provider: event.provider,
876
+ workflowEffectId: event.workflowEffectId,
877
+ callId: event.callId,
878
+ callSequence: event.callSequence,
879
+ source: event.source,
880
+ clientRequestId: event.clientRequestId,
881
+ requestId: event.requestId || null,
882
+ responseId: event.responseId || null,
883
+ requestIdentitySemantics: event.requestIdentitySemantics,
884
+ independentlyReadable: event.independentlyReadable,
885
+ }),
886
+ );
887
+ }
813
888
 
814
889
  if (event.type === "token-usage") {
815
890
  const forwardedUsage = {
@@ -819,6 +894,7 @@ export class SubAgentContext {
819
894
  model: event.model || null,
820
895
  usage: event.usage || null,
821
896
  ...(event.source ? { source: event.source } : {}),
897
+ providerReceipt: providerReceiptForSettlement(event),
822
898
  attribution: event.attribution || null,
823
899
  ...(event.boundaryNotified === true ||
824
900
  event.ledgerPersisted === true
@@ -865,6 +941,9 @@ export class SubAgentContext {
865
941
  }
866
942
 
867
943
  if (event.type === "model-usage-started") {
944
+ if (event.providerRequestId) {
945
+ this._recordProviderRequestAttempt(event);
946
+ }
868
947
  if (strictUsageTelemetry) {
869
948
  if (
870
949
  event.boundaryNotified === true ||
@@ -896,6 +975,7 @@ export class SubAgentContext {
896
975
  ? "semantic-compaction"
897
976
  : "model"),
898
977
  code: event.code || "provider_transport_outcome_unknown",
978
+ providerReceipt: providerReceiptForSettlement(event),
899
979
  attribution: event.attribution || null,
900
980
  ...(event.boundaryNotified === true ||
901
981
  event.ledgerPersisted === true
@@ -932,9 +1012,26 @@ export class SubAgentContext {
932
1012
  }
933
1013
 
934
1014
  if (event.type === "tool-result") {
1015
+ const nestedEffectSettlement =
1016
+ this._recordNestedEffectSettlement(event);
935
1017
  if (strictUsageTelemetry && event.tool_use_id) {
936
1018
  observeToolSettlement(event);
937
1019
  }
1020
+ // A consumer may stop the async generator after this yielded result
1021
+ // because of cancellation or a local token budget. Fence the unknown
1022
+ // here, after the strict settlement observer closes its boundary, so
1023
+ // generator.return() cannot suppress the outer reconciliation signal.
1024
+ if (nestedEffectSettlement?.outcomeUnknown === true) {
1025
+ const error = new Error(
1026
+ `Workflow-bound nested tool ${nestedEffectSettlement.tool} has an unknown outcome and requires reconciliation`,
1027
+ );
1028
+ error.code = "CC_WORKFLOW_NESTED_TOOL_OUTCOME_UNKNOWN";
1029
+ error.workflowEffectOutcomeUnknown = true;
1030
+ error.workflowEffectId = nestedEffectSettlement.workflowEffectId;
1031
+ error.workflowChildEffectId =
1032
+ nestedEffectSettlement.childEffectId;
1033
+ throw error;
1034
+ }
938
1035
  // Store large tool results as artifacts
939
1036
  const resultStr = JSON.stringify(event.result);
940
1037
  // Estimate token count from tool result (~4 chars per token)
@@ -1004,8 +1101,22 @@ export class SubAgentContext {
1004
1101
  "Strict child execution ended with an unsettled provider or tool call",
1005
1102
  );
1006
1103
  }
1104
+ if (
1105
+ this._workflowEffectId &&
1106
+ this._nestedEffectAttempts.length !==
1107
+ this._nestedEffectSettlements.length
1108
+ ) {
1109
+ const error = new Error(
1110
+ "Workflow-bound child execution ended with an unsettled nested effect",
1111
+ );
1112
+ error.code = "CC_WORKFLOW_CHILD_EFFECT_SETTLEMENT_INCOMPLETE";
1113
+ error.workflowEffectOutcomeUnknown = true;
1114
+ throw error;
1115
+ }
1007
1116
  } catch (err) {
1008
- if (err?.runtimeLedgerPersistence === true) throw err;
1117
+ if (err?.runtimeLedgerPersistence === true || this._workflowEffectId) {
1118
+ throw err;
1119
+ }
1009
1120
  if (this.isAborted()) {
1010
1121
  this.forceComplete(this._cancelReason || "cancelled", {
1011
1122
  partialContent: lastContent,
@@ -1159,11 +1270,166 @@ export class SubAgentContext {
1159
1270
  parentTraceId: this._hookParentTraceId || null,
1160
1271
  checkpointIds: [...this._checkpointIds],
1161
1272
  toolUseIds: [...this._toolUseIds],
1273
+ ...(this._workflowEffectId
1274
+ ? {
1275
+ providerRequestAttempts: this.providerRequestAttempts(),
1276
+ providerRequestReceipts: this.providerRequestReceipts(),
1277
+ nestedEffectAttempts: this.nestedEffectAttempts(),
1278
+ nestedEffectSettlements: this.nestedEffectSettlements(),
1279
+ }
1280
+ : {}),
1162
1281
  worktreeId: worktree?.branch || this._worktreeBranch || null,
1163
1282
  worktreePath: worktree?.path || this._worktreePath || null,
1164
1283
  };
1165
1284
  }
1166
1285
 
1286
+ /**
1287
+ * Effect-bound provider calls admitted before transport dispatch.
1288
+ */
1289
+ providerRequestAttempts() {
1290
+ return this._providerRequestAttempts.map((attempt) => ({ ...attempt }));
1291
+ }
1292
+
1293
+ /**
1294
+ * Provider-returned request identifiers observed during this child run.
1295
+ * Values are copied so callers cannot mutate recovery evidence in place.
1296
+ */
1297
+ providerRequestReceipts() {
1298
+ return this._providerRequestReceipts.map((receipt) => ({ ...receipt }));
1299
+ }
1300
+
1301
+ nestedEffectAttempts() {
1302
+ return this._nestedEffectAttempts.map((attempt) => ({ ...attempt }));
1303
+ }
1304
+
1305
+ nestedEffectSettlements() {
1306
+ return this._nestedEffectSettlements.map((settlement) => ({
1307
+ ...settlement,
1308
+ }));
1309
+ }
1310
+
1311
+ _recordNestedEffectAttempt(event) {
1312
+ if (!this._workflowEffectId) return;
1313
+ const attempt = {
1314
+ protocol: event?.workflowEffectProtocol,
1315
+ workflowEffectId: event?.workflowEffectId,
1316
+ childEffectId: event?.workflowChildEffectId,
1317
+ childSequence: event?.workflowChildSequence,
1318
+ kind: "tool",
1319
+ tool: event?.tool,
1320
+ toolUseId: event?.tool_use_id,
1321
+ identitySemantics: "runtime-derived",
1322
+ };
1323
+ if (
1324
+ attempt.protocol !== WORKFLOW_CHILD_EFFECT_PROTOCOL ||
1325
+ attempt.workflowEffectId !== this._workflowEffectId ||
1326
+ !WORKFLOW_EFFECT_ID_RE.test(attempt.childEffectId || "") ||
1327
+ !Number.isSafeInteger(attempt.childSequence) ||
1328
+ attempt.childSequence < 1 ||
1329
+ !WORKFLOW_TOOL_NAME_RE.test(attempt.tool || "") ||
1330
+ !WORKFLOW_TOOL_CALL_ID_RE.test(attempt.toolUseId || "") ||
1331
+ this._nestedEffectAttempts.some(
1332
+ (existing) =>
1333
+ existing.childEffectId === attempt.childEffectId ||
1334
+ existing.childSequence === attempt.childSequence,
1335
+ )
1336
+ ) {
1337
+ const error = new TypeError(
1338
+ "workflow nested-effect boundary is malformed or duplicated",
1339
+ );
1340
+ error.code = "CC_WORKFLOW_CHILD_EFFECT_BOUNDARY_INVALID";
1341
+ throw error;
1342
+ }
1343
+ this._nestedEffectAttempts.push(Object.freeze(attempt));
1344
+ }
1345
+
1346
+ _recordNestedEffectSettlement(event) {
1347
+ if (!this._workflowEffectId || !event?.workflowChildEffectId) return null;
1348
+ const attempt = this._nestedEffectAttempts.find(
1349
+ (candidate) =>
1350
+ candidate.childEffectId === event.workflowChildEffectId,
1351
+ );
1352
+ const outcomeUnknown = ownDataValue(event.result, "outcomeUnknown") === true;
1353
+ const resultError = ownDataValue(event.result, "error");
1354
+ const mcpLedgerId = ownDataValue(event.result, "mcpLedgerId") ?? null;
1355
+ const settlement = {
1356
+ protocol: event?.workflowEffectProtocol,
1357
+ workflowEffectId: event?.workflowEffectId,
1358
+ childEffectId: event?.workflowChildEffectId,
1359
+ childSequence: event?.workflowChildSequence,
1360
+ kind: "tool",
1361
+ tool: event?.tool,
1362
+ toolUseId: event?.tool_use_id,
1363
+ status: outcomeUnknown
1364
+ ? "outcome_unknown"
1365
+ : event?.error || resultError
1366
+ ? "failed"
1367
+ : "completed",
1368
+ outcomeUnknown,
1369
+ mcpLedgerId,
1370
+ mcpLedgerPrewritePersisted:
1371
+ ownDataValue(event.result, "mcpLedgerPrewritePersisted") === true,
1372
+ mcpLedgerSettlementPersisted:
1373
+ ownDataValue(event.result, "mcpLedgerSettlementPersisted") === true,
1374
+ };
1375
+ if (
1376
+ !attempt ||
1377
+ settlement.protocol !== WORKFLOW_CHILD_EFFECT_PROTOCOL ||
1378
+ settlement.workflowEffectId !== this._workflowEffectId ||
1379
+ settlement.childSequence !== attempt.childSequence ||
1380
+ settlement.tool !== attempt.tool ||
1381
+ settlement.toolUseId !== attempt.toolUseId ||
1382
+ (mcpLedgerId !== null &&
1383
+ !PROVIDER_CALL_ID_RE.test(String(mcpLedgerId))) ||
1384
+ this._nestedEffectSettlements.some(
1385
+ (existing) => existing.childEffectId === settlement.childEffectId,
1386
+ )
1387
+ ) {
1388
+ const error = new TypeError(
1389
+ "workflow nested-effect settlement is malformed or duplicated",
1390
+ );
1391
+ error.code = "CC_WORKFLOW_CHILD_EFFECT_SETTLEMENT_INVALID";
1392
+ error.workflowEffectOutcomeUnknown = true;
1393
+ throw error;
1394
+ }
1395
+ this._nestedEffectSettlements.push(Object.freeze(settlement));
1396
+ return settlement;
1397
+ }
1398
+
1399
+ _recordProviderRequestAttempt(event) {
1400
+ if (!this._workflowEffectId) return;
1401
+ const attempt = {
1402
+ protocol: "cc-provider-request-attempt/v1",
1403
+ provider: event?.provider,
1404
+ workflowEffectId: event?.workflowEffectId,
1405
+ callId: event?.callId,
1406
+ callSequence: event?.callSequence,
1407
+ source: event?.source,
1408
+ clientRequestId: event?.providerRequestId,
1409
+ requestIdentitySemantics: event?.requestIdentitySemantics,
1410
+ };
1411
+ if (
1412
+ !PROVIDER_NAME_RE.test(attempt.provider || "") ||
1413
+ attempt.workflowEffectId !== this._workflowEffectId ||
1414
+ !PROVIDER_CALL_ID_RE.test(attempt.callId || "") ||
1415
+ !Number.isSafeInteger(attempt.callSequence) ||
1416
+ attempt.callSequence < 1 ||
1417
+ !["model", "semantic-compaction"].includes(attempt.source) ||
1418
+ !PROVIDER_CLIENT_REQUEST_ID_RE.test(attempt.clientRequestId || "") ||
1419
+ attempt.requestIdentitySemantics !== "trace-only" ||
1420
+ this._providerRequestAttempts.some(
1421
+ (existing) => existing.callId === attempt.callId,
1422
+ )
1423
+ ) {
1424
+ const error = new TypeError(
1425
+ "workflow provider request boundary is malformed or duplicated",
1426
+ );
1427
+ error.code = "CC_WORKFLOW_PROVIDER_REQUEST_BOUNDARY_INVALID";
1428
+ throw error;
1429
+ }
1430
+ this._providerRequestAttempts.push(Object.freeze(attempt));
1431
+ }
1432
+
1167
1433
  /**
1168
1434
  * True when this sub-agent has been asked to stop — either via an external
1169
1435
  * abort signal or via this context's own `abort()` (precise single-agent
@@ -24,7 +24,7 @@ export const TaskType = {
24
24
  */
25
25
  const TASK_MODEL_MAP = {
26
26
  [TaskType.CHAT]: {
27
- volcengine: "doubao-seed-2-1-pro-260628",
27
+ volcengine: "deepseek-v4-flash-260425",
28
28
  openai: "gpt-4o-mini",
29
29
  anthropic: "claude-sonnet-4-6",
30
30
  deepseek: "deepseek-chat",
@@ -88,7 +88,22 @@ export function withFileLock(targetPath, fn, opts = {}) {
88
88
  const incumbent = ownerRead.owner;
89
89
  acquisitionError = ownerRead.error;
90
90
  if (incumbent) {
91
- if (!isOwnerAlive(incumbent)) {
91
+ // The owner writes this exact-token marker only after its critical
92
+ // section has finished. A Windows sharing violation can then prevent
93
+ // the final recursive removal while the owner process remains alive,
94
+ // which would otherwise strand every later strict caller behind a
95
+ // lock that no longer protects any work. Complete only the release
96
+ // explicitly published by the still-current owner; a missing,
97
+ // corrupt or mismatched marker remains fail-closed.
98
+ const publishedRelease = completePublishedRelease(
99
+ _fs,
100
+ lockDir,
101
+ incumbent,
102
+ owner,
103
+ isOwnerAlive,
104
+ );
105
+ if (publishedRelease.completed) continue;
106
+ if (!publishedRelease.published && !isOwnerAlive(incumbent)) {
92
107
  if (reclaimOwnedDirectory(_fs, lockDir, incumbent, isOwnerAlive)) {
93
108
  continue;
94
109
  }
@@ -146,7 +161,7 @@ export function withFileLock(targetPath, fn, opts = {}) {
146
161
  let releaseError = null;
147
162
  if (held) {
148
163
  try {
149
- released = releaseOwnedDirectory(_fs, lockDir, owner);
164
+ released = releaseOwnedDirectory(_fs, lockDir, owner, isOwnerAlive);
150
165
  } catch (error) {
151
166
  releaseError = error;
152
167
  }
@@ -160,8 +175,9 @@ export function withFileLock(targetPath, fn, opts = {}) {
160
175
  throw error;
161
176
  }
162
177
  if (held && released && yieldAfterReleaseMs > 0) {
163
- // The lock is already gone. This lets an existing waiter run before a
164
- // process performing many tiny transactions can immediately reacquire it.
178
+ // The lock is gone or its exact release marker has transferred cleanup to
179
+ // contenders. Let an existing waiter run before a process performing many
180
+ // tiny transactions can immediately reacquire it.
165
181
  _sleep(yieldAfterReleaseMs);
166
182
  }
167
183
  return result;
@@ -383,7 +399,69 @@ function reclaimLegacyDirectory(_fs, lockDir, stat) {
383
399
  return true;
384
400
  }
385
401
 
386
- function releaseOwnedDirectory(_fs, lockDir, owner) {
402
+ function completePublishedRelease(_fs, lockDir, owner, claimant, ownerAlive) {
403
+ const markerPath = path.join(lockDir, `.release-${owner.token}`);
404
+ if (!sameOwner(readOwner(_fs, markerPath), owner)) {
405
+ return { published: false, completed: false };
406
+ }
407
+ const claimPath = path.join(lockDir, `.release-claim-${owner.token}`);
408
+ for (;;) {
409
+ const existingClaim = readOwner(_fs, claimPath);
410
+ if (existingClaim) {
411
+ if (sameOwner(existingClaim, claimant)) break;
412
+ if (!ownerAlive(existingClaim)) {
413
+ if (removeOwnMarker(_fs, claimPath, existingClaim)) continue;
414
+ }
415
+ return { published: true, completed: false };
416
+ }
417
+ try {
418
+ writeOwnerMarker(_fs, claimPath, claimant);
419
+ break;
420
+ } catch (error) {
421
+ if (error?.code === "ENOENT") {
422
+ return { published: true, completed: !directoryExists(_fs, lockDir) };
423
+ }
424
+ if (error?.code === "EEXIST" || isTransientLockError(error)) {
425
+ return { published: true, completed: false };
426
+ }
427
+ throw error;
428
+ }
429
+ }
430
+ if (!sameOwner(readOwner(_fs, path.join(lockDir, "owner.json")), owner)) {
431
+ removeOwnMarker(_fs, claimPath, claimant);
432
+ return { published: true, completed: false };
433
+ }
434
+ const cleanupDir = `${lockDir}.released-${owner.token}-${claimant.token}`;
435
+ try {
436
+ // Detach the exact released directory from the acquisition path before
437
+ // cleaning it. Recursive removal may span several Windows filesystem
438
+ // operations; deleting `lockDir` in place lets a new atomic acquirer
439
+ // install a replacement between those operations and risks deleting that
440
+ // replacement. The claimant token makes delayed cleanup private to this
441
+ // handoff, just like the owner's normal rename-first release path.
442
+ _fs.renameSync(lockDir, cleanupDir);
443
+ try {
444
+ _fs.rmSync(cleanupDir, { recursive: true, force: true });
445
+ } catch {
446
+ // The acquisition path is already free. A uniquely named released
447
+ // directory is harmless bounded debris if Windows still holds a handle.
448
+ }
449
+ return { published: true, completed: true };
450
+ } catch (error) {
451
+ removeOwnMarker(_fs, claimPath, claimant);
452
+ if (error?.code === "ENOENT") {
453
+ return { published: true, completed: !directoryExists(_fs, lockDir) };
454
+ }
455
+ // A Windows sharing transient leaves the exact release marker in place so
456
+ // this or another contender can retry within its normal deadline.
457
+ if (isTransientLockError(error)) {
458
+ return { published: true, completed: false };
459
+ }
460
+ throw error;
461
+ }
462
+ }
463
+
464
+ function releaseOwnedDirectory(_fs, lockDir, owner, ownerAlive) {
387
465
  if (!sameOwner(readOwner(_fs, path.join(lockDir, "owner.json")), owner)) {
388
466
  return false;
389
467
  }
@@ -419,6 +497,18 @@ function releaseOwnedDirectory(_fs, lockDir, owner) {
419
497
  removeOwnMarker(_fs, markerPath, owner);
420
498
  return false;
421
499
  }
500
+ if (typeof _fs.renameSync === "function") {
501
+ // After publishing the marker, use the same exclusive claim protocol as a
502
+ // later contender. The releasing process must not perform an unclaimed,
503
+ // delayed delete: another process could complete the release and install a
504
+ // replacement lock before that delete resumes.
505
+ // Publishing the exact release marker is the logical handoff point. A
506
+ // contender may win the cleanup claim before this owner does; that is a
507
+ // successful release, not lost ownership, because every acquirer must
508
+ // complete or wait behind the published marker before entering its body.
509
+ return completePublishedRelease(_fs, lockDir, owner, owner, ownerAlive)
510
+ .published;
511
+ }
422
512
  _fs.rmSync(lockDir, { recursive: true, force: true });
423
513
  return true;
424
514
  }