chainlesschain 0.162.198 → 0.162.200

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 (195) hide show
  1. package/bin/chainlesschain.js +14 -6
  2. package/package.json +4 -1
  3. package/scripts/command-lifecycle-report.mjs +94 -0
  4. package/src/assets/web-panel/assets/{AIOps-CSN-D2xb.js → AIOps-DcQ0bWKy.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-CBSMlGzv.js → ActionButton-GecwfFPt.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-3yr3Nt2t.js → Analytics-CD6iSt9v.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-1b-o3lMH.js → AppLayout-CDEtuEYf.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-Dr8rJi17.js → Artifacts-DS3jAjeW.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-2V689HC2.js → Audit-CcSJA048.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-DIxkNrD_.js → BackgroundAgents-DRo9Morb.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-Pg6BcwrE.js → Backup-Vw5MrWft.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-CLUpqyfH.js → BaseInput-DNHIu823.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-qVS8rD4G.js → Chat-DvAsO8-b.js} +5 -5
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-ti49X12Q.js → ChatBubbleRenderer-9aDuObay.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-Bs9AaEay.js → Checkbox-CKCa7tvn.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-Dvc1mq8q.js → Codegen-CIFvZdaT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-C4_biO7A.js → Col-C79yz7bK.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-al25XQUV.js → Community-DVvZskLJ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-D-ZDB8M1.js → Compact-oaew61hV.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-6lIfDz1W.js → Compliance-DqA7wmRx.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-DdI48XMJ.js → Cowork-B-qGadxa.js} +2 -2
  22. package/src/assets/web-panel/assets/{Cron-DF5HZd2g.js → Cron-CUlCl4sS.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-DuxeSvt3.js → Crosschain-CNOlr9DU.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-B9yHkUTd.js → DID-DJqFCT_3.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-Z5EuI3IM.js → Dashboard-CznGpphf.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-vuRfO3dY.js → Dropdown-_cJw218E.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-QFLCc1uL.js → EmailListRenderer-Dr-1BZ9C.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BZ7EXkFJ.js → FamilyGuardDashboard-Dxf4xtSk.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-DycIEg2s.js → Federation-DOiG3i_1.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-Ba0H_7yG.js → FormItemContext-BzbMcFB9.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer--JgSTK-C.js → GenericCardRenderer-VbBrYttP.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-DEvTbldE.js → Git-CLG8E6ED.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-BJIljy18.js → Governance-LGJQfk_q.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-CVyW3G3q.js → Inference-DlQKx9lF.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-v8-borge.js → KnowledgeGraph-DU8vEouV.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BCdS7gGo.js → Logs-FZIQOdln.js} +2 -2
  37. package/src/assets/web-panel/assets/MarkdownRenderer-BJNWIrbw.js +1 -0
  38. package/src/assets/web-panel/assets/{Marketplace-AN6tGA2B.js → Marketplace-C_xd-zZI.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-DGViXASN.js → McpTools-q-YktphS.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-Bpvmx9X4.js → Memory-Cq9OhDin.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-CTP-DOTC.js → MobileBridge-B03_R98T.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-C8qfktxn.js → MobileProjects-DZrk-qmF.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-ndYr0M1s.js → Mtc-BidCKW28.js} +2 -2
  44. package/src/assets/web-panel/assets/{MtcAudit-Ci0-YK7W.js → MtcAudit-r-iaMVM7.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-BehSTFW7.js → Multisig-CgTUFvff.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-B91_YBLv.js → NLProgramming-B7NvzfAV.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-BQlJ0-gq.js → Notes-80CWNqJI.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-CDgYMjYK.js → NotificationSettings-0H6xdiY3.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-BJkSlKeH.js → OrderTableRenderer-CKP4AFWy.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-B-b3cWCp.js → Organization-B8sxCV_h.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-CILUL4nL.js → Overflow-CW9JBBhQ.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-BTCFBkF0.js → P2P-bdSQV7oZ.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-BAR6AUx5.js → PdhVaultBrowser-DQad42nW.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-RRf6HbWJ.js → Permissions-CueexxTR.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-BGTMiICa.js → PersonalDataHub-fqxflhR8.js} +6 -6
  56. package/src/assets/web-panel/assets/{Pipeline-945qY9YF.js → Pipeline-KFwwnIMC.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-DPZwI6lJ.js → Privacy-ClnD9buY.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-BPH2NRKc.js → ProjectInit-CZEz6sm3.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-BZCRNCvb.js → ProjectSettings-B6NzpmOI.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-BTI__tBt.js → Projects-DYyuwPCs.js} +1 -1
  61. package/src/assets/web-panel/assets/Providers-C3tz_jdh.js +1 -0
  62. package/src/assets/web-panel/assets/{QrScannerModal-N6rbqt1r.js → QrScannerModal-BzuOBbqB.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-DrwjPS7M.js → QuickAsk-CmNO9qSj.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-Cog9Wd99.js → Recommend-VJNj3_NF.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-DC9pynjM.js → RemoteSession-CrbdbSBF.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-D4th4gKS.js → Reputation-DTyMd45j.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-ogiWR6v9.js → Row-DHrQ-Icz.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-uYlk-2Oj.js → RssFeed-DqWxY9c5.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-DYzzwvtM.js → Search-ctF1Ks3J.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-Da7YOyX6.js → Security-BrQz8uo5.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-CLI1VM1X.js → Services-D4scVFar.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-DK77dzDC.js → Skeleton-oUScaOjv.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-C_z7Lmhl.js → Skills-CL-y0eLt.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-D6413Q8L.js → Sla-DQExhwXj.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-C07ydbj2.js → SpeechSettings-iNp_I7kN.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-DGOm2W22.js → SyncSettings-CTDlMo0A.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-Cr_PVyLM.js → Tasks-4kb8l3sc.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-BhL2mZ0t.js → Templates-BXcL9oXW.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-CPk5oyUr.js → Tenant-DLE8eS0D.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-ClhqUsqV.js → Terminal-DGpUZPPK.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-BCC7MV5a.js → TimelineRenderer-C1pWXGPC.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-DD1Jt4oO.js → Tokens-BYfbfqhZ.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BCydFvN2.js → Trigger-K9YfLOCw.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-DDHT1noa.js → Trust-Ctkq77Hy.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-CWPZC7oI.js → UkeySign-C6_CSe9J.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-9Ea0nJR0.js → VideoEditing-Be3rpOWs.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-BjxhLxTf.js → Wallet-D0UIplxk.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-C6uCye67.js → WebAuthn-CF9bM9I3.js} +3 -3
  89. package/src/assets/web-panel/assets/{WorkflowEditor-d2g4reYo.js → WorkflowEditor-D0H1XVPY.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-frTc6ScX.js → chat-Cgzzbe0o.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-B5K6lG3v.js → colors-BwNxEVS0.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-Cdn46r8D.js → compact-item-D60Z4Hhs.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-DU1jqVQi.js → createContext-MFk58XB0.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BJiw369i.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-DdNH-aW_.js → hasIn-CRj2SvGO.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DM5FmPZU.js → index--s45_1cb.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-D4l82Me1.js → index--tquQ2ZG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CQorP1Ng.js → index-1T1rhokn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CHmkkdfT.js → index-B-M82_rx.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CGiwfwB9.js → index-BE_el1Zs.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BwlIxAmX.js → index-BhRkR7IO.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cj6P9Nst.js → index-Bo0y96-K.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BoFak7le.js → index-CJgu5JUK.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Dca_i7H8.js → index-CLWwdebO.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BVLL-mq_.js → index-CNAK5QG_.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-LaivuXyF.js → index-CQjJjPRD.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-t_OBzIP4.js → index-CTDbAfae.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BcGwGIaR.js → index-CVI_9sRK.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-D23xGYjM.js → index-CY14oMQK.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-VDM2KKo9.js → index-CbiTJkZf.js} +1 -1
  111. package/src/assets/web-panel/assets/index-CiMouosn.js +1 -0
  112. package/src/assets/web-panel/assets/{index-32ajXmTb.js → index-CqWpxMqx.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-PjAS0sIZ.js → index-CvB8lYUR.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-7ehytj1K.js → index-D51XwtCY.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DtBCCkC4.js → index-D6HvK_1O.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Be4OtL-N.js → index-D9jVIwLJ.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CUoNAmE8.js → index-DA4tE98u.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-cYaWV4Mq.js → index-DBwOtXw0.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-MM4Zz2OE.js → index-DKdNzTIb.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DIVyTmZ3.js → index-DRySdmvN.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CtRPi4YC.js → index-DWzyTev7.js} +1 -1
  122. package/src/assets/web-panel/assets/index-DXcKlwy7.js +1 -0
  123. package/src/assets/web-panel/assets/{index-kVO_MDm-.js → index-DYjs7jAP.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D4miNkWv.js → index-DbROzMBp.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-yZRuZpwK.js → index-DhFtk5PR.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BXKhhx9V.js → index-Di2AbMLg.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BC4eGkar.js → index-DvmocQsM.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-NmEXVjzt.js → index-Dw-o0zEF.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DRzBBdQ0.js → index-DysPoP38.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-HjCvXHf5.js → index-LX9pJxm_.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-gg3QPC3i.js → index-OI_fXWx0.js} +3 -3
  132. package/src/assets/web-panel/assets/{index-mgU3Q-lN.js → index-eDTOX8Xj.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-D1qIC4GR.js → index-gWKDsTzF.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-DftZuyA7.js → index-q_GDUVAA.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-DwLTRNdo.js → initDefaultProps-C61kZqEd.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-BchZDXnh.js → motion-DZnT5U1L.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-e8M-zZFl.js → move-L8wzwbel.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-Ci40fQb3.js → mtc-parser-DLdYSejc.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-C7110_f2.js → omit-D53cbS59.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-skteMgnM.js → pickAttrs-Bbc5HPtu.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-MGZ-lh2B.js → placementArrow-BkvmO860.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-N4Sd7uy-.js → responsiveObserve-u2y1ryS1.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-C7HDGRaa.js → slide-BBqj7svU.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-BhRexlgy.js → statusUtils-GN0mqu2d.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-ChEoQkN2.js → styleChecker-BvwPKoBT.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-CDbG1s2Y.js → useFlexGapSupport-CCt8tnfY.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-CUIx0tcJ.js → useFs-DXAxl2SV.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-bkO7L7ec.js → usePersonalDataHub-DihlR2dj.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-B_ZvABIG.js → vnode-D7BOX2gi.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-D5shjpS8.js → zoom-CVbO-nL7.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +1 -1
  153. package/src/commands/mcp.js +119 -0
  154. package/src/commands/session-show.js +8 -10
  155. package/src/commands/session.js +60 -52
  156. package/src/data/changelog.json +27 -1
  157. package/src/gateways/ws/session-protocol.js +14 -4
  158. package/src/gateways/ws/ws-agent-handler.js +31 -0
  159. package/src/harness/jsonl-session-store.js +1147 -130
  160. package/src/harness/mcp-client.js +44 -11
  161. package/src/harness/session-index.js +80 -80
  162. package/src/harness/session-list-index.js +147 -2
  163. package/src/lib/background-interaction-journal.js +2 -2
  164. package/src/lib/command-lifecycle-report.js +516 -0
  165. package/src/lib/mcp-stdio-environment.js +48 -0
  166. package/src/lib/mcp-stdio-executable-identity.js +552 -17
  167. package/src/lib/mcp-stdio-package-materialization.js +1424 -0
  168. package/src/lib/packer/native-update-state.js +979 -1
  169. package/src/lib/packer/pack-update-applier.js +1050 -125
  170. package/src/lib/packer/pkg-config-generator.js +1 -0
  171. package/src/lib/paths.js +53 -0
  172. package/src/lib/process-execution-broker/index.js +180 -37
  173. package/src/lib/process-execution-broker/platform-sandbox.js +291 -24
  174. package/src/lib/recent-session.js +17 -17
  175. package/src/lib/secure-file-identity.js +47 -0
  176. package/src/lib/secure-fs.js +21 -2
  177. package/src/lib/session-anti-rollback-anchor.js +766 -0
  178. package/src/lib/session-host-lease.js +492 -0
  179. package/src/lib/session-persistence-failure.js +109 -0
  180. package/src/lib/session-picker.js +26 -3
  181. package/src/lib/session-search.js +25 -11
  182. package/src/lib/side-effect-ledger-store.js +2 -2
  183. package/src/lib/skill-execution-authority.js +13 -9
  184. package/src/lib/turn-binding-store.js +2 -2
  185. package/src/repl/agent-repl.js +309 -39
  186. package/src/runtime/agent-core.js +88 -5
  187. package/src/runtime/cleanup-deadline.js +143 -0
  188. package/src/runtime/headless-runner.js +357 -139
  189. package/src/runtime/headless-stream.js +207 -28
  190. package/src/runtime/output-backpressure.js +53 -0
  191. package/src/assets/web-panel/assets/MarkdownRenderer-CU-JB2Ns.js +0 -1
  192. package/src/assets/web-panel/assets/Providers-DDCf_nIw.js +0 -1
  193. package/src/assets/web-panel/assets/devWarning-CP-jEUyw.js +0 -1
  194. package/src/assets/web-panel/assets/index-CzrCHrFy.js +0 -1
  195. package/src/assets/web-panel/assets/index-OJsGskZw.js +0 -1
@@ -37,6 +37,7 @@ import {
37
37
  * the selected backend; it does not merely describe a configured intent.
38
38
  */
39
39
  export const SANDBOX_BOUNDARIES = Object.freeze({
40
+ CODE_SNAPSHOT: "code-snapshot",
40
41
  FILESYSTEM: "filesystem",
41
42
  NETWORK: "network",
42
43
  PROCESS_EXEC: "process-exec",
@@ -105,6 +106,7 @@ const WINDOWS_IDENTITY_WAIT = new Int32Array(new SharedArrayBuffer(4));
105
106
  const WINDOWS_RESTRICTED_TOKEN_BACKEND = "windows-job-restricted-token";
106
107
  const WINDOWS_APPCONTAINER_BACKEND =
107
108
  "windows-appcontainer-job-restricted-token";
109
+ const MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND = "strict-mcp-node-capsule";
108
110
  const WINDOWS_ADAPTER_IDLE_TTL_MS = 60_000;
109
111
  // Every native helper operation starts a trusted System32 PowerShell host and
110
112
  // byte-loads the checked-in assembly. Hosted Windows runners can spend well
@@ -163,6 +165,11 @@ const LINUX_ATTESTATION_HASH_CHUNK_BYTES = 1024 * 1024;
163
165
  const LINUX_ENTRY_SNAPSHOT_MECHANISM =
164
166
  "verified-o_tmpfile-copy-bwrap-ro-bind-data-v1";
165
167
  const LINUX_ENTRY_SNAPSHOT_SOURCE_MODE = 0o400;
168
+ const LINUX_EXECUTABLE_SNAPSHOT_SOURCE_MODE = 0o500;
169
+ const LINUX_MCP_CAPSULE_BACKEND = "linux-fd-code-snapshot";
170
+ const LINUX_MCP_RUNTIME_SNAPSHOT_SCOPE = "mcp-node-runtime-executable";
171
+ const LINUX_MCP_CAPSULE_SNAPSHOT_MECHANISM =
172
+ "verified-o_tmpfile-copy-inherited-fd-exec-v1";
166
173
  const LINUX_NODE_ENTRY_SNAPSHOT_SCOPE = "plugin-entry-source";
167
174
  const LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE = "plugin-entry-executable";
168
175
  const LINUX_NODE_PLUGIN_TREE_SNAPSHOT_SCOPE = "all-pinned-plugin-regular-files";
@@ -1884,13 +1891,18 @@ function windowsSandboxHostEnvironment(runtime) {
1884
1891
  function windowsPluginNodeEntrySnapshot(invocation, sandboxOpts, spawnOpts) {
1885
1892
  const contract = sandboxOpts.executionContract;
1886
1893
  if (!contract) return { locks: null, reason: null };
1887
- if (contract.kind !== "strict-plugin-node-bin") {
1894
+ const capsuleContract =
1895
+ contract.kind === MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND;
1896
+ if (contract.kind !== "strict-plugin-node-bin" && !capsuleContract) {
1888
1897
  return {
1889
1898
  locks: null,
1890
1899
  reason: "windows_plugin_execution_contract_unsupported",
1891
1900
  };
1892
1901
  }
1893
- if (sandboxOpts.sync !== true || spawnOpts?.detached === true) {
1902
+ if (
1903
+ (!capsuleContract && sandboxOpts.sync !== true) ||
1904
+ spawnOpts?.detached === true
1905
+ ) {
1894
1906
  return {
1895
1907
  locks: null,
1896
1908
  reason: "windows_plugin_launch_path_lock_requires_sync_foreground",
@@ -2586,10 +2598,34 @@ export function applyWindowsSandbox(
2586
2598
  ? {
2587
2599
  ...(appContainerRuntimeProbe || nodeSnapshotRuntimeProbe),
2588
2600
  contentSnapshot: true,
2589
- contentSnapshotScope: "plugin-entry-source",
2601
+ contentSnapshotScope:
2602
+ sandboxOpts.executionContract?.kind ===
2603
+ MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND
2604
+ ? "mcp-capsule-entry-source"
2605
+ : "plugin-entry-source",
2590
2606
  contentSnapshotMechanism:
2591
2607
  "verified-handle-inherited-pipe-module-compile-v1",
2592
2608
  handleAtomic: false,
2609
+ ...(sandboxOpts.executionContract?.kind ===
2610
+ MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND
2611
+ ? {
2612
+ runtimeAttestedSha256: entrySnapshot.locks.find(
2613
+ ({ role }) => role === "runtime",
2614
+ )?.sha256,
2615
+ runtimeAttestedBytes: entrySnapshot.locks.find(
2616
+ ({ role }) => role === "runtime",
2617
+ )?.bytes,
2618
+ entrySnapshotSha256: entrySnapshot.locks.find(
2619
+ ({ role }) => role === "entry",
2620
+ )?.sha256,
2621
+ entrySnapshotBytes: entrySnapshot.locks.find(
2622
+ ({ role }) => role === "entry",
2623
+ )?.bytes,
2624
+ entrySnapshotAtomic: true,
2625
+ runtimeLaunchAtomic: false,
2626
+ sharedLibraryClosure: false,
2627
+ }
2628
+ : {}),
2593
2629
  }
2594
2630
  : appContainerRuntimeProbe;
2595
2631
  return createSandboxPlan({
@@ -2599,13 +2635,20 @@ export function applyWindowsSandbox(
2599
2635
  policyAttested: requiresAppContainer ? true : null,
2600
2636
  policyDigest: appContainerPolicyDigest,
2601
2637
  runtimeProbe: entrySnapshotRuntimeProbe,
2602
- guarantees: requiresAppContainer
2603
- ? appContainerGuarantees
2604
- : [
2605
- SANDBOX_BOUNDARIES.PROCESS_TREE,
2606
- SANDBOX_BOUNDARIES.RESOURCE_LIMITS,
2607
- SANDBOX_BOUNDARIES.PRIVILEGE_REDUCTION,
2608
- ],
2638
+ guarantees: [
2639
+ ...(requiresAppContainer
2640
+ ? appContainerGuarantees
2641
+ : [
2642
+ SANDBOX_BOUNDARIES.PROCESS_TREE,
2643
+ SANDBOX_BOUNDARIES.RESOURCE_LIMITS,
2644
+ SANDBOX_BOUNDARIES.PRIVILEGE_REDUCTION,
2645
+ ]),
2646
+ ...(entrySnapshot.locks &&
2647
+ sandboxOpts.executionContract?.kind ===
2648
+ MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND
2649
+ ? [SANDBOX_BOUNDARIES.CODE_SNAPSHOT]
2650
+ : []),
2651
+ ],
2609
2652
  command: helperInvocation.command,
2610
2653
  args: helperInvocation.args,
2611
2654
  options,
@@ -3042,7 +3085,10 @@ function validateLinuxPluginContract(
3042
3085
  sync,
3043
3086
  { sealedEntry = false } = {},
3044
3087
  ) {
3045
- const nodeContract = contract?.kind === "strict-plugin-node-bin";
3088
+ const capsuleContract =
3089
+ contract?.kind === MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND;
3090
+ const nodeContract =
3091
+ contract?.kind === "strict-plugin-node-bin" || capsuleContract;
3046
3092
  const legacyStaticNativeContract =
3047
3093
  contract?.kind === "strict-plugin-native-static-elf-bin";
3048
3094
  const nativeContract =
@@ -3060,7 +3106,7 @@ function validateLinuxPluginContract(
3060
3106
  if (
3061
3107
  (sync !== true && sync !== false) ||
3062
3108
  spawnOpts?.shell !== false ||
3063
- spawnOpts?.detached === true ||
3109
+ (spawnOpts?.detached === true && !capsuleContract) ||
3064
3110
  !linuxStdioIsNarrow(spawnOpts?.stdio) ||
3065
3111
  spawnOpts?.serialization !== undefined ||
3066
3112
  spawnOpts?.argv0 !== undefined ||
@@ -3274,6 +3320,7 @@ function linuxEntrySnapshotContract(targetRuntime) {
3274
3320
  return {
3275
3321
  scope: LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE,
3276
3322
  targetMode: LINUX_NATIVE_ENTRY_SNAPSHOT_TARGET_MODE,
3323
+ sourceMode: LINUX_ENTRY_SNAPSHOT_SOURCE_MODE,
3277
3324
  minimumBytes: LINUX_ELF64_HEADER_BYTES,
3278
3325
  errorPrefix: "native_entry_snapshot",
3279
3326
  };
@@ -3282,6 +3329,7 @@ function linuxEntrySnapshotContract(targetRuntime) {
3282
3329
  return {
3283
3330
  scope: LINUX_NODE_ENTRY_SNAPSHOT_SCOPE,
3284
3331
  targetMode: LINUX_NODE_ENTRY_SNAPSHOT_TARGET_MODE,
3332
+ sourceMode: LINUX_ENTRY_SNAPSHOT_SOURCE_MODE,
3285
3333
  minimumBytes: 0,
3286
3334
  errorPrefix: "node_entry_snapshot",
3287
3335
  };
@@ -3296,11 +3344,22 @@ function linuxNodePluginTreeFileSnapshotContract(sourceMode) {
3296
3344
  return {
3297
3345
  scope: LINUX_NODE_PLUGIN_TREE_SNAPSHOT_SCOPE,
3298
3346
  targetMode: (sourceMode & 0o111) !== 0 ? "0500" : "0400",
3347
+ sourceMode: LINUX_ENTRY_SNAPSHOT_SOURCE_MODE,
3299
3348
  minimumBytes: 0,
3300
3349
  errorPrefix: "node_plugin_tree_snapshot",
3301
3350
  };
3302
3351
  }
3303
3352
 
3353
+ function linuxMcpRuntimeSnapshotContract() {
3354
+ return {
3355
+ scope: LINUX_MCP_RUNTIME_SNAPSHOT_SCOPE,
3356
+ targetMode: "0500",
3357
+ sourceMode: LINUX_EXECUTABLE_SNAPSHOT_SOURCE_MODE,
3358
+ minimumBytes: 1,
3359
+ errorPrefix: "mcp_node_runtime_snapshot",
3360
+ };
3361
+ }
3362
+
3304
3363
  function createLinuxRegularFileSnapshot(
3305
3364
  runtime,
3306
3365
  entryMount,
@@ -3311,10 +3370,16 @@ function createLinuxRegularFileSnapshot(
3311
3370
  let probeFd;
3312
3371
  let finalFd;
3313
3372
  try {
3373
+ const snapshotSourceMode = snapshotContract?.sourceMode;
3314
3374
  if (
3315
3375
  !snapshotContract ||
3316
3376
  typeof snapshotContract.scope !== "string" ||
3317
3377
  typeof snapshotContract.targetMode !== "string" ||
3378
+ !Number.isSafeInteger(snapshotSourceMode) ||
3379
+ ![
3380
+ LINUX_ENTRY_SNAPSHOT_SOURCE_MODE,
3381
+ LINUX_EXECUTABLE_SNAPSHOT_SOURCE_MODE,
3382
+ ].includes(snapshotSourceMode) ||
3318
3383
  !Number.isSafeInteger(snapshotContract.minimumBytes) ||
3319
3384
  typeof snapshotContract.errorPrefix !== "string"
3320
3385
  ) {
@@ -3369,7 +3434,8 @@ function createLinuxRegularFileSnapshot(
3369
3434
  throw snapshotError("source_mode_changed");
3370
3435
  }
3371
3436
  if (
3372
- snapshotContract.scope === LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE &&
3437
+ (snapshotContract.scope === LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE ||
3438
+ snapshotContract.scope === LINUX_MCP_RUNTIME_SNAPSHOT_SCOPE) &&
3373
3439
  (sourceFileMode & 0o111) === 0
3374
3440
  ) {
3375
3441
  throw snapshotError("source_mode_changed");
@@ -3382,11 +3448,7 @@ function createLinuxRegularFileSnapshot(
3382
3448
  Number(constants.O_TMPFILE ?? LINUX_O_TMPFILE) |
3383
3449
  Number(constants.O_NOFOLLOW || 0) |
3384
3450
  Number(constants.O_NONBLOCK || 0);
3385
- writerFd = runtime.fs.openSync(
3386
- "/tmp",
3387
- writerFlags,
3388
- LINUX_ENTRY_SNAPSHOT_SOURCE_MODE,
3389
- );
3451
+ writerFd = runtime.fs.openSync("/tmp", writerFlags, snapshotSourceMode);
3390
3452
 
3391
3453
  const sourceDigest = crypto.createHash("sha256");
3392
3454
  const chunk = Buffer.allocUnsafe(
@@ -3455,15 +3517,14 @@ function createLinuxRegularFileSnapshot(
3455
3517
  throw snapshotError("identity_changed");
3456
3518
  }
3457
3519
 
3458
- runtime.fs.fchmodSync(writerFd, LINUX_ENTRY_SNAPSHOT_SOURCE_MODE);
3520
+ runtime.fs.fchmodSync(writerFd, snapshotSourceMode);
3459
3521
  runtime.fs.fsyncSync(writerFd);
3460
3522
  const sealedBeforeRead = runtime.fs.fstatSync(writerFd);
3461
3523
  if (
3462
3524
  !sealedBeforeRead.isFile() ||
3463
3525
  Number(sealedBeforeRead.nlink) !== 0 ||
3464
3526
  Number(sealedBeforeRead.size) !== entryIdentity.bytes ||
3465
- (Number(sealedBeforeRead.mode) & 0o777) !==
3466
- LINUX_ENTRY_SNAPSHOT_SOURCE_MODE
3527
+ (Number(sealedBeforeRead.mode) & 0o777) !== snapshotSourceMode
3467
3528
  ) {
3468
3529
  throw snapshotError("readonly_mode_unattested");
3469
3530
  }
@@ -3513,7 +3574,7 @@ function createLinuxRegularFileSnapshot(
3513
3574
  }),
3514
3575
  sourceMtimeMs: Number(entryIdentity.mtimeMs),
3515
3576
  sourceFileMode: sourceFileMode.toString(8).padStart(4, "0"),
3516
- sourceMode: LINUX_ENTRY_SNAPSHOT_SOURCE_MODE.toString(8).padStart(4, "0"),
3577
+ sourceMode: snapshotSourceMode.toString(8).padStart(4, "0"),
3517
3578
  targetMode: snapshotContract.targetMode,
3518
3579
  });
3519
3580
  const descriptor = (fd) => {
@@ -3581,12 +3642,13 @@ function attestLinuxRegularFileSnapshot(
3581
3642
  snapshotContract,
3582
3643
  ) {
3583
3644
  const errorPrefix = snapshotContract.errorPrefix;
3645
+ const snapshotSourceMode = snapshotContract.sourceMode;
3584
3646
  const before = runtime.fs.fstatSync(mount?.fd);
3585
3647
  if (
3586
3648
  attestation?.scope !== snapshotContract.scope ||
3587
3649
  attestation?.mechanism !== LINUX_ENTRY_SNAPSHOT_MECHANISM ||
3588
3650
  attestation?.sourceMode !==
3589
- LINUX_ENTRY_SNAPSHOT_SOURCE_MODE.toString(8).padStart(4, "0") ||
3651
+ snapshotSourceMode.toString(8).padStart(4, "0") ||
3590
3652
  typeof attestation?.sourceFileMode !== "string" ||
3591
3653
  !/^[0-7]{4}$/.test(attestation.sourceFileMode) ||
3592
3654
  attestation?.targetMode !== snapshotContract.targetMode ||
@@ -3595,7 +3657,7 @@ function attestLinuxRegularFileSnapshot(
3595
3657
  mount?.contentSnapshot !== attestation ||
3596
3658
  !before.isFile() ||
3597
3659
  Number(before.nlink) !== 0 ||
3598
- (Number(before.mode) & 0o777) !== LINUX_ENTRY_SNAPSHOT_SOURCE_MODE ||
3660
+ (Number(before.mode) & 0o777) !== snapshotSourceMode ||
3599
3661
  String(before.dev) !== String(mount?.snapshotIdentity?.fileId?.dev) ||
3600
3662
  String(before.ino) !== String(mount?.snapshotIdentity?.fileId?.ino) ||
3601
3663
  Number(before.size) !== Number(attestation?.bytes) ||
@@ -3624,6 +3686,200 @@ function attestLinuxEntrySnapshot(runtime, mount, attestation, targetRuntime) {
3624
3686
  );
3625
3687
  }
3626
3688
 
3689
+ function linuxIdentityExpectedStat(identity) {
3690
+ return {
3691
+ dev: identity?.fileId?.dev,
3692
+ ino: identity?.fileId?.ino,
3693
+ size: identity?.bytes,
3694
+ mtimeMs: identity?.mtimeMs,
3695
+ };
3696
+ }
3697
+
3698
+ function applyLinuxMcpCapsuleCodeSnapshot(
3699
+ command,
3700
+ args,
3701
+ spawnOpts,
3702
+ sandboxOpts,
3703
+ runtime,
3704
+ base,
3705
+ ) {
3706
+ const contract = sandboxOpts.executionContract;
3707
+ const validation = validateLinuxPluginContract(
3708
+ command,
3709
+ args,
3710
+ spawnOpts,
3711
+ contract,
3712
+ runtime,
3713
+ sandboxOpts.sync,
3714
+ );
3715
+ const unavailable = (reason, runtimeProbe = null) =>
3716
+ createSandboxPlan({
3717
+ ...base,
3718
+ backend: null,
3719
+ candidateBackend: LINUX_MCP_CAPSULE_BACKEND,
3720
+ policyAttested: false,
3721
+ runtimeProbe: runtimeProbe
3722
+ ? {
3723
+ kind: "linux-mcp-capsule-code-snapshot-v1",
3724
+ attempted: runtimeProbe.attempted !== false,
3725
+ probeRuntime: "node",
3726
+ targetRuntime: "node",
3727
+ ...runtimeProbe,
3728
+ }
3729
+ : null,
3730
+ reason,
3731
+ guarantees: [],
3732
+ });
3733
+ if (
3734
+ contract?.kind !== MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND ||
3735
+ !validation.ok
3736
+ ) {
3737
+ return unavailable("linux_mcp_capsule_execution_contract_invalid", {
3738
+ attempted: false,
3739
+ runnable: false,
3740
+ reason: validation.reason,
3741
+ contentSnapshot: false,
3742
+ handleAtomic: false,
3743
+ });
3744
+ }
3745
+
3746
+ const invocation = normalizeWrappedInvocation(
3747
+ command,
3748
+ args,
3749
+ spawnOpts,
3750
+ runtime.platform,
3751
+ );
3752
+ let runtimePin;
3753
+ let entryPin;
3754
+ let runtimeSnapshot;
3755
+ let entrySnapshot;
3756
+ try {
3757
+ runtimePin = pinLinuxRegularFile(
3758
+ runtime,
3759
+ contract.runtimeIdentity.realPath,
3760
+ contract.runtimeIdentity.realPath,
3761
+ linuxIdentityExpectedStat(contract.runtimeIdentity),
3762
+ { requireSingleLink: true },
3763
+ );
3764
+ entryPin = pinLinuxRegularFile(
3765
+ runtime,
3766
+ contract.entryIdentity.realPath,
3767
+ contract.entryIdentity.realPath,
3768
+ linuxIdentityExpectedStat(contract.entryIdentity),
3769
+ { requireSingleLink: true },
3770
+ );
3771
+ runtimeSnapshot = createLinuxRegularFileSnapshot(
3772
+ runtime,
3773
+ runtimePin,
3774
+ contract.runtimeIdentity,
3775
+ linuxMcpRuntimeSnapshotContract(),
3776
+ );
3777
+ entrySnapshot = createLinuxEntrySnapshot(
3778
+ runtime,
3779
+ entryPin,
3780
+ contract.entryIdentity,
3781
+ "node",
3782
+ );
3783
+ attestLinuxRegularFileSnapshot(
3784
+ runtime,
3785
+ runtimeSnapshot.finalMount,
3786
+ runtimeSnapshot.attestation,
3787
+ linuxMcpRuntimeSnapshotContract(),
3788
+ );
3789
+ attestLinuxEntrySnapshot(
3790
+ runtime,
3791
+ entrySnapshot.finalMount,
3792
+ entrySnapshot.attestation,
3793
+ "node",
3794
+ );
3795
+ } catch (error) {
3796
+ closeLinuxPinnedMounts(runtime, [
3797
+ runtimePin,
3798
+ entryPin,
3799
+ runtimeSnapshot?.probeMount,
3800
+ runtimeSnapshot?.finalMount,
3801
+ entrySnapshot?.probeMount,
3802
+ entrySnapshot?.finalMount,
3803
+ ]);
3804
+ return unavailable("linux_mcp_capsule_code_snapshot_unavailable", {
3805
+ runnable: false,
3806
+ reason: error.message || "snapshot_unattested",
3807
+ contentSnapshot: false,
3808
+ handleAtomic: false,
3809
+ });
3810
+ }
3811
+
3812
+ closeLinuxPinnedMounts(runtime, [
3813
+ runtimePin,
3814
+ entryPin,
3815
+ runtimeSnapshot.probeMount,
3816
+ entrySnapshot.probeMount,
3817
+ ]);
3818
+ const descriptors = [runtimeSnapshot.finalMount, entrySnapshot.finalMount];
3819
+ const runtimeFd = 3;
3820
+ const entryFd = 4;
3821
+ const snapshotIdentity = Object.freeze({
3822
+ runtimeSnapshotSha256: runtimeSnapshot.attestation.sha256,
3823
+ runtimeSnapshotBytes: runtimeSnapshot.attestation.bytes,
3824
+ entrySnapshotSha256: entrySnapshot.attestation.sha256,
3825
+ entrySnapshotBytes: entrySnapshot.attestation.bytes,
3826
+ });
3827
+ const policyDigest = sha256(
3828
+ JSON.stringify({
3829
+ version: 1,
3830
+ backend: LINUX_MCP_CAPSULE_BACKEND,
3831
+ contractKind: contract.kind,
3832
+ requiredBoundaries: [
3833
+ ...new Set(sandboxOpts.requiredBoundaries || []),
3834
+ ].sort(),
3835
+ identity: snapshotIdentity,
3836
+ cwd: invocation.options.cwd,
3837
+ passthroughArgsDigest: sha256(JSON.stringify(invocation.args.slice(1))),
3838
+ }),
3839
+ );
3840
+ let closed = false;
3841
+ const cleanup = () => {
3842
+ if (closed) return;
3843
+ closed = true;
3844
+ closeLinuxPinnedMounts(runtime, descriptors);
3845
+ };
3846
+ return createSandboxPlan({
3847
+ ...base,
3848
+ applied: true,
3849
+ enforcement: LINUX_MCP_CAPSULE_BACKEND,
3850
+ backend: LINUX_MCP_CAPSULE_BACKEND,
3851
+ candidateBackend: null,
3852
+ policyAttested: true,
3853
+ policyDigest,
3854
+ runtimeProbe: {
3855
+ kind: "linux-mcp-capsule-code-snapshot-v1",
3856
+ attempted: true,
3857
+ runnable: true,
3858
+ reason: null,
3859
+ probeRuntime: "node",
3860
+ targetRuntime: "node",
3861
+ contentSnapshot: true,
3862
+ contentSnapshotScope: "mcp-capsule-entry-and-node-runtime",
3863
+ contentSnapshotMechanism: LINUX_MCP_CAPSULE_SNAPSHOT_MECHANISM,
3864
+ handleAtomic: true,
3865
+ sharedLibraryClosure: false,
3866
+ ...snapshotIdentity,
3867
+ },
3868
+ reason: null,
3869
+ guarantees: [SANDBOX_BOUNDARIES.CODE_SNAPSHOT],
3870
+ command: `/proc/self/fd/${runtimeFd}`,
3871
+ args: [`/proc/self/fd/${entryFd}`, ...invocation.args.slice(1)],
3872
+ options: {
3873
+ ...invocation.options,
3874
+ shell: false,
3875
+ stdio: Object.freeze(
3876
+ linuxStdioWithPinnedMounts(invocation.options.stdio, descriptors),
3877
+ ),
3878
+ },
3879
+ cleanup,
3880
+ });
3881
+ }
3882
+
3627
3883
  function buildLinuxNodePluginTreeSnapshotAttestation(mounts, entryDestination) {
3628
3884
  const members = [];
3629
3885
  const destinations = new Set();
@@ -5439,6 +5695,17 @@ export function applyLinuxSandbox(
5439
5695
  boundary === SANDBOX_BOUNDARIES.PROCESS_TREE,
5440
5696
  );
5441
5697
 
5698
+ if (requiredBoundaries.includes(SANDBOX_BOUNDARIES.CODE_SNAPSHOT)) {
5699
+ return applyLinuxMcpCapsuleCodeSnapshot(
5700
+ command,
5701
+ args,
5702
+ spawnOpts,
5703
+ sandboxOpts,
5704
+ runtime,
5705
+ base,
5706
+ );
5707
+ }
5708
+
5442
5709
  if (requiresStrongLinuxBoundary) {
5443
5710
  if (isLinuxGenericWorkspaceContract(sandboxOpts.executionContract)) {
5444
5711
  return applyLinuxGenericWorkspaceSandbox(
@@ -7,8 +7,10 @@
7
7
  */
8
8
 
9
9
  import { listSessions } from "./session-manager.js";
10
- import { listJsonlSessions } from "../harness/jsonl-session-store.js";
11
- import { feature } from "./feature-flags.js";
10
+ import {
11
+ listSessionAuthoritySummaries,
12
+ listSessionEvidenceIds,
13
+ } from "../harness/jsonl-session-store.js";
12
14
 
13
15
  /**
14
16
  * Merge both stores into a single newest-first, id-deduped list.
@@ -21,15 +23,18 @@ import { feature } from "./feature-flags.js";
21
23
  export function listRecentSessions(ctx, opts = {}) {
22
24
  const scan = Math.max(1, opts.scan || 20);
23
25
  const sessions = [];
26
+ const canonicalIds = new Set(listSessionEvidenceIds());
24
27
 
25
28
  if (ctx && ctx.db) {
26
29
  try {
27
30
  const db = ctx.db.getDatabase();
28
31
  sessions.push(
29
- ...listSessions(db, { limit: scan }).map((s) => ({
30
- ...s,
31
- _store: "db",
32
- })),
32
+ ...listSessions(db, { limit: scan })
33
+ .filter((s) => !canonicalIds.has(s?.id))
34
+ .map((s) => ({
35
+ ...s,
36
+ _store: "db",
37
+ })),
33
38
  );
34
39
  } catch (err) {
35
40
  /* db unavailable — fall through to JSONL */
@@ -37,17 +42,12 @@ export function listRecentSessions(ctx, opts = {}) {
37
42
  }
38
43
  }
39
44
 
40
- if (feature("JSONL_SESSION")) {
41
- try {
42
- sessions.push(
43
- ...listJsonlSessions({ limit: scan }).map((s) => ({
44
- ...s,
45
- _store: "jsonl",
46
- })),
47
- );
48
- } catch (err) {
49
- void err;
50
- }
45
+ try {
46
+ // Existing canonical history remains authoritative even when the feature
47
+ // flag that controls new JSONL adoption is disabled.
48
+ sessions.push(...listSessionAuthoritySummaries({ limit: scan }));
49
+ } catch (err) {
50
+ void err;
51
51
  }
52
52
 
53
53
  // Dedup by id (JSONL precedence via sort+seen), newest updated_at first.
@@ -33,6 +33,10 @@ const IDENTITY_FIELDS = Object.freeze([
33
33
  const IDENTITY_FIELDS_EXCEPT_DEVICE = Object.freeze(
34
34
  IDENTITY_FIELDS.filter((field) => field !== "dev"),
35
35
  );
36
+ const STABLE_FILE_IDENTITY_FIELDS = Object.freeze(["dev", "ino", "mode"]);
37
+ const STABLE_FILE_IDENTITY_FIELDS_EXCEPT_DEVICE = Object.freeze(
38
+ STABLE_FILE_IDENTITY_FIELDS.filter((field) => field !== "dev"),
39
+ );
36
40
  const DIRECTORY_IDENTITY_FIELDS = Object.freeze(["dev", "ino", "mode"]);
37
41
  const DIRECTORY_IDENTITY_FIELDS_EXCEPT_DEVICE = Object.freeze(
38
42
  DIRECTORY_IDENTITY_FIELDS.filter((field) => field !== "dev"),
@@ -184,6 +188,49 @@ export function samePathHandleFileIdentity(
184
188
  );
185
189
  }
186
190
 
191
+ /**
192
+ * Compare only the immutable/type portion of a path/handle file identity.
193
+ *
194
+ * This is for appenders: size and timestamps are expected to change while the
195
+ * descriptor is held, but the published name must continue to designate that
196
+ * exact regular file. The independently opened parent/volume device keeps the
197
+ * narrow Node 22.12 Windows device-projection bridge fail-closed.
198
+ */
199
+ export function samePathHandleStableFileIdentity(
200
+ pathStat,
201
+ handleStat,
202
+ expectedDevice,
203
+ runtime = undefined,
204
+ ) {
205
+ const pathIdentity = fileStatIdentity(pathStat);
206
+ const handleIdentity = fileStatIdentity(handleStat);
207
+ const trustedDevice =
208
+ expectedDevice === undefined || expectedDevice === null
209
+ ? null
210
+ : String(expectedDevice);
211
+ if (
212
+ !hasFields(pathIdentity, STABLE_FILE_IDENTITY_FIELDS) ||
213
+ !hasFields(handleIdentity, STABLE_FILE_IDENTITY_FIELDS) ||
214
+ trustedDevice === null ||
215
+ handleIdentity.dev !== trustedDevice
216
+ ) {
217
+ return false;
218
+ }
219
+ if (sameFields(pathIdentity, handleIdentity, STABLE_FILE_IDENTITY_FIELDS)) {
220
+ return true;
221
+ }
222
+ return (
223
+ isAffectedWindowsZeroDeviceStatRuntime(runtime) &&
224
+ pathIdentity.dev !== handleIdentity.dev &&
225
+ handleIdentity.dev !== "0" &&
226
+ sameFields(
227
+ pathIdentity,
228
+ handleIdentity,
229
+ STABLE_FILE_IDENTITY_FIELDS_EXCEPT_DEVICE,
230
+ )
231
+ );
232
+ }
233
+
187
234
  /**
188
235
  * Directory contents can legitimately change while a child is written, so a
189
236
  * parent recheck excludes content-derived metadata and the directory link
@@ -1004,6 +1004,7 @@ export function ensurePrivateDirectory(target, options = {}) {
1004
1004
  platform,
1005
1005
  );
1006
1006
  let existed = deps.fs.existsSync(target);
1007
+ let existingMode = null;
1007
1008
  if (existed) {
1008
1009
  const pathEntry = readPrivatePathEntry(
1009
1010
  target,
@@ -1013,6 +1014,7 @@ export function ensurePrivateDirectory(target, options = {}) {
1013
1014
  );
1014
1015
  existed = pathEntry.exists;
1015
1016
  const existing = pathEntry.entry;
1017
+ existingMode = existing ? Number(existing.mode) : null;
1016
1018
  if (existed && existing.isSymbolicLink()) {
1017
1019
  throw new Error(
1018
1020
  `Refusing owner-only directory through a symbolic link: ${target}`,
@@ -1031,7 +1033,16 @@ export function ensurePrivateDirectory(target, options = {}) {
1031
1033
  mode: PRIVATE_DIRECTORY_MODE,
1032
1034
  });
1033
1035
  if ((options.platform || deps.platform()) !== "win32") {
1034
- deps.fs.chmodSync(target, PRIVATE_DIRECTORY_MODE);
1036
+ // chmod updates ctime even when the mode is already correct. Session and
1037
+ // security stores use ctime as part of their physical witness, so avoid
1038
+ // invalidating an otherwise unchanged owner-only directory.
1039
+ if (
1040
+ !existed ||
1041
+ !Number.isFinite(existingMode) ||
1042
+ (existingMode & 0o7777) !== PRIVATE_DIRECTORY_MODE
1043
+ ) {
1044
+ deps.fs.chmodSync(target, PRIVATE_DIRECTORY_MODE);
1045
+ }
1035
1046
  } else if (
1036
1047
  options.applyWindowsAcl === true ||
1037
1048
  (!existed && options.applyWindowsAcl !== false)
@@ -1071,7 +1082,15 @@ export function ensurePrivateFile(target, options = {}) {
1071
1082
  throw new Error(`Expected a file for owner-only storage: ${target}`);
1072
1083
  }
1073
1084
  if (platform !== "win32") {
1074
- deps.fs.chmodSync(target, PRIVATE_FILE_MODE);
1085
+ // Preserve ctime for an already-secure file. Rejected CAS writes and
1086
+ // read-only preflights must not make a persisted physical witness stale.
1087
+ const existingMode = Number(existing.mode);
1088
+ if (
1089
+ !Number.isFinite(existingMode) ||
1090
+ (existingMode & 0o7777) !== PRIVATE_FILE_MODE
1091
+ ) {
1092
+ deps.fs.chmodSync(target, PRIVATE_FILE_MODE);
1093
+ }
1075
1094
  } else if (
1076
1095
  options.applyWindowsAcl === true &&
1077
1096
  (options.deps || !securedWindowsPaths.has(target))