chainlesschain 0.162.187 → 0.162.189

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 (191) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-CETaO3s8.js → AIOps-DbZft8w0.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-CAe7E9TW.js → ActionButton-DoOZpwfN.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-BGr2mzwO.js → Analytics-B0J0KLTW.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-BEoY-NcT.js → AppLayout-D9eIR0qq.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-DUZivCgt.js → Artifacts-BnZXEVeo.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-C3yBs68k.js → Audit-X1ce4Bjn.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-C9KwiY3O.js → BackgroundAgents-BvWR-y5v.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BoSWD_C8.js → Backup-BublSGEd.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-Dp09GkxL.js → BaseInput-CR_77Hso.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-5t9d-Ja0.js → Chat-DixW52tq.js} +6 -6
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DmVku4GH.js → ChatBubbleRenderer-DDHDpBBA.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-8QJFlkqI.js → Checkbox-CCv1C87K.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-BJVFQ3lL.js → Codegen-B03Cc3dd.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-6hdLD0oB.js → Col-CA06-6pU.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-B8opo0uY.js → Community-Bj881J52.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-Dv8HudiN.js → Compact-UZLTPoaL.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-DhxuCS5N.js → Compliance-kGwzRb_0.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Dggy6VIL.js → Cowork-KundoYM8.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-DSmDqJTT.js → Cron-BYZyoqDe.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-7bZq0j5f.js → Crosschain-BhrrppBX.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BWY4kh-H.js → DID-CdC6EguZ.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-bOzPZxlu.js → Dashboard-CcQx3J4y.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-BTFJHqQu.js → Dropdown-BpijtEzQ.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-BC3MOdtT.js → EmailListRenderer-DfZjeZTe.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CFHAdTTY.js → FamilyGuardDashboard-BFYSf69C.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-DZ_4aF_n.js → Federation-Okdi3pJm.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-BZje6UTj.js → FormItemContext-mH-HebKW.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-D7QfCvjN.js → GenericCardRenderer-xivV69x7.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-DuG7qANG.js → Git-1rh_YBwg.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-BDmYAgTh.js → Governance-eAXkNxdY.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-DSXAfc1Q.js → Inference-CS6dMVTN.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DusleniP.js → KnowledgeGraph-Bq_TnkvD.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-CpImmA8p.js → Logs-KchHtiHt.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BZ6LJBkk.js → MarkdownRenderer-TabqHW9B.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-DXS8CXkE.js → Marketplace-CsmGcX5X.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-D151UfUI.js → McpTools-BaPFnnyz.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-DKDxnoA1.js → Memory-2m-sUUf7.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BFZ95iKM.js → MobileBridge-D5nVZBRu.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-CDAR-grF.js → MobileProjects-Bo8MLZvT.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-OGJ95xJs.js → Mtc-3GDvamGa.js} +6 -6
  42. package/src/assets/web-panel/assets/{MtcAudit-BmGTo1XC.js → MtcAudit-D2zGuZCH.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-XumJRqOa.js → Multisig-BBVQ4t6t.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-DHhhRZed.js → NLProgramming-BIOCyOGp.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-B9rGxNNr.js → Notes-DVwBGpzc.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-DClLbMYV.js → NotificationSettings-BDnHfIfX.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-I8OohT7N.js → OrderTableRenderer-C7Ek20ag.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-M6WzaYVa.js → Organization-D3SRKaa7.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Cb3B5KtM.js → Overflow-C2B8ahyn.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-BGk57kya.js → P2P-2iwQzG65.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-CXcKyPqK.js → PdhVaultBrowser-CoPiWqMN.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-hsfJ-Y0e.js → Permissions-yV5XNXo5.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-BSBii4js.js → PersonalDataHub-uCDJhMUF.js} +4 -4
  54. package/src/assets/web-panel/assets/{Pipeline-BNw52rey.js → Pipeline-hN7ucMOA.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-DUpfue4E.js → Privacy-CgU0ptN4.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-C68NsP3h.js → ProjectInit-CfYPiT6j.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-BJ2It9v4.js → ProjectSettings-B0A2C4nw.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-BE5C0ajn.js +1 -0
  59. package/src/assets/web-panel/assets/{Providers-EnU4VWwS.js → Providers-CstQPf8W.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-BRt5-B_I.js → QrScannerModal-Co3z_ynk.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-Jd5L9UL2.js → QuickAsk-DMTS6pQ6.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-BNDhoVIu.js → Recommend-ETw4odUa.js} +1 -1
  63. package/src/assets/web-panel/assets/RemoteSession-B0rkE09d.js +5 -0
  64. package/src/assets/web-panel/assets/{Reputation-D1wQPUNv.js → Reputation-BTVqQgKq.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-DnjRegul.js → Row-CBKKwwtO.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-C_T4I-nF.js → RssFeed-BHUQ438I.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-C4lrxlLh.js → Search-B4NqeUsF.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-ByYT-VzP.js → Security-BRjoYG5_.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-BHpyEqB5.js → Services-CxQzu3p3.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-C0I23vG-.js → Skeleton-BKzwwmWO.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-DGZXz22K.js → Skills-DrsFcgth.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-DL_urP9A.js → Sla-D21tmXCH.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-6x-hUNQ_.js → SpeechSettings-CjyjLfp8.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-Crk5TCXq.js → SyncSettings-Cu45-tnx.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-rZGq5PZs.js → Tasks-oMcILYOj.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-DT5el8Q8.js → Templates-Bh-Zcr5R.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-mlgeyMK8.js → Tenant-68ns63aI.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-C8HEA-lM.js → Terminal-BLJkFykE.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-DvlqBayB.js → TimelineRenderer-xcrJLq-O.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-C7lDcNmT.js → Tokens-C6PpFJtf.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-CM8nCnIJ.js → Trigger-BaKA7tdM.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-BQDLUQ9x.js → Trust-BzhA57Jc.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CVqM0OFE.js → UkeySign-4_U3JkIR.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-BvAcQpzT.js → VideoEditing-162AANxX.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-zFVFaAZ2.js → Wallet-B1viuql_.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-Cc522ums.js → WebAuthn-ZVWazhCy.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-Bg_wxpDo.js → WorkflowEditor-tzdyUk-1.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-jg0QwC8K.js → chat-DmrHNuwq.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-ALzYz1gO.js → colors-DAJ_M-PK.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-DXHYUoqi.js → compact-item-uIzmYwLs.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-BsGsBM5m.js → createContext-D_ZmuLwb.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-DIrncrV1.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-BccMLzxA.js → hasIn-CDt_shTL.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-Cg9cOVe9.js → index-BAvSmJXW.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BTTeAiOP.js → index-BDF6EQBs.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-C3k3ZwkQ.js → index-BHvlm0In.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CQ1ORdp-.js → index-BRa4kJ_S.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BcJzphrP.js → index-BTCXf1t-.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-rIy96PeM.js → index-Bbxjfstq.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CSFESurl.js → index-Bx4d6c1k.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Cmg_Npx5.js → index-ByzGvpR9.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CahDve8M.js → index-C16fYKFD.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-2VstcImq.js → index-C5WcdJH2.js} +3 -3
  104. package/src/assets/web-panel/assets/{index-C3Rf8wIM.js → index-CG0lY4h3.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-uzykuL_R.js → index-CKyd472S.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CBVIFX-x.js → index-CaelyMgF.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-TeCof1s3.js → index-CbD_WW_6.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BfF0OGKU.js → index-CcZ9xJI9.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-oWltafBn.js → index-CeUpKjvi.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CWLWBgfe.js → index-Cvak6Oe2.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-C0AbXsDy.js → index-CzXqvMuP.js} +1 -1
  112. package/src/assets/web-panel/assets/index-D-Swwqld.js +1 -0
  113. package/src/assets/web-panel/assets/{index-BfV8RDlg.js → index-D4n1lGTB.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Dxqiu9-F.js → index-D9OE8B3d.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BdCmJ_bG.js → index-DBFmcaCQ.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DgjQ-kdT.js → index-DdUMcwRB.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BBbbr1YB.js → index-DeGBoLQA.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Dmk7dD-T.js → index-DfSSy9uy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-JS2LXdd0.js → index-DqDSF2RW.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-D9oV2iRR.js → index-DvFhhe6r.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BvsII10i.js → index-DwfvPe-j.js} +1 -1
  122. package/src/assets/web-panel/assets/index-FMZAMKy8.js +1 -0
  123. package/src/assets/web-panel/assets/{index-CrZHUsOZ.js → index-FyplHuNt.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CLzPzN75.js → index-GcKOHD_c.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-koT8Q86Z.js → index-OzTxFIJP.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-08diKcTm.js → index-QsgfSZrV.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CJg6Wsjc.js → index-Z8P4BexE.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-srqW5_-X.js → index-clSX4PEc.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-mIQUoUx1.js → index-ifxmu8SY.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Cz_s25m3.js → index-rS3gl_68.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-UQXedWQz.js → index-rXdwUeE3.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-rcwgNMc0.js → index-saf3VNeP.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-xafkqrW3.js → initDefaultProps-pmfaD-aK.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BrtyQXLk.js → motion-CaQ2CpYF.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-BunUyg59.js → move-CkKTqNbI.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-BPwjBeuE.js → mtc-parser-Dd4xsgRI.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-BpyqZlNO.js → omit-BFuA5YEs.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-DDrV0XjE.js → pickAttrs-DDfxOAJc.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-1A18Pcl2.js → placementArrow-Be595JCp.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-B8Fnlmso.js → responsiveObserve-7byljUM3.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-x-Fwb2Zm.js → slide-CTWfOTaY.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-aAG5yJl_.js → statusUtils-CNZOuIxM.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-BRQSTMRL.js → styleChecker-DwOcBV78.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-D5X_KozY.js → useFlexGapSupport-B6MNwAO9.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DN_YIjP-.js → useFs-CzyQjH9r.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-BizxAvjw.js → usePersonalDataHub-Dxh3KiVA.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-B5oAxN93.js → vnode-DVfZiS5N.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-DJhvyzKv.js → zoom-BYme7fbY.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agent.js +22 -0
  151. package/src/commands/auto-mode.js +47 -0
  152. package/src/commands/checkpoint-managed.js +1059 -0
  153. package/src/commands/checkpoint.js +3 -0
  154. package/src/commands/team-distributed.js +3324 -0
  155. package/src/commands/team.js +1130 -74
  156. package/src/data/auto-mode-safety-eval-v1.json +3294 -0
  157. package/src/data/changelog.json +26 -0
  158. package/src/lib/agent-core.js +1 -0
  159. package/src/lib/agent-team/task-lease.js +271 -3
  160. package/src/lib/agent-team/team-adjudication.js +1501 -0
  161. package/src/lib/agent-team/team-budget.js +4 -1
  162. package/src/lib/agent-team/team-control-store.js +582 -0
  163. package/src/lib/agent-team/team-distributed-queue.js +5246 -0
  164. package/src/lib/agent-team/team-process-checkpoint.js +211 -0
  165. package/src/lib/agent-team/team-runner.js +319 -22
  166. package/src/lib/agent-team/team-worktree.js +1403 -95
  167. package/src/lib/auto-mode-safety-classifier.js +2034 -0
  168. package/src/lib/auto-mode-safety-eval.js +1022 -0
  169. package/src/lib/managed-checkpoint-render.js +60 -0
  170. package/src/lib/managed-tool-checkpoint.js +194 -0
  171. package/src/lib/process-execution-broker/index.js +438 -5
  172. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
  173. package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
  174. package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
  175. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  176. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  177. package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
  178. package/src/lib/process-execution-broker/workspace-transaction.js +3556 -0
  179. package/src/lib/secure-file-identity.js +433 -0
  180. package/src/lib/turn-binding.js +160 -2
  181. package/src/lib/with-file-lock.js +140 -23
  182. package/src/repl/agent-repl.js +19 -1
  183. package/src/runtime/agent-core.js +366 -122
  184. package/src/runtime/headless-runner.js +12 -0
  185. package/src/runtime/headless-stream.js +3 -0
  186. package/src/runtime/policies/agent-policy.js +14 -0
  187. package/src/assets/web-panel/assets/Projects-D2FHLUa-.js +0 -1
  188. package/src/assets/web-panel/assets/RemoteSession-BII41Mel.js +0 -5
  189. package/src/assets/web-panel/assets/devWarning-Dy1GJA4S.js +0 -1
  190. package/src/assets/web-panel/assets/index-8zbMbbuf.js +0 -1
  191. package/src/assets/web-panel/assets/index-C97f71oi.js +0 -1
@@ -0,0 +1,433 @@
1
+ /**
2
+ * Cross-API filesystem identity checks for secure path reads.
3
+ *
4
+ * Node 22.12 ships libuv 1.49.1. On Windows hosts that expose
5
+ * GetFileInformationByName, libuv 1.49/1.50 can project a path stat's device as
6
+ * zero while fstat on the same opened file reports the real volume serial.
7
+ * libuv 1.51 fixed the structure layout and device projection.
8
+ *
9
+ * Keep this compatibility deliberately narrow: only the affected Windows
10
+ * libuv range may ignore the path API's projected device, the handle device
11
+ * must match a separately opened parent/volume-root directory handle, and
12
+ * every other identity or mutation field remains exact.
13
+ *
14
+ * Holding a parent descriptor does not give Node's path-based fs APIs openat
15
+ * semantics. Callers must keep the canonical parent authority outside the
16
+ * control of untrusted concurrent writers. The checks below detect ordinary
17
+ * path/handle replacement, but a complete rename-away/replace/restore ABA can
18
+ * happen between checks and is deliberately not represented as prevented.
19
+ */
20
+
21
+ import fs from "node:fs";
22
+ import path from "node:path";
23
+
24
+ const IDENTITY_FIELDS = Object.freeze([
25
+ "dev",
26
+ "ino",
27
+ "mode",
28
+ "nlink",
29
+ "size",
30
+ "mtimeNs",
31
+ "ctimeNs",
32
+ ]);
33
+ const IDENTITY_FIELDS_EXCEPT_DEVICE = Object.freeze(
34
+ IDENTITY_FIELDS.filter((field) => field !== "dev"),
35
+ );
36
+ const DIRECTORY_IDENTITY_FIELDS = Object.freeze(["dev", "ino", "mode"]);
37
+ const DIRECTORY_IDENTITY_FIELDS_EXCEPT_DEVICE = Object.freeze(
38
+ DIRECTORY_IDENTITY_FIELDS.filter((field) => field !== "dev"),
39
+ );
40
+
41
+ export const SECURE_FILE_IDENTITY_ERROR = Object.freeze({
42
+ INVALID_PARENT: "SECURE_FILE_IDENTITY_INVALID_PARENT",
43
+ PARENT_RACE: "SECURE_FILE_IDENTITY_PARENT_RACE",
44
+ });
45
+
46
+ export class SecureFileIdentityError extends Error {
47
+ constructor(code, message, details = {}, cause = null) {
48
+ super(message, cause ? { cause } : undefined);
49
+ this.name = "SecureFileIdentityError";
50
+ this.code = code;
51
+ Object.assign(this, details);
52
+ }
53
+ }
54
+
55
+ function identityError(code, message, details = {}, cause = null) {
56
+ return new SecureFileIdentityError(code, message, details, cause);
57
+ }
58
+
59
+ function timestampValue(stat, nanosecondsKey, millisecondsKey) {
60
+ if (stat?.[nanosecondsKey] !== undefined && stat?.[nanosecondsKey] !== null) {
61
+ return String(stat[nanosecondsKey]);
62
+ }
63
+ if (stat?.[millisecondsKey] !== undefined) {
64
+ const value = Number(stat[millisecondsKey]);
65
+ return Number.isFinite(value)
66
+ ? String(Math.trunc(value * 1_000_000))
67
+ : null;
68
+ }
69
+ return null;
70
+ }
71
+
72
+ function scalarValue(stat, field) {
73
+ return stat?.[field] === undefined || stat?.[field] === null
74
+ ? null
75
+ : String(stat[field]);
76
+ }
77
+
78
+ /**
79
+ * Normalize either an fs.Stats/fs.BigIntStats object or an already normalized
80
+ * identity. Nanosecond fields are preferred so BigInt stat callers retain the
81
+ * exact timestamps returned by the kernel.
82
+ */
83
+ export function fileStatIdentity(stat) {
84
+ return {
85
+ dev: scalarValue(stat, "dev"),
86
+ ino: scalarValue(stat, "ino"),
87
+ mode: scalarValue(stat, "mode"),
88
+ nlink: scalarValue(stat, "nlink"),
89
+ size: scalarValue(stat, "size"),
90
+ mtimeNs: timestampValue(stat, "mtimeNs", "mtimeMs"),
91
+ ctimeNs: timestampValue(stat, "ctimeNs", "ctimeMs"),
92
+ };
93
+ }
94
+
95
+ function hasCompleteIdentity(identity) {
96
+ return IDENTITY_FIELDS.every((field) => identity[field] !== null);
97
+ }
98
+
99
+ function hasFields(identity, fields) {
100
+ return fields.every((field) => identity[field] !== null);
101
+ }
102
+
103
+ function sameFields(left, right, fields) {
104
+ return fields.every((field) => left[field] === right[field]);
105
+ }
106
+
107
+ function runtimePlatform(runtime) {
108
+ return String(runtime?.platform || process.platform);
109
+ }
110
+
111
+ function windowsVolumeOrShareRoot(canonicalPath) {
112
+ const normalized = path.win32.normalize(String(canonicalPath));
113
+ const extendedUnc = /^\\\\\?\\UNC\\([^\\]+)\\([^\\]+)(?:\\|$)/iu.exec(
114
+ normalized,
115
+ );
116
+ if (extendedUnc) {
117
+ return `\\\\?\\UNC\\${extendedUnc[1]}\\${extendedUnc[2]}\\`;
118
+ }
119
+ return path.win32.parse(normalized).root || path.parse(normalized).root;
120
+ }
121
+
122
+ export function sameFileStatIdentity(leftStat, rightStat) {
123
+ const left = fileStatIdentity(leftStat);
124
+ const right = fileStatIdentity(rightStat);
125
+ return (
126
+ hasCompleteIdentity(left) &&
127
+ hasCompleteIdentity(right) &&
128
+ sameFields(left, right, IDENTITY_FIELDS)
129
+ );
130
+ }
131
+
132
+ export function sameDirectoryStatIdentity(leftStat, rightStat) {
133
+ const left = fileStatIdentity(leftStat);
134
+ const right = fileStatIdentity(rightStat);
135
+ return (
136
+ hasFields(left, DIRECTORY_IDENTITY_FIELDS) &&
137
+ hasFields(right, DIRECTORY_IDENTITY_FIELDS) &&
138
+ sameFields(left, right, DIRECTORY_IDENTITY_FIELDS)
139
+ );
140
+ }
141
+
142
+ export function isAffectedWindowsZeroDeviceStatRuntime({
143
+ platform = process.platform,
144
+ uvVersion = process.versions.uv,
145
+ } = {}) {
146
+ if (platform !== "win32") return false;
147
+ const match = /^(\d+)\.(\d+)(?:\.|$)/.exec(String(uvVersion || ""));
148
+ if (!match) return false;
149
+ const major = Number(match[1]);
150
+ const minor = Number(match[2]);
151
+ return major === 1 && (minor === 49 || minor === 50);
152
+ }
153
+
154
+ /**
155
+ * Compare a path stat with an opened-handle stat (or a previously normalized
156
+ * handle identity) while binding the file to a trusted directory device.
157
+ */
158
+ export function samePathHandleFileIdentity(
159
+ pathStat,
160
+ handleStat,
161
+ expectedDevice,
162
+ runtime = undefined,
163
+ ) {
164
+ const pathIdentity = fileStatIdentity(pathStat);
165
+ const handleIdentity = fileStatIdentity(handleStat);
166
+ const trustedDevice =
167
+ expectedDevice === undefined || expectedDevice === null
168
+ ? null
169
+ : String(expectedDevice);
170
+ if (
171
+ !hasCompleteIdentity(pathIdentity) ||
172
+ !hasCompleteIdentity(handleIdentity) ||
173
+ trustedDevice === null ||
174
+ handleIdentity.dev !== trustedDevice
175
+ ) {
176
+ return false;
177
+ }
178
+ if (sameFields(pathIdentity, handleIdentity, IDENTITY_FIELDS)) return true;
179
+ return (
180
+ isAffectedWindowsZeroDeviceStatRuntime(runtime) &&
181
+ pathIdentity.dev !== handleIdentity.dev &&
182
+ handleIdentity.dev !== "0" &&
183
+ sameFields(pathIdentity, handleIdentity, IDENTITY_FIELDS_EXCEPT_DEVICE)
184
+ );
185
+ }
186
+
187
+ /**
188
+ * Directory contents can legitimately change while a child is written, so a
189
+ * parent recheck excludes content-derived metadata and the directory link
190
+ * count, retaining only stable identity/type/permission fields.
191
+ */
192
+ export function samePathHandleDirectoryIdentity(
193
+ pathStat,
194
+ handleStat,
195
+ expectedDevice,
196
+ runtime = undefined,
197
+ ) {
198
+ const pathIdentity = fileStatIdentity(pathStat);
199
+ const handleIdentity = fileStatIdentity(handleStat);
200
+ const trustedDevice =
201
+ expectedDevice === undefined || expectedDevice === null
202
+ ? null
203
+ : String(expectedDevice);
204
+ if (
205
+ !hasFields(pathIdentity, DIRECTORY_IDENTITY_FIELDS) ||
206
+ !hasFields(handleIdentity, DIRECTORY_IDENTITY_FIELDS) ||
207
+ trustedDevice === null ||
208
+ handleIdentity.dev !== trustedDevice
209
+ ) {
210
+ return false;
211
+ }
212
+ if (sameDirectoryStatIdentity(pathIdentity, handleIdentity)) {
213
+ return true;
214
+ }
215
+ return (
216
+ isAffectedWindowsZeroDeviceStatRuntime(runtime) &&
217
+ pathIdentity.dev !== handleIdentity.dev &&
218
+ handleIdentity.dev !== "0" &&
219
+ sameFields(
220
+ pathIdentity,
221
+ handleIdentity,
222
+ DIRECTORY_IDENTITY_FIELDS_EXCEPT_DEVICE,
223
+ )
224
+ );
225
+ }
226
+
227
+ function canonicalRealpath(runtimeFs, target) {
228
+ const realpathSync = runtimeFs?.realpathSync;
229
+ if (typeof realpathSync?.native === "function") {
230
+ return realpathSync.native(target);
231
+ }
232
+ if (typeof realpathSync === "function") {
233
+ return realpathSync.call(runtimeFs, target);
234
+ }
235
+ throw new TypeError("runtime fs does not provide realpathSync");
236
+ }
237
+
238
+ /**
239
+ * Open and retain the canonical parent directory while a caller inspects and
240
+ * opens a child. On Windows, a separately opened canonical volume/share root
241
+ * supplies the independently trusted device used by the identity comparators.
242
+ *
243
+ * Security precondition: the caller must ensure that an untrusted concurrent
244
+ * writer cannot rename the canonical parent (or one of its ancestors) during
245
+ * the callback. Node does not expose the handle-relative open/rename primitives
246
+ * required to make this helper an openat-style authority boundary.
247
+ */
248
+ export function withTrustedFileParentSync(
249
+ runtimeFs,
250
+ filePath,
251
+ callback,
252
+ { runtime = undefined } = {},
253
+ ) {
254
+ if (!runtimeFs || typeof callback !== "function") {
255
+ throw new TypeError("runtime fs and callback are required");
256
+ }
257
+ if (callback.constructor?.name === "AsyncFunction") {
258
+ throw new TypeError("secure file parent callback must be synchronous");
259
+ }
260
+ const requested = path.resolve(filePath);
261
+ const requestedParent = path.dirname(requested);
262
+ let canonicalParent;
263
+ try {
264
+ canonicalParent = canonicalRealpath(runtimeFs, requestedParent);
265
+ } catch (cause) {
266
+ throw identityError(
267
+ SECURE_FILE_IDENTITY_ERROR.INVALID_PARENT,
268
+ `Could not canonicalize secure file parent: ${requestedParent}`,
269
+ { filePath: requested, parentPath: requestedParent },
270
+ cause,
271
+ );
272
+ }
273
+
274
+ let authorityDescriptor = null;
275
+ let descriptor = null;
276
+ try {
277
+ const flags =
278
+ Number(runtimeFs.constants?.O_RDONLY ?? fs.constants.O_RDONLY) |
279
+ Number(runtimeFs.constants?.O_NOFOLLOW ?? fs.constants.O_NOFOLLOW ?? 0);
280
+ let trustedDevice = null;
281
+ if (runtimePlatform(runtime) === "win32") {
282
+ const authorityPath = windowsVolumeOrShareRoot(canonicalParent);
283
+ if (!authorityPath) {
284
+ throw identityError(
285
+ SECURE_FILE_IDENTITY_ERROR.INVALID_PARENT,
286
+ `Could not identify secure file parent volume/share root: ${canonicalParent}`,
287
+ { filePath: requested, parentPath: canonicalParent },
288
+ );
289
+ }
290
+ try {
291
+ authorityDescriptor = runtimeFs.openSync(authorityPath, flags);
292
+ const authority = runtimeFs.fstatSync(authorityDescriptor, {
293
+ bigint: true,
294
+ });
295
+ if (!authority.isDirectory() || String(authority.dev) === "0") {
296
+ throw identityError(
297
+ SECURE_FILE_IDENTITY_ERROR.INVALID_PARENT,
298
+ `Secure file parent volume/share root is not a stable directory: ${authorityPath}`,
299
+ {
300
+ filePath: requested,
301
+ parentPath: canonicalParent,
302
+ authorityPath,
303
+ },
304
+ );
305
+ }
306
+ trustedDevice = String(authority.dev);
307
+ } catch (cause) {
308
+ if (cause instanceof SecureFileIdentityError) throw cause;
309
+ throw identityError(
310
+ SECURE_FILE_IDENTITY_ERROR.INVALID_PARENT,
311
+ `Could not open secure file parent volume/share root: ${authorityPath}`,
312
+ {
313
+ filePath: requested,
314
+ parentPath: canonicalParent,
315
+ authorityPath,
316
+ },
317
+ cause,
318
+ );
319
+ }
320
+ }
321
+
322
+ let before;
323
+ try {
324
+ before = runtimeFs.lstatSync(canonicalParent, { bigint: true });
325
+ } catch (cause) {
326
+ throw identityError(
327
+ SECURE_FILE_IDENTITY_ERROR.INVALID_PARENT,
328
+ `Could not inspect secure file parent: ${canonicalParent}`,
329
+ { filePath: requested, parentPath: canonicalParent },
330
+ cause,
331
+ );
332
+ }
333
+ if (before.isSymbolicLink() || !before.isDirectory()) {
334
+ throw identityError(
335
+ SECURE_FILE_IDENTITY_ERROR.INVALID_PARENT,
336
+ `Secure file parent must be a real directory: ${canonicalParent}`,
337
+ { filePath: requested, parentPath: canonicalParent },
338
+ );
339
+ }
340
+ if (trustedDevice === null) {
341
+ trustedDevice = scalarValue(before, "dev");
342
+ }
343
+
344
+ descriptor = runtimeFs.openSync(canonicalParent, flags);
345
+ const opened = runtimeFs.fstatSync(descriptor, { bigint: true });
346
+ if (
347
+ !opened.isDirectory() ||
348
+ !samePathHandleDirectoryIdentity(before, opened, trustedDevice, runtime)
349
+ ) {
350
+ throw identityError(
351
+ SECURE_FILE_IDENTITY_ERROR.PARENT_RACE,
352
+ `Secure file parent changed while opening: ${canonicalParent}`,
353
+ { filePath: requested, parentPath: canonicalParent },
354
+ );
355
+ }
356
+ let result;
357
+ let callbackFailure = null;
358
+ try {
359
+ result = callback({
360
+ canonicalPath: path.join(canonicalParent, path.basename(requested)),
361
+ parentPath: canonicalParent,
362
+ parentDevice: String(opened.dev),
363
+ parentDescriptor: descriptor,
364
+ });
365
+ if (result && typeof result.then === "function") {
366
+ callbackFailure = new TypeError(
367
+ "secure file parent callback must not return a promise",
368
+ );
369
+ }
370
+ } catch (cause) {
371
+ callbackFailure = cause;
372
+ }
373
+
374
+ let heldAfter;
375
+ try {
376
+ heldAfter = runtimeFs.fstatSync(descriptor, { bigint: true });
377
+ } catch (cause) {
378
+ throw identityError(
379
+ SECURE_FILE_IDENTITY_ERROR.PARENT_RACE,
380
+ `Secure file parent handle became unavailable: ${canonicalParent}`,
381
+ { filePath: requested, parentPath: canonicalParent },
382
+ cause,
383
+ );
384
+ }
385
+ if (
386
+ !heldAfter.isDirectory() ||
387
+ !sameDirectoryStatIdentity(heldAfter, opened)
388
+ ) {
389
+ throw identityError(
390
+ SECURE_FILE_IDENTITY_ERROR.PARENT_RACE,
391
+ `Secure file parent handle changed during operation: ${canonicalParent}`,
392
+ { filePath: requested, parentPath: canonicalParent },
393
+ callbackFailure,
394
+ );
395
+ }
396
+
397
+ let after;
398
+ try {
399
+ after = runtimeFs.lstatSync(canonicalParent, { bigint: true });
400
+ } catch (cause) {
401
+ throw identityError(
402
+ SECURE_FILE_IDENTITY_ERROR.PARENT_RACE,
403
+ `Secure file parent became unavailable: ${canonicalParent}`,
404
+ { filePath: requested, parentPath: canonicalParent },
405
+ cause,
406
+ );
407
+ }
408
+ if (
409
+ after.isSymbolicLink() ||
410
+ !after.isDirectory() ||
411
+ !samePathHandleDirectoryIdentity(after, heldAfter, opened.dev, runtime)
412
+ ) {
413
+ throw identityError(
414
+ SECURE_FILE_IDENTITY_ERROR.PARENT_RACE,
415
+ `Secure file parent changed during operation: ${canonicalParent}`,
416
+ { filePath: requested, parentPath: canonicalParent },
417
+ callbackFailure,
418
+ );
419
+ }
420
+ if (callbackFailure) throw callbackFailure;
421
+ return result;
422
+ } finally {
423
+ try {
424
+ if (descriptor !== null) {
425
+ runtimeFs.closeSync(descriptor);
426
+ }
427
+ } finally {
428
+ if (authorityDescriptor !== null) {
429
+ runtimeFs.closeSync(authorityDescriptor);
430
+ }
431
+ }
432
+ }
433
+ }
@@ -69,6 +69,20 @@ export function classifyToolKind(name) {
69
69
  * mutation WITHOUT a checkpoint → NONE (files unrecoverable)
70
70
  */
71
71
  export function computeCoverage(flags = {}) {
72
+ const managedCoverage =
73
+ flags.managedCheckpointCoverage === TURN_COVERAGE.FULL ||
74
+ flags.managedCheckpointCoverage === TURN_COVERAGE.PARTIAL ||
75
+ flags.managedCheckpointCoverage === TURN_COVERAGE.NONE
76
+ ? flags.managedCheckpointCoverage
77
+ : null;
78
+ // An explicit managed-checkpoint verdict is stronger than the legacy
79
+ // tool-name heuristic. `none` means the requested writer lifetime could not
80
+ // be bounded; `partial` means workspace recovery exists but at least one
81
+ // path/effect remains outside it.
82
+ if (managedCoverage === TURN_COVERAGE.NONE) return TURN_COVERAGE.NONE;
83
+ if (managedCoverage === TURN_COVERAGE.PARTIAL) {
84
+ return TURN_COVERAGE.PARTIAL;
85
+ }
72
86
  const sideEffects = Boolean(
73
87
  flags.ranShell ||
74
88
  flags.spawnedExternalProcess ||
@@ -76,10 +90,23 @@ export function computeCoverage(flags = {}) {
76
90
  );
77
91
  if (sideEffects) return TURN_COVERAGE.PARTIAL;
78
92
  if (!flags.mutatedFiles) return TURN_COVERAGE.FULL;
79
- if (flags.hasFileCheckpoint) return TURN_COVERAGE.FULL;
93
+ if (managedCoverage === TURN_COVERAGE.FULL || flags.hasFileCheckpoint) {
94
+ return TURN_COVERAGE.FULL;
95
+ }
80
96
  return TURN_COVERAGE.NONE;
81
97
  }
82
98
 
99
+ function mergeManagedCoverage(current, next) {
100
+ const rank = {
101
+ [TURN_COVERAGE.FULL]: 2,
102
+ [TURN_COVERAGE.PARTIAL]: 1,
103
+ [TURN_COVERAGE.NONE]: 0,
104
+ };
105
+ if (!(next in rank)) return current ?? null;
106
+ if (!(current in rank)) return next;
107
+ return rank[next] < rank[current] ? next : current;
108
+ }
109
+
83
110
  function emptyRecord(turnId, conversationOffset) {
84
111
  return {
85
112
  turnId,
@@ -92,6 +119,7 @@ function emptyRecord(turnId, conversationOffset) {
92
119
  permissionDecisionIds: [],
93
120
  childAgentIds: [],
94
121
  childBindings: [],
122
+ managedCheckpoints: [],
95
123
  worktreeId: null,
96
124
  // side-effect flags → coverage (kept internal; surfaced via `coverage`)
97
125
  _flags: {
@@ -100,6 +128,7 @@ function emptyRecord(turnId, conversationOffset) {
100
128
  userEditedDuringTurn: false,
101
129
  mutatedFiles: false,
102
130
  hasFileCheckpoint: false,
131
+ managedCheckpointCoverage: null,
103
132
  },
104
133
  };
105
134
  }
@@ -206,6 +235,62 @@ export class TurnBindingLog {
206
235
  return this;
207
236
  }
208
237
 
238
+ /**
239
+ * Bind durable Process Broker workspace-transaction evidence without
240
+ * overloading `fileCheckpointId` (which belongs to the legacy git rewind
241
+ * engine). Multiple tool calls may settle in one turn, so the worst coverage
242
+ * verdict governs the turn.
243
+ */
244
+ recordManagedCheckpoint(turnId, checkpoint = {}) {
245
+ const coverage =
246
+ checkpoint.coverage === TURN_COVERAGE.FULL ||
247
+ checkpoint.coverage === TURN_COVERAGE.PARTIAL ||
248
+ checkpoint.coverage === TURN_COVERAGE.NONE
249
+ ? checkpoint.coverage
250
+ : TURN_COVERAGE.NONE;
251
+ const normalized = {
252
+ toolUseId:
253
+ checkpoint.toolUseId == null ? null : String(checkpoint.toolUseId),
254
+ tool: checkpoint.tool == null ? null : String(checkpoint.tool),
255
+ transactionId:
256
+ checkpoint.transactionId == null
257
+ ? null
258
+ : String(checkpoint.transactionId),
259
+ checkpointId:
260
+ checkpoint.checkpointId == null
261
+ ? null
262
+ : String(checkpoint.checkpointId),
263
+ evidenceDigest:
264
+ checkpoint.evidenceDigest == null
265
+ ? null
266
+ : String(checkpoint.evidenceDigest),
267
+ phase: checkpoint.phase == null ? null : String(checkpoint.phase),
268
+ coverage,
269
+ fileCoverage:
270
+ checkpoint.fileCoverage === TURN_COVERAGE.FULL ||
271
+ checkpoint.fileCoverage === TURN_COVERAGE.PARTIAL ||
272
+ checkpoint.fileCoverage === TURN_COVERAGE.NONE
273
+ ? checkpoint.fileCoverage
274
+ : TURN_COVERAGE.NONE,
275
+ };
276
+ const rec = this._rec(turnId);
277
+ const existingIndex = rec.managedCheckpoints.findIndex(
278
+ (item) =>
279
+ (normalized.transactionId &&
280
+ item.transactionId === normalized.transactionId) ||
281
+ (!normalized.transactionId &&
282
+ normalized.toolUseId &&
283
+ item.toolUseId === normalized.toolUseId),
284
+ );
285
+ if (existingIndex === -1) rec.managedCheckpoints.push(normalized);
286
+ else rec.managedCheckpoints[existingIndex] = normalized;
287
+ rec._flags.managedCheckpointCoverage = mergeManagedCoverage(
288
+ rec._flags.managedCheckpointCoverage,
289
+ coverage,
290
+ );
291
+ return this;
292
+ }
293
+
209
294
  setWorktree(turnId, worktreeId) {
210
295
  this._rec(turnId).worktreeId =
211
296
  worktreeId == null ? null : String(worktreeId);
@@ -271,6 +356,13 @@ export class TurnBindingLog {
271
356
  checkpointIds: [...binding.checkpointIds],
272
357
  toolUseIds: [...binding.toolUseIds],
273
358
  })),
359
+ ...(rec.managedCheckpoints.length > 0
360
+ ? {
361
+ managedCheckpoints: rec.managedCheckpoints.map((checkpoint) => ({
362
+ ...checkpoint,
363
+ })),
364
+ }
365
+ : {}),
274
366
  worktreeId: rec.worktreeId,
275
367
  coverage: computeCoverage(rec._flags),
276
368
  };
@@ -280,7 +372,13 @@ export class TurnBindingLog {
280
372
  return {
281
373
  turns: this._order.map((id) => {
282
374
  const r = this._turns.get(id);
283
- return { ...r, _flags: { ...r._flags } };
375
+ return {
376
+ ...r,
377
+ managedCheckpoints: r.managedCheckpoints.map((checkpoint) => ({
378
+ ...checkpoint,
379
+ })),
380
+ _flags: { ...r._flags },
381
+ };
284
382
  }),
285
383
  };
286
384
  }
@@ -316,6 +414,39 @@ export class TurnBindingLog {
316
414
  worktreePath: binding.worktreePath ?? null,
317
415
  }))
318
416
  : [],
417
+ managedCheckpoints: Array.isArray(r.managedCheckpoints)
418
+ ? r.managedCheckpoints.map((checkpoint) => ({
419
+ toolUseId:
420
+ checkpoint?.toolUseId == null
421
+ ? null
422
+ : String(checkpoint.toolUseId),
423
+ tool: checkpoint?.tool == null ? null : String(checkpoint.tool),
424
+ transactionId:
425
+ checkpoint?.transactionId == null
426
+ ? null
427
+ : String(checkpoint.transactionId),
428
+ checkpointId:
429
+ checkpoint?.checkpointId == null
430
+ ? null
431
+ : String(checkpoint.checkpointId),
432
+ evidenceDigest:
433
+ checkpoint?.evidenceDigest == null
434
+ ? null
435
+ : String(checkpoint.evidenceDigest),
436
+ phase:
437
+ checkpoint?.phase == null ? null : String(checkpoint.phase),
438
+ coverage:
439
+ checkpoint?.coverage === TURN_COVERAGE.FULL ||
440
+ checkpoint?.coverage === TURN_COVERAGE.PARTIAL
441
+ ? checkpoint.coverage
442
+ : TURN_COVERAGE.NONE,
443
+ fileCoverage:
444
+ checkpoint?.fileCoverage === TURN_COVERAGE.FULL ||
445
+ checkpoint?.fileCoverage === TURN_COVERAGE.PARTIAL
446
+ ? checkpoint.fileCoverage
447
+ : TURN_COVERAGE.NONE,
448
+ }))
449
+ : [],
319
450
  worktreeId: r.worktreeId ?? null,
320
451
  _flags: { ...emptyRecord(id)._flags, ...(r._flags || {}) },
321
452
  });
