chainlesschain 0.163.2 → 0.163.4

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 (222) hide show
  1. package/package.json +3 -2
  2. package/scripts/command-lifecycle-report.mjs +330 -38
  3. package/src/assets/web-panel/assets/{AIOps-DnGsmsqJ.js → AIOps-DB5X7qqV.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CrO6fcQ7.js → ActionButton-DxHRtKLk.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-DsBdOaP2.js → Analytics-DIH5QrxT.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-DmQslKUB.js → AppLayout-uu0oxVz7.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-BvyrT_QQ.js → Artifacts-BNSMiW9Y.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-prndoLiC.js → Audit-DcAzB4_x.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-DOTFyA55.js → BackgroundAgents-ClSsvjIc.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-9zOnPp_8.js → Backup-BMciY0js.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-C4PC6sAG.js → BaseInput-Hn3b57Sh.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-BBAF-W9v.js → Chat-CoSlC53V.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CWhvvloO.js → ChatBubbleRenderer-CFevJ3So.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-BI9iculd.js → Checkbox-B4L4Eu4w.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-3DP9BCKb.js → Codegen-C77FgRxF.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-df-2TnJX.js → Col-BrLtJLH3.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-CHs8gG_A.js → Community-Be7pXXhA.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-CDfF6q7V.js → Compact-DuGPecjo.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-CthrDnO7.js → Compliance-CzS66mjP.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-HjIqsWJw.js → Cowork-CEhjCoyK.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-BKc196fb.js → Cron-D7ir8Vja.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-CJnjdc8W.js → Crosschain-BcXmuLxf.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-BHUy-8Q2.js → DID-Cto1dtBy.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-QDB7sKeY.js → Dashboard-B7KjgwOX.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-DStcVK11.js → Dropdown-COMqxijg.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-BwSUskMJ.js → EmailListRenderer-BZrya-nf.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DgF_kurT.js → FamilyGuardDashboard-AmVeMq0J.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-k8wvv-wN.js → Federation-kZU8c-27.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-C11qoq2r.js → FormItemContext-sOV22v2-.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-BIIpr9kg.js → GenericCardRenderer-BUuLtGCz.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-BlA1exTe.js → Git-K-ZxsLsH.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-CYa1ZYM4.js → Governance-D3OEnawI.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-6_kKVIt6.js → Inference-Bll0gRmP.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-CB4AwxYy.js → KnowledgeGraph-DBXwjr53.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-xf7nHFAe.js → Logs-CEzPwEfP.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-Bf3DEqP6.js → MarkdownRenderer-CLmCbHx6.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-aPg8e4Jt.js → Marketplace-BYyRyOPz.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-C-TNlVUf.js → McpTools-DVu8w_rM.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-DMTPIpGa.js → Memory-tenuZ_hO.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-BxyvveRq.js → MobileBridge-Bxgy4ozS.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-DyPCrAyQ.js → MobileProjects-Dgx1D6sc.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-BdhqNXp6.js → Mtc-CRfJ8r8j.js} +5 -5
  43. package/src/assets/web-panel/assets/{MtcAudit-Cz-HFxzW.js → MtcAudit-BBJq4Aiz.js} +5 -5
  44. package/src/assets/web-panel/assets/{Multisig-CIUaFNsx.js → Multisig-B3ii9lai.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-CWe6-J17.js → NLProgramming-FIi-Cubv.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-vYAKeviU.js → Notes-DIq8MPf1.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-DQL9-2d7.js → NotificationSettings-BFOD9eGd.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-DnXj1jBn.js → OrderTableRenderer--41u7vre.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-B3spU8AN.js → Organization-B_0AWztW.js} +2 -2
  50. package/src/assets/web-panel/assets/{Overflow-DgEwo7vm.js → Overflow-B6I7TuXv.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-hujMRH8X.js → P2P-Cdf6qens.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-C_qhdg9h.js → PdhVaultBrowser-B_0FcQJY.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-vp7LMcSf.js → Permissions-6nFBjsQp.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-Dxr5Hgnq.js → PersonalDataHub-nQYV7xpn.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-DEPZqqH4.js → Pipeline-CBVod5jm.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-DTpDiKPe.js → Privacy-D52jW1Nt.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-jDJsRWmF.js → ProjectInit-Bdx3BYhf.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-Dhor5RWH.js → ProjectSettings-b7xr4DUa.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-DhfifCKx.js +1 -0
  60. package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +1 -0
  61. package/src/assets/web-panel/assets/{QrScannerModal-CjIw-wIb.js → QrScannerModal-FQKSmc3e.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-DX6jcqoi.js → QuickAsk-CSTrhzcF.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-DopXkhar.js → Recommend-CxMABOUh.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-BLvqJE9c.js → RemoteSession-Cv2T8yRa.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-DhRj2Me7.js → Reputation-CE1k4Vvu.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DQ53vuPq.js → Row-BNgNcAV_.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-BslvwiBD.js → RssFeed-5ZiBZ3ph.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-DJKBDNKZ.js → Search-mDOIF1UV.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-COhpxHER.js → Security-CtuCTC-e.js} +3 -3
  70. package/src/assets/web-panel/assets/{Services-dbulD6cI.js → Services-DxFO2Zg7.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-ciIVNfhC.js → Skeleton-CKmKkH9y.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-AiRTkTCk.js → Skills-Bmzl9qkk.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-DDMcWYBc.js → Sla-9RqaWHOC.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-BPxWbpXO.js → SpeechSettings-CGdzFIYw.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-BoEOXCyg.js → SyncSettings-CvNuCeBT.js} +2 -2
  76. package/src/assets/web-panel/assets/Tasks-B_sLdcnw.js +1 -0
  77. package/src/assets/web-panel/assets/{Templates-UowY8Ixe.js → Templates-D9_YijdK.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-DauSLQXN.js → Tenant-Cuc7BUSP.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-DBj7Jneq.js → Terminal-6VYRnJgd.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-UMrPwoZC.js → TimelineRenderer-GtK8xd8Y.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-4ea96-bn.js → Tokens-BZ94Lt5W.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger--ISCqt4b.js → Trigger-DEcZgDaX.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-D4GVimRJ.js → Trust-CC3z9THe.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-Ci-nXRQc.js → UkeySign-Cvtw8cV3.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-Cjk7mKPw.js → VideoEditing-CL4e6Bn3.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-BgOLOO3Z.js → Wallet-ycEemP27.js} +2 -2
  87. package/src/assets/web-panel/assets/{WebAuthn-CCX0K29i.js → WebAuthn-DgZ4X_aw.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-Ch3zgIsE.js → WorkflowEditor-D0qMhSv7.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-DKOU_2Vb.js → chat-DsAg01Zn.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-6OVo4u3C.js → colors-B0oqAhrQ.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-BH78Hlsq.js → compact-item-CmX0Rabz.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-CFRpSbbC.js → createContext-Be4cn1Ub.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DNQhgcVi.js → hasIn-D5v12kVq.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BkN3iTTp.js → index-0eWLkQKT.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CkNEMANR.js → index-301ogenv.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-D3RtTT4K.js → index-5LGY0JbG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-dDkoL4Pe.js → index-7VKDn-t1.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BHa4WyTZ.js → index-8WvYGnON.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CaYqY35Z.js → index-B1YGjOWC.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-bKm3TUDp.js → index-B3mUHuVc.js} +1 -1
  102. package/src/assets/web-panel/assets/{index--rq90rL9.js → index-BA4oulaP.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DaDgdJYj.js → index-BBREnn1V.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B_Z-YoxX.js → index-BBvyHguy.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CkqHgG-b.js → index-BKmsS2jj.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CrcLHN2F.js → index-BRIqLy1m.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BaVE6E8y.js → index-BXGSbelg.js} +3 -3
  108. package/src/assets/web-panel/assets/{index-BYrnm5Ob.js → index-BaE0Ui07.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BDSR7ecX.js → index-BoAIAZ5C.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CNuCHHQT.js → index-BqltWojQ.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Eag8e_mA.js → index-CGiIyTZw.js} +1 -1
  112. package/src/assets/web-panel/assets/index-CKB0vbJU.js +1 -0
  113. package/src/assets/web-panel/assets/{index-BepBTpa8.js → index-CZ2wUEgr.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-HNGGVF6o.js → index-CZpfzcuy.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-AuBhLqOm.js → index-Ckrk3pIV.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-7TC3S8SN.js → index-CqoCZ5vR.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CFPJ8yzF.js → index-CyWBir7N.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Bb6kDrUx.js → index-CyyGTMKd.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DKN2noIF.js → index-D0BSkxYQ.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Du0rnksC.js → index-D5YVopyr.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BxRy5LPR.js → index-DKUBj36_.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-sm7W8AXy.js → index-DM724NgU.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BujsOZfc.js → index-DQRsQen0.js} +1 -1
  124. package/src/assets/web-panel/assets/index-DgoXmwqB.js +1 -0
  125. package/src/assets/web-panel/assets/{index-Y9MJv28e.js → index-DikJ0Qqe.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-caye7AOE.js → index-Dn2dSdXx.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-cdf7fd7d.js → index-DyUaFWEk.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BnnXQot_.js → index-Hfe_dEGx.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DvHGtNnF.js → index-_JjRLSD0.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-nbfHl1oV.js → index-frSojpHj.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-Cu3L4r5l.js → index-jK3ax6Z6.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D84id2Y4.js → index-qMuZ-NRR.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-hPY0fFrd.js → index-q_fQ94SE.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-BZ9w6jPL.js → initDefaultProps-D4VdhJ9_.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BnwUiOho.js → motion-CZJA0MQW.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-ByUKJnNm.js → move-BqEs7Sb1.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-V7yK1KEl.js → mtc-parser-BpHEtsu1.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-BEXBBt4j.js → omit-BW94tY9W.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-qDm_46Gi.js → pickAttrs-BnBjPWP9.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-BIbhEo_0.js → placementArrow-uUkjQ_hu.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-NbPbDgA_.js → responsiveObserve-B-klUqXK.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-BnUWglZ0.js → slide-CvY1cNAs.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-BX8_wZI5.js → statusUtils-Ix5sEDT2.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-BV4OycQX.js → styleChecker-C8qcXOne.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-Od_H5llY.js → useFlexGapSupport-KNF6Kj8f.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-BWI-PyiX.js → useFs-DGJr345n.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-BqMuhG3g.js → usePersonalDataHub-BboN99rI.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-DNd23z6f.js → vnode-Bgt8Vgte.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-Cjaxnm7l.js → zoom-BhJtErgt.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +2 -2
  152. package/src/commands/agenda.js +6 -5
  153. package/src/commands/agent.js +237 -75
  154. package/src/commands/agents.js +6 -0
  155. package/src/commands/artifacts.js +98 -0
  156. package/src/commands/background-session.js +11 -1
  157. package/src/commands/mcp.js +218 -31
  158. package/src/commands/review.js +221 -22
  159. package/src/commands/session-mcp-recovery.js +5 -5
  160. package/src/data/changelog.json +27 -1
  161. package/src/gateways/ws/ws-agent-handler.js +492 -14
  162. package/src/harness/jsonl-session-store.js +39 -1
  163. package/src/harness/mcp-client.js +414 -71
  164. package/src/harness/prompt-compressor.js +24 -0
  165. package/src/harness/provider-backed-compaction.js +205 -0
  166. package/src/harness/worktree-isolator.js +26 -3
  167. package/src/lib/agent-worktree.js +41 -2
  168. package/src/lib/app-preview.js +3 -0
  169. package/src/lib/background-agent-supervisor.js +790 -186
  170. package/src/lib/background-command-argv.js +318 -0
  171. package/src/lib/background-launch-profile.js +1 -0
  172. package/src/lib/background-phase-reporter.js +22 -2
  173. package/src/lib/background-worktree-policy.js +109 -0
  174. package/src/lib/command-lifecycle-report.js +1340 -145
  175. package/src/lib/cowork-mcp-tools.js +51 -6
  176. package/src/lib/cowork-task-runner.js +1 -0
  177. package/src/lib/delivery-coordinator.js +30 -5
  178. package/src/lib/delivery-production-adapter.js +1777 -0
  179. package/src/lib/delivery-production-runner.js +240 -0
  180. package/src/lib/durable-security-store.js +29 -17
  181. package/src/lib/execution-trace/trace-context.cjs +179 -0
  182. package/src/lib/execution-trace/trace-context.js +8 -175
  183. package/src/lib/mcp-call-ledger-store.js +58 -6
  184. package/src/lib/mcp-headers-helper.js +76 -8
  185. package/src/lib/mcp-host-recovery-runtime.js +2 -0
  186. package/src/lib/mcp-ledger-recovery-admission.js +39 -3
  187. package/src/lib/mcp-recovery-adjudication.js +256 -19
  188. package/src/lib/mcp-sandbox-policy.js +184 -0
  189. package/src/lib/mcp-stdio-capsule-builder-worker.cjs +495 -0
  190. package/src/lib/mcp-stdio-executable-identity.js +33 -2
  191. package/src/lib/mcp-stdio-execution-authority.js +163 -0
  192. package/src/lib/mcp-stdio-immutable-vfs-resolver.cjs +830 -0
  193. package/src/lib/mcp-stdio-package-materialization.js +721 -333
  194. package/src/lib/mcp-stdio-workspace-authority.js +252 -0
  195. package/src/lib/observability/index.js +2 -0
  196. package/src/lib/observability/otlp-exporter.js +2 -2
  197. package/src/lib/packer/pack-update-applier.js +8 -1
  198. package/src/lib/packer/pkg-config-generator.js +34 -0
  199. package/src/lib/plugin-runtime/sandbox-policy.js +42 -2
  200. package/src/lib/process-execution-broker/index.js +213 -109
  201. package/src/lib/process-execution-broker/platform-sandbox.js +1024 -169
  202. package/src/lib/process-tree-termination.js +17 -1
  203. package/src/lib/scheduler-kernel/contract.js +315 -0
  204. package/src/lib/scheduler-kernel/store.js +1345 -0
  205. package/src/lib/session-host-lease.js +403 -30
  206. package/src/lib/session-persistence-failure.js +4 -1
  207. package/src/lib/skill-mcp.js +65 -4
  208. package/src/lib/sub-agent-context.js +8 -0
  209. package/src/lib/web-fetch.js +4 -4
  210. package/src/repl/agent-repl.js +285 -38
  211. package/src/runtime/agent-core.js +1044 -90
  212. package/src/runtime/agent-runtime.js +61 -8
  213. package/src/runtime/headless-runner.js +260 -166
  214. package/src/runtime/headless-stream.js +254 -25
  215. package/src/runtime/mcp-config.js +235 -28
  216. package/src/workers/background-agent-worker.js +492 -71
  217. package/src/assets/web-panel/assets/Projects-08xUDZGt.js +0 -1
  218. package/src/assets/web-panel/assets/Providers-BGs47s1a.js +0 -1
  219. package/src/assets/web-panel/assets/Tasks-CEYFJwmr.js +0 -1
  220. package/src/assets/web-panel/assets/devWarning-DEMblAIm.js +0 -1
  221. package/src/assets/web-panel/assets/index-BTvq-36D.js +0 -1
  222. package/src/assets/web-panel/assets/index-hUpvk6Jl.js +0 -1
@@ -34,12 +34,17 @@ import {
34
34
  import { createWsApprovalGate } from "./ws-approval-gate.js";
35
35
  import { createSessionMcpLedgerSink } from "../../lib/mcp-call-ledger-store.js";
36
36
  import { createMcpHostRecoveryRuntime } from "../../lib/mcp-host-recovery-runtime.js";
37
+ import { compactConversationWithProvider } from "../../harness/provider-backed-compaction.js";
38
+ import { projectCanonicalResumeMessages } from "../../lib/session-message-provenance.js";
39
+ import { feature } from "../../lib/feature-flags.js";
37
40
  import {
41
+ appendCompactEventIfMessagesMatch,
38
42
  claimWsTurnIfHead,
39
43
  computeWsTurnInputDigest,
40
44
  createWsTurnClaimId,
41
45
  normalizeWsTurnRequestId,
42
46
  readVerifiedWsTurnState,
47
+ readVerifiedMessages,
43
48
  settleWsTurnClaim,
44
49
  } from "../../harness/jsonl-session-store.js";
45
50
 
@@ -78,6 +83,8 @@ export class WSAgentHandler {
78
83
  db,
79
84
  approvalGate,
80
85
  agentLoop: runAgentLoop,
86
+ compactionLlmQuery,
87
+ compactionChatFn,
81
88
  canonicalSessionStore,
82
89
  sessionHostLease = null,
83
90
  }) {
@@ -122,12 +129,22 @@ export class WSAgentHandler {
122
129
  this._mcpRecoveryRevision = null;
123
130
  this._mcpRecoveryClient = null;
124
131
  this._agentLoop = runAgentLoop || agentLoop;
132
+ this._compactionLlmQuery = compactionLlmQuery || null;
133
+ this._compactionSettlementBlock = null;
134
+ this._compactionChatFn =
135
+ compactionChatFn ||
136
+ (async (...args) => {
137
+ const { chatWithTools } = await import("../../runtime/agent-core.js");
138
+ return chatWithTools(...args);
139
+ });
125
140
  this._canonicalSessionStore = {
141
+ appendCompactEventIfMessagesMatch,
126
142
  claimWsTurnIfHead,
127
143
  computeWsTurnInputDigest,
128
144
  createWsTurnClaimId,
129
145
  normalizeWsTurnRequestId,
130
146
  readVerifiedWsTurnState,
147
+ readVerifiedMessages,
131
148
  settleWsTurnClaim,
132
149
  ...(canonicalSessionStore || {}),
133
150
  };
@@ -171,9 +188,56 @@ export class WSAgentHandler {
171
188
  return this.session.messages;
172
189
  }
173
190
 
191
+ _latchCompactionSettlement({
192
+ code,
193
+ reason,
194
+ commitState = "unknown",
195
+ usageOutcome = null,
196
+ }) {
197
+ this._compactionSettlementBlock = Object.freeze({
198
+ code: code || "CC_COMPACTION_SETTLEMENT_UNKNOWN",
199
+ reason: reason || "canonical_compaction_settlement_failed",
200
+ commitState,
201
+ ...(usageOutcome ? { usageOutcome } : {}),
202
+ });
203
+ return this._compactionSettlementBlock;
204
+ }
205
+
206
+ _reconcileCompactionSettlementBlock() {
207
+ if (
208
+ this._compactionSettlementBlock?.commitState !== "provider-usage-unknown"
209
+ ) {
210
+ this._compactionSettlementBlock = null;
211
+ }
212
+ }
213
+
214
+ _emitCompactionUsage(requestId, usageEvent) {
215
+ if (!usageEvent) return;
216
+ this.interaction.emit("token-usage", {
217
+ requestId,
218
+ ...usageEvent,
219
+ });
220
+ }
221
+
222
+ _emitCompactionUsageUnknown(requestId, usageUnknownEvent) {
223
+ const block = this._latchCompactionSettlement({
224
+ code: "CC_COMPACTION_USAGE_UNKNOWN",
225
+ reason: usageUnknownEvent?.reason || "provider_transport_outcome_unknown",
226
+ commitState: "provider-usage-unknown",
227
+ usageOutcome: "unknown",
228
+ });
229
+ this.interaction.emit("compaction-usage-unknown", {
230
+ requestId,
231
+ ...usageUnknownEvent,
232
+ code: block.code,
233
+ });
234
+ return block;
235
+ }
236
+
174
237
  _canonicalTurnFromAuthority(state, requestId, userMessage, inputDigest) {
175
238
  const status = state?.status || (state?.turn ? "completed" : "none");
176
239
  this._applyCanonicalMessages(state?.messages || []);
240
+ this._reconcileCompactionSettlementBlock();
177
241
  if (status === "completed" && state.turn) {
178
242
  return {
179
243
  requestId,
@@ -210,6 +274,193 @@ export class WSAgentHandler {
210
274
  return null;
211
275
  }
212
276
 
277
+ async _compactCanonicalHistoryBeforeClaim(requestId) {
278
+ const { session } = this;
279
+ if (
280
+ session.autoCompact === false ||
281
+ !feature("PROMPT_COMPRESSOR") ||
282
+ this._compactionSettlementBlock?.commitState === "provider-usage-unknown"
283
+ ) {
284
+ return true;
285
+ }
286
+
287
+ const before = session.messages.length;
288
+ const expectedMessages = [...session.messages];
289
+ let result;
290
+ try {
291
+ this._sessionHostLease?.assert?.();
292
+ result = await compactConversationWithProvider(session.messages, {
293
+ provider: session.provider,
294
+ model: session.model,
295
+ baseUrl: session.baseUrl,
296
+ apiKey: session.apiKey,
297
+ signal: this._sessionHostLease?.signal,
298
+ onlyIfNeeded: true,
299
+ preserveCompletedExchange: true,
300
+ llmQuery: this._compactionLlmQuery,
301
+ chatFn: this._compactionChatFn,
302
+ chatOptions: {
303
+ cwd: session.projectRoot,
304
+ sessionId: session.id,
305
+ },
306
+ maxMessages: session.compactionMaxMessages,
307
+ maxTokens: session.compactionMaxTokens,
308
+ maxOutputTokens: session.compactionMaxOutputTokens,
309
+ summaryInputMaxChars: session.compactionInputMaxChars,
310
+ });
311
+ this._sessionHostLease?.assert?.();
312
+ } catch (error) {
313
+ if (isAbortError(error) || this._sessionHostLease?.signal?.aborted) {
314
+ throw error;
315
+ }
316
+ const block = this._latchCompactionSettlement({
317
+ code: error?.code || "CC_COMPACTION_FAILED",
318
+ reason: `compaction_failed:${String(error?.message || error).slice(0, 240)}`,
319
+ commitState: "provider-outcome-unknown",
320
+ });
321
+ this.interaction.emit("compaction-degraded", {
322
+ requestId,
323
+ reason: block.reason,
324
+ summaryMode: "none",
325
+ code: block.code,
326
+ });
327
+ this.interaction.emit("error", {
328
+ requestId,
329
+ code: block.code,
330
+ message: "Canonical history could not be compacted before turn claim",
331
+ });
332
+ return false;
333
+ }
334
+
335
+ const {
336
+ messages: compacted,
337
+ stats,
338
+ degradedEvent,
339
+ usageEvent,
340
+ usageUnknownEvent,
341
+ } = result;
342
+ if (degradedEvent) {
343
+ this.interaction.emit("compaction-degraded", {
344
+ requestId,
345
+ ...degradedEvent,
346
+ });
347
+ }
348
+ if (usageUnknownEvent) {
349
+ const block = this._emitCompactionUsageUnknown(
350
+ requestId,
351
+ usageUnknownEvent,
352
+ );
353
+ this.interaction.emit("error", {
354
+ requestId,
355
+ code: block.code,
356
+ message:
357
+ "Compaction provider usage is unknown; the paid compaction is not retried",
358
+ });
359
+ return false;
360
+ }
361
+
362
+ const messagesChanged =
363
+ session.messages.length !== expectedMessages.length ||
364
+ session.messages.some(
365
+ (message, index) => message !== expectedMessages[index],
366
+ );
367
+ const shouldSettle = stats?.strategy !== "none" && stats?.saved > 0;
368
+ let settlementError = null;
369
+ if (!messagesChanged && shouldSettle) {
370
+ try {
371
+ const settlement =
372
+ this._canonicalSessionStore.appendCompactEventIfMessagesMatch(
373
+ session.id,
374
+ {
375
+ ...stats,
376
+ trigger: "auto",
377
+ messages: projectCanonicalResumeMessages(compacted, {
378
+ strict: true,
379
+ }),
380
+ },
381
+ expectedMessages,
382
+ );
383
+ if (settlement && typeof settlement.then === "function") {
384
+ void Promise.resolve(settlement).catch(() => {});
385
+ const error = new TypeError(
386
+ "Canonical compact settlement must be synchronous",
387
+ );
388
+ error.code = "CC_COMPACTION_SETTLEMENT_ASYNC";
389
+ throw error;
390
+ }
391
+ } catch (error) {
392
+ settlementError = error;
393
+ }
394
+ }
395
+
396
+ this._emitCompactionUsage(requestId, usageEvent);
397
+ if (messagesChanged || settlementError) {
398
+ const stale = settlementError?.code === "SESSION_REVISION_STALE";
399
+ let reconciled = false;
400
+ if (stale) {
401
+ try {
402
+ const authoritativeMessages =
403
+ this._canonicalSessionStore.readVerifiedMessages(session.id);
404
+ if (!Array.isArray(authoritativeMessages)) {
405
+ throw new TypeError(
406
+ "Canonical message refresh must be synchronous",
407
+ );
408
+ }
409
+ this._applyCanonicalMessages(authoritativeMessages);
410
+ this._reconcileCompactionSettlementBlock();
411
+ reconciled = true;
412
+ } catch {
413
+ // Latch below until a later verified canonical refresh succeeds.
414
+ }
415
+ }
416
+ const block =
417
+ reconciled && stale
418
+ ? {
419
+ code: "CC_COMPACTION_REVISION_STALE",
420
+ reason: "session_messages_changed_during_compaction",
421
+ commitState: "reconciled-stale",
422
+ }
423
+ : this._latchCompactionSettlement({
424
+ code:
425
+ settlementError?.code ||
426
+ (messagesChanged
427
+ ? "CC_COMPACTION_LOCAL_STATE_CHANGED"
428
+ : "CC_COMPACTION_SETTLEMENT_UNKNOWN"),
429
+ reason:
430
+ messagesChanged || stale
431
+ ? "session_messages_changed_during_compaction"
432
+ : "canonical_compaction_settlement_failed",
433
+ commitState: "unknown",
434
+ });
435
+ this.interaction.emit("compaction-degraded", {
436
+ requestId,
437
+ reason: block.reason,
438
+ summaryMode: "none",
439
+ code: block.code,
440
+ });
441
+ this.interaction.emit("error", {
442
+ requestId,
443
+ code: block.code,
444
+ message:
445
+ "Canonical compaction did not settle; the paid compaction is not retried",
446
+ });
447
+ return false;
448
+ }
449
+
450
+ if (shouldSettle) {
451
+ session.messages.length = 0;
452
+ session.messages.push(...compacted);
453
+ this.interaction.emit("compaction", {
454
+ requestId,
455
+ stats,
456
+ trigger: "auto",
457
+ messagesBefore: before,
458
+ messagesAfter: session.messages.length,
459
+ });
460
+ }
461
+ return true;
462
+ }
463
+
213
464
  _claimCanonicalTurn(userMessage, requestId) {
214
465
  const inputDigest =
215
466
  this._canonicalSessionStore.computeWsTurnInputDigest(userMessage);
@@ -396,6 +647,7 @@ export class WSAgentHandler {
396
647
  sink: this._mcpRecoverySink,
397
648
  recovery: session.mcpLedgerRecovery || null,
398
649
  controller,
650
+ dispatchAdmission: this._sessionHostLease?.admitMcpDispatch || null,
399
651
  });
400
652
  this._mcpRecoveryClient = rawClient;
401
653
  this._mcpRecoveryRevision = revision;
@@ -509,7 +761,26 @@ export class WSAgentHandler {
509
761
  this._sessionHostLease?.assert?.();
510
762
 
511
763
  if (session.canonicalJsonlSession === true) {
512
- canonicalTurn = this._claimCanonicalTurn(userMessage, requestId);
764
+ const inputDigest =
765
+ this._canonicalSessionStore.computeWsTurnInputDigest(userMessage);
766
+ const preflightState =
767
+ this._canonicalSessionStore.readVerifiedWsTurnState(
768
+ session.id,
769
+ requestId,
770
+ { inputDigest },
771
+ );
772
+ canonicalTurn = this._canonicalTurnFromAuthority(
773
+ preflightState,
774
+ requestId,
775
+ userMessage,
776
+ inputDigest,
777
+ );
778
+ if (!canonicalTurn) {
779
+ const compacted =
780
+ await this._compactCanonicalHistoryBeforeClaim(requestId);
781
+ if (!compacted) return;
782
+ }
783
+ canonicalTurn ||= this._claimCanonicalTurn(userMessage, requestId);
513
784
  if (canonicalTurn.status === "completed") {
514
785
  this.interaction.emit("response-complete", {
515
786
  requestId,
@@ -601,6 +872,7 @@ export class WSAgentHandler {
601
872
  // headless and REPL sessions. For unknown/write/destructive effects a
602
873
  // failed prewrite blocks the network call inside agent-core.
603
874
  mcpCallLedger: mcpRecoveryRuntime.ledger,
875
+ mcpDispatchAdmission: this._sessionHostLease?.admitMcpDispatch || null,
604
876
  shellPolicyOverrides: session.shellPolicyOverrides || null,
605
877
  slotFiller,
606
878
  interaction: this.interaction,
@@ -608,6 +880,12 @@ export class WSAgentHandler {
608
880
  // P0 authority: null unless opted in (byte-identical default — agent
609
881
  // core already defaults `options.approvalGate || null`).
610
882
  approvalGate: await this._ensureApprovalGate(),
883
+ // Canonical WS history is compacted before ws_turn_prepare. Compaction
884
+ // inside agent-core would include the staged pending user, which is not
885
+ // part of the canonical message projection until turn settlement.
886
+ ...(session.canonicalJsonlSession === true
887
+ ? { autoCompact: false }
888
+ : {}),
611
889
  };
612
890
 
613
891
  // P0-2: crash-safe side-effect ledger for the IDE/bridge path — mirrors
@@ -718,6 +996,37 @@ export class WSAgentHandler {
718
996
  }
719
997
  break;
720
998
 
999
+ case "compaction-degraded":
1000
+ this.interaction.emit("compaction-degraded", {
1001
+ requestId,
1002
+ reason: event.reason,
1003
+ summaryMode: event.summaryMode,
1004
+ ...(event.code ? { code: event.code } : {}),
1005
+ stats: event.stats,
1006
+ });
1007
+ break;
1008
+
1009
+ case "compaction-usage-unknown":
1010
+ this._emitCompactionUsageUnknown(requestId, event);
1011
+ break;
1012
+
1013
+ case "compaction":
1014
+ this.interaction.emit("compaction", {
1015
+ requestId,
1016
+ stats: event.stats,
1017
+ });
1018
+ break;
1019
+
1020
+ case "token-usage":
1021
+ this.interaction.emit("token-usage", {
1022
+ requestId,
1023
+ provider: event.provider,
1024
+ model: event.model,
1025
+ usage: event.usage,
1026
+ ...(event.source ? { source: event.source } : {}),
1027
+ });
1028
+ break;
1029
+
721
1030
  case "response-complete":
722
1031
  if (session.canonicalJsonlSession === true) {
723
1032
  const settled = this._settleCanonicalSuccess(
@@ -983,25 +1292,194 @@ export class WSAgentHandler {
983
1292
  });
984
1293
  break;
985
1294
 
986
- case "/compact":
987
- if (session.contextEngine && session.messages.length > 5) {
988
- const compacted = session.contextEngine.smartCompact(
989
- session.messages,
990
- );
991
- session.messages.length = 0;
992
- session.messages.push(...compacted);
993
- } else if (session.messages.length > 5) {
994
- const systemMsg = session.messages[0];
995
- const recent = session.messages.slice(-4);
996
- session.messages.length = 0;
997
- session.messages.push(systemMsg, ...recent);
1295
+ case "/compact": {
1296
+ if (this._compactionSettlementBlock) {
1297
+ const block = this._compactionSettlementBlock;
1298
+ this.interaction.emit("compaction-degraded", {
1299
+ requestId,
1300
+ reason: block.reason,
1301
+ summaryMode: "none",
1302
+ code: block.code,
1303
+ });
1304
+ this.interaction.emit("command-response", {
1305
+ requestId,
1306
+ command: cmd,
1307
+ result: {
1308
+ messageCount: session.messages.length,
1309
+ error: block,
1310
+ },
1311
+ });
1312
+ break;
1313
+ }
1314
+
1315
+ const before = session.messages.length;
1316
+ const expectedMessages = [...session.messages];
1317
+ let result = null;
1318
+ let commandError = null;
1319
+ try {
1320
+ this._sessionHostLease?.assert?.();
1321
+ result = await compactConversationWithProvider(session.messages, {
1322
+ provider: session.provider,
1323
+ model: session.model,
1324
+ baseUrl: session.baseUrl,
1325
+ apiKey: session.apiKey,
1326
+ signal: this._sessionHostLease?.signal,
1327
+ force: true,
1328
+ preserveCompletedExchange: true,
1329
+ llmQuery: this._compactionLlmQuery,
1330
+ chatFn: this._compactionChatFn,
1331
+ chatOptions: {
1332
+ cwd: session.projectRoot,
1333
+ sessionId: session.id,
1334
+ },
1335
+ maxOutputTokens: session.compactionMaxOutputTokens,
1336
+ summaryInputMaxChars: session.compactionInputMaxChars,
1337
+ });
1338
+ this._sessionHostLease?.assert?.();
1339
+ } catch (error) {
1340
+ if (isAbortError(error) || this._sessionHostLease?.signal?.aborted) {
1341
+ throw error;
1342
+ }
1343
+ commandError = this._latchCompactionSettlement({
1344
+ code: error?.code || "CC_COMPACTION_FAILED",
1345
+ reason: `compaction_failed:${String(error?.message || error).slice(0, 240)}`,
1346
+ commitState: "provider-outcome-unknown",
1347
+ });
1348
+ this.interaction.emit("compaction-degraded", {
1349
+ requestId,
1350
+ reason: commandError.reason,
1351
+ summaryMode: "none",
1352
+ code: commandError.code,
1353
+ });
1354
+ }
1355
+ if (result) {
1356
+ const {
1357
+ messages,
1358
+ stats,
1359
+ degradedEvent,
1360
+ usageEvent,
1361
+ usageUnknownEvent,
1362
+ } = result;
1363
+ if (degradedEvent) {
1364
+ this.interaction.emit("compaction-degraded", {
1365
+ requestId,
1366
+ ...degradedEvent,
1367
+ });
1368
+ }
1369
+ if (usageUnknownEvent) {
1370
+ commandError = this._emitCompactionUsageUnknown(
1371
+ requestId,
1372
+ usageUnknownEvent,
1373
+ );
1374
+ }
1375
+ const messagesChanged =
1376
+ session.messages.length !== expectedMessages.length ||
1377
+ session.messages.some(
1378
+ (message, index) => message !== expectedMessages[index],
1379
+ );
1380
+ let settlementError = null;
1381
+ if (
1382
+ !commandError &&
1383
+ !messagesChanged &&
1384
+ session.canonicalJsonlSession === true
1385
+ ) {
1386
+ if (stats?.strategy !== "none") {
1387
+ try {
1388
+ const settlement =
1389
+ this._canonicalSessionStore.appendCompactEventIfMessagesMatch(
1390
+ session.id,
1391
+ {
1392
+ ...stats,
1393
+ trigger: "manual",
1394
+ messages: projectCanonicalResumeMessages(messages, {
1395
+ strict: true,
1396
+ }),
1397
+ },
1398
+ expectedMessages,
1399
+ );
1400
+ if (settlement && typeof settlement.then === "function") {
1401
+ void Promise.resolve(settlement).catch(() => {});
1402
+ const error = new TypeError(
1403
+ "Canonical compact settlement must be synchronous",
1404
+ );
1405
+ error.code = "CC_COMPACTION_SETTLEMENT_ASYNC";
1406
+ throw error;
1407
+ }
1408
+ } catch (error) {
1409
+ settlementError = error;
1410
+ }
1411
+ }
1412
+ }
1413
+
1414
+ if (!commandError && !messagesChanged && !settlementError) {
1415
+ session.messages.length = 0;
1416
+ session.messages.push(...messages);
1417
+ this.interaction.emit("compaction", {
1418
+ requestId,
1419
+ stats,
1420
+ messagesBefore: before,
1421
+ messagesAfter: session.messages.length,
1422
+ });
1423
+ } else if (!commandError) {
1424
+ const stale = settlementError?.code === "SESSION_REVISION_STALE";
1425
+ let reconciled = false;
1426
+ if (stale) {
1427
+ try {
1428
+ const authoritativeMessages =
1429
+ this._canonicalSessionStore.readVerifiedMessages(session.id);
1430
+ if (!Array.isArray(authoritativeMessages)) {
1431
+ throw new TypeError(
1432
+ "Canonical message refresh must be synchronous",
1433
+ );
1434
+ }
1435
+ this._applyCanonicalMessages(authoritativeMessages);
1436
+ this._reconcileCompactionSettlementBlock();
1437
+ reconciled = true;
1438
+ } catch {
1439
+ // Latch below until a later verified canonical refresh.
1440
+ }
1441
+ }
1442
+ commandError =
1443
+ reconciled && stale
1444
+ ? {
1445
+ code: "CC_COMPACTION_REVISION_STALE",
1446
+ reason: "session_messages_changed_during_compaction",
1447
+ commitState: "reconciled-stale",
1448
+ }
1449
+ : this._latchCompactionSettlement({
1450
+ code:
1451
+ settlementError?.code ||
1452
+ (messagesChanged
1453
+ ? "CC_COMPACTION_LOCAL_STATE_CHANGED"
1454
+ : "CC_COMPACTION_SETTLEMENT_UNKNOWN"),
1455
+ reason:
1456
+ messagesChanged || stale
1457
+ ? "session_messages_changed_during_compaction"
1458
+ : "canonical_compaction_settlement_failed",
1459
+ commitState: "unknown",
1460
+ });
1461
+ this.interaction.emit("compaction-degraded", {
1462
+ requestId,
1463
+ reason: commandError.reason,
1464
+ summaryMode: "none",
1465
+ code: commandError.code,
1466
+ });
1467
+ }
1468
+ // Provider usage is emitted exactly once even when the message CAS
1469
+ // rejects the candidate; the paid request already happened and is
1470
+ // never retried by this command.
1471
+ this._emitCompactionUsage(requestId, usageEvent);
998
1472
  }
999
1473
  this.interaction.emit("command-response", {
1000
1474
  requestId,
1001
1475
  command: cmd,
1002
- result: { messageCount: session.messages.length },
1476
+ result: {
1477
+ messageCount: session.messages.length,
1478
+ ...(commandError ? { error: commandError } : {}),
1479
+ },
1003
1480
  });
1004
1481
  break;
1482
+ }
1005
1483
 
1006
1484
  case "/task":
1007
1485
  if (arg === "clear") {
@@ -18,8 +18,9 @@ import fs, {
18
18
  readSync,
19
19
  writeSync,
20
20
  ftruncateSync,
21
+ fsyncSync,
21
22
  } from "node:fs";
22
- import { join, basename, resolve } from "node:path";
23
+ import { join, basename, dirname, resolve } from "node:path";
23
24
  import { createHash, randomUUID } from "node:crypto";
24
25
  import { TextDecoder } from "node:util";
25
26
  import { getHomeDir } from "../lib/paths.js";
@@ -83,6 +84,10 @@ let securedSessionsDirIdentity = null;
83
84
  // keeps production behavior and ordinary tests on the exact same append path.
84
85
  export const _sessionScaleFaultHooks = Object.seal({
85
86
  beforeTranscriptAppend: null,
87
+ beforeTranscriptFsync: null,
88
+ afterTranscriptFsync: null,
89
+ beforeTranscriptDirectoryFsync: null,
90
+ afterTranscriptDirectoryFsync: null,
86
91
  afterTranscriptAppend: null,
87
92
  afterForkCopy: null,
88
93
  afterForkLineage: null,
@@ -981,6 +986,39 @@ function appendTranscriptEventAtPath({
981
986
  wroteBytes = true;
982
987
  }
983
988
  appendCompleted = true;
989
+ // The sidecar and independent anti-rollback witness are published after
990
+ // this function returns. Make the transcript bytes durable first so a
991
+ // power loss cannot preserve a newer authority head while losing the event
992
+ // that head authenticates. A full-write fsync failure is outcome-unknown:
993
+ // the caller must verify the transcript and must not advance either anchor.
994
+ runSessionScaleFaultHook("beforeTranscriptFsync", payload);
995
+ fsyncSync(fd);
996
+ runSessionScaleFaultHook("afterTranscriptFsync", payload);
997
+
998
+ // fsync(file) does not durably publish a newly-created directory entry on
999
+ // POSIX. Flush the sessions directory before any sidecar/external witness
1000
+ // can advance. Windows does not expose a usable directory fsync through
1001
+ // Node; FlushFileBuffers on the newly-created file is the available host
1002
+ // durability boundary there.
1003
+ if (expectedState === null && process.platform !== "win32") {
1004
+ runSessionScaleFaultHook("beforeTranscriptDirectoryFsync", payload);
1005
+ let directoryDescriptor = null;
1006
+ try {
1007
+ const directoryFlags =
1008
+ fsConstants.O_RDONLY |
1009
+ Number(fsConstants.O_DIRECTORY ?? 0) |
1010
+ Number(fsConstants.O_NOFOLLOW ?? 0);
1011
+ directoryDescriptor = openSync(dirname(filePath), directoryFlags);
1012
+ const directoryStats = fstatSync(directoryDescriptor);
1013
+ if (!directoryStats.isDirectory()) {
1014
+ throw new Error("Session transcript parent is not a directory");
1015
+ }
1016
+ fsyncSync(directoryDescriptor);
1017
+ runSessionScaleFaultHook("afterTranscriptDirectoryFsync", payload);
1018
+ } finally {
1019
+ if (directoryDescriptor !== null) closeSync(directoryDescriptor);
1020
+ }
1021
+ }
984
1022
  runSessionScaleFaultHook("afterTranscriptAppend", payload);
985
1023
  const descriptorStats = fstatSync(fd, { bigint: true });
986
1024
  const descriptorState = physicalTranscriptStateFromStats(descriptorStats);