@@ -537,6 +668,33 @@ export function createTurnBindingFeed({
537
668
  dirty = true;
538
669
  }
539
670
  break;
671
+ case "managed-checkpoint-settled":
672
+ log.recordManagedCheckpoint(turnId, {
673
+ toolUseId: event.tool_use_id,
674
+ tool: event.tool,
675
+ transactionId: event.transaction_id,
676
+ checkpointId: event.id,
677
+ evidenceDigest: event.evidence_digest,
678
+ phase: event.phase,
679
+ coverage: event.coverage,
680
+ fileCoverage: event.file_coverage,
681
+ });
682
+ dirty = true;
683
+ break;
684
+ case "managed-checkpoint-error":
685
+ if (event.phase !== "prepare" && event.recovery_required === true) {
686
+ log.recordManagedCheckpoint(turnId, {
687
+ toolUseId: event.tool_use_id,
688
+ tool: event.tool,
689
+ transactionId: event.transaction_id,
690
+ checkpointId: event.checkpoint_id,
691
+ phase: event.phase || "settle",
692
+ coverage: TURN_COVERAGE.NONE,
693
+ fileCoverage: TURN_COVERAGE.NONE,
694
+ });
695
+ dirty = true;
696
+ }
697
+ break;
540
698
  case "tool-executing": {
541
699
  const callId =
542
700
  event.tool_use_id == null