chainlesschain 0.166.84 → 0.166.86

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 (187) hide show
  1. package/README.md +17 -4
  2. package/package.json +3 -2
  3. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  4. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  5. package/src/assets/web-panel/assets/{AIOps-OYLnAlB5.js → AIOps-DZwiXAIh.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-uc9kbLR6.js → ActionButton-aNzXoIJP.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-BH901FG0.js → Analytics-ETQk4ZXt.js} +3 -3
  8. package/src/assets/web-panel/assets/{AppLayout-qypKb8GL.js → AppLayout-D5oEbeEC.js} +4 -4
  9. package/src/assets/web-panel/assets/{Artifacts-Q-uPWyLY.js → Artifacts-ja48FAne.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-B8hMGMim.js → Audit-DlN0YnDt.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-BSs8rJL_.js → BackgroundAgents-CzvyNsRg.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-2YMU_nCd.js → Backup-DXgEC2MQ.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-DyMppTuT.js → BaseInput-D9XqejLx.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-B5QGMaDO.js → Chat-BTwuYUlN.js} +5 -5
  15. package/src/assets/web-panel/assets/{ChatBubbleRenderer-B8ts_JnN.js → ChatBubbleRenderer-0anIC2Wn.js} +1 -1
  16. package/src/assets/web-panel/assets/{Checkbox-DAvtgEGd.js → Checkbox-CLuaQbDt.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-BSChEVM3.js → Codegen-KwQUZs8A.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-G0zYohIP.js → Col-BCmROkzc.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-CzCN0B_p.js → Community-CXlUEHR4.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-DrIN3G8z.js → Compact-Dzx-lWI2.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-CIsLa64Z.js → Compliance-CfXW82FF.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-CGYoafsB.js → Cowork-DIISTRm2.js} +3 -3
  23. package/src/assets/web-panel/assets/{Cron-DJ1JPXuH.js → Cron-nyJ-KcFE.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-B-IqVFJl.js → Crosschain-Dtv1E4cV.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-CAAczPz_.js → DID-BCTpektS.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-Dkpel8VE.js → Dashboard-C1Q_m0So.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-ETNBf8-Z.js → Dropdown-BrQ5_PPL.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-CJGDyjVD.js → EmailListRenderer-DjoNlONP.js} +1 -1
  29. package/src/assets/web-panel/assets/{EvolutionSettings-CL_hvVYe.js → EvolutionSettings-DaGaE6bX.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-iexOh6Dh.js → FamilyGuardDashboard-nJDWBECt.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-DnDHvV5D.js → Federation-DhMUY_wG.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-DLCDJjUp.js → FormItemContext-CbONJPuc.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-CaCgXCjS.js → GenericCardRenderer-CDdX4dX-.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-BVm-euUF.js → Git-DXbDVJdR.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-DPnUvLWr.js → Governance-DbAfAvs1.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-5qCWSYKl.js → Inference-Dy-fGea2.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-DXdv2oEh.js → KnowledgeGraph-DAxRpGUF.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-BF2f7bBA.js → Logs-5L1UCR8X.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-u0FPafvT.js +1 -0
  40. package/src/assets/web-panel/assets/{Marketplace-Bp-0Y7Ng.js → Marketplace-DUk_McDG.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-dm-Xdi63.js → McpTools-B6PNsZ_p.js} +2 -2
  42. package/src/assets/web-panel/assets/{Memory-TBHK031z.js → Memory-BUUs8j5F.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-BTILAlds.js → MobileBridge-pXRsgyuO.js} +1 -1
  44. package/src/assets/web-panel/assets/{MobileProjects-DC0Clgzs.js → MobileProjects-D7v-2Hwm.js} +1 -1
  45. package/src/assets/web-panel/assets/{Mtc-BvyWkmRw.js → Mtc-Deaj4faB.js} +4 -4
  46. package/src/assets/web-panel/assets/{MtcAudit-D7ksLoNa.js → MtcAudit-wUV0VUH1.js} +2 -2
  47. package/src/assets/web-panel/assets/{Multisig-Dpzu7X1t.js → Multisig-C67IIkts.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-DZ0ADHSB.js → NLProgramming-BCA_CWrW.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-hIdkmRug.js → Notes-D9eu14hQ.js} +4 -4
  50. package/src/assets/web-panel/assets/{NotificationSettings-R3jyP6Vf.js → NotificationSettings-Cnd-J4LM.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-d1zrFkqx.js → OrderTableRenderer-C4pHUuyZ.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-DoA5WMcc.js → Organization-4YExAbnp.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-DHjIloAQ.js → Overflow-BF-wFuZz.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-2TMon_Wy.js → P2P-CdiajqCT.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-DEUxGSWO.js → PdhVaultBrowser-BkjfS6Th.js} +5 -5
  56. package/src/assets/web-panel/assets/{Permissions-CxKcZ-8X.js → Permissions-DrZ4D78_.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-CcYtkHqc.js → PersonalDataHub-lDc6OXjz.js} +6 -6
  58. package/src/assets/web-panel/assets/{Pipeline-BxwOuq22.js → Pipeline-DqDS9ON1.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-VIx9pdsH.js → Privacy-BMekZRZu.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-COjp_erK.js → ProjectInit-LYS7NFvJ.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-x9NrZU80.js → ProjectSettings-CcJNHk9A.js} +2 -2
  62. package/src/assets/web-panel/assets/{Projects-DMgkw7QJ.js → Projects-DrfAHgdF.js} +1 -1
  63. package/src/assets/web-panel/assets/{Providers-B_fR7Y7Y.js → Providers-aqsly9SR.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-BUvzrn3V.js → QrScannerModal-ZWvR3zqg.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-C0sJwLy2.js → QuickAsk-BT5CgsvW.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-CJC7Wpce.js → Recommend-Cz8Dz2RN.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-B62J6ZP0.js → RemoteSession-BlxPVbtQ.js} +2 -2
  68. package/src/assets/web-panel/assets/{Reputation-4f48kVBn.js → Reputation-B3VGBE-1.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-EzEMaYoV.js → Row-Bwv7vSEA.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-Dy0wD83j.js → RssFeed-DHI085r_.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-GeVRHQND.js → Search-DBler5T0.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-omLr1J7w.js → Security-BZdw43NW.js} +3 -3
  73. package/src/assets/web-panel/assets/{Services-DmGPaDAs.js → Services-Cslk5lUj.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-CAla21Gm.js → Skeleton-Bur-C-25.js} +1 -1
  75. package/src/assets/web-panel/assets/Skills-v0zgLy0X.js +1 -0
  76. package/src/assets/web-panel/assets/{Sla-Cwi15-pZ.js → Sla-B1OWgQ51.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-Cs8Zi1J8.js → SpeechSettings-CaBcEaV4.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-ipuJsDKP.js → SyncSettings-CBbG8L6J.js} +2 -2
  79. package/src/assets/web-panel/assets/Tasks-C7EjXrnN.js +1 -0
  80. package/src/assets/web-panel/assets/{Templates-slUp9yXF.js → Templates-VBBOZ1H5.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-6edCSG5K.js → Tenant-CtQTj7IF.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-DKq84G3h.js → Terminal-DXVTQjnl.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-Dg0Aw6se.js → TimelineRenderer-BRIno0aN.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-D63h7qYd.js → Tokens-uiiAfkYN.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-F7gTP_gv.js → Trigger-CPApN8o6.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-zw8Zqp8k.js → Trust-T2jKVwCE.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-C-YBUmod.js → UkeySign-C2U_gbzP.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-AhVxTgmC.js → VideoEditing-BCdlr3Kj.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-C58zxjIg.js → Wallet-CWnjoSc1.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-D-n-rlfI.js → WebAuthn-CqSbYqHq.js} +4 -4
  91. package/src/assets/web-panel/assets/{WorkflowEditor-Ddc8gGD4.js → WorkflowEditor-Dr_I3jcr.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-C66V_U7w.js → chat-Cg21FKdE.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-BcJyVZRj.js → colors-CK85tY9e.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-UgUXWcgP.js → compact-item-m-XYPC7W.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-oxEKHKYP.js → createContext-BmiEeNvs.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-CkMEODFC.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-CbqzrKQO.js → hasIn-CCw4KAoc.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CH13yc9x.js → index-6jXf-Lln.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BdFgUn1S.js → index-8KzhlvJ9.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-D3xNhHF3.js → index-B7xJkhtJ.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DAIuMvVR.js → index-B8IC9ZeZ.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CrG-1Kqs.js → index-BGUYgc96.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-Bu1A2sXy.js → index-BGb1j2z9.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BQlGMcZ9.js → index-BHsiSoGY.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DWFNv_EG.js → index-BIOTRzaF.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BrWZmUdA.js → index-BStyjmLG.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-C-p9hhAz.js → index-BUYyhYP6.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CM2S5TJZ.js → index-BcfVHmFK.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-7lSY8B9T.js → index-BiVPFsat.js} +1 -1
  110. package/src/assets/web-panel/assets/index-BjE5qaKn.js +1 -0
  111. package/src/assets/web-panel/assets/{index-B-h0Jgtj.js → index-Br0ZNdu1.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-D8rgeCoC.js → index-Br9t_I1X.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DRrxJygS.js → index-BtvVupUL.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DO-5vgTW.js → index-Bv90WEWa.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DBEL07sa.js → index-BvjC3t35.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-UpN_kC9R.js → index-ByVvAXdl.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-SzGgAqai.js → index-CBcgIWOe.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-C3j9jTfB.js → index-CN2xbLGz.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-Bmhw_Ler.js → index-CYEzWoyZ.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Crqqbu-W.js → index-Cf5arJie.js} +1 -1
  121. package/src/assets/web-panel/assets/index-CsFH-2Zk.js +1 -0
  122. package/src/assets/web-panel/assets/{index-BMOY4PSW.js → index-D1sU5-XA.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BT2PZ9df.js → index-D7gS_TJl.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-YvNh1UdB.js → index-DAlGVix-.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BZ7mv_oS.js → index-DI9GJFcn.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-Ov77O9mH.js → index-DIlGxS2W.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CJluGB2F.js → index-DMbrl43d.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-C6X26RJu.js → index-DmSgfXuz.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DMbJ06sF.js → index-DpIy2HzE.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CUmYwZyf.js → index-DxRhnsEC.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BNIq0NAn.js → index-IskAKXhx.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DR4YKlLt.js → index-J81uvUMN.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-RnxuL8wM.js → index-OIbfsi-o.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-Bp9rfr-P.js → index-lhoexBDH.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-BpqJNeIj.js → index-sWUmvT8H.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CuZupgUw.js → index-yYmjkf38.js} +3 -3
  137. package/src/assets/web-panel/assets/{initDefaultProps-BK9ncFiF.js → initDefaultProps-Cc1XOmNe.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-BxzXi1Pe.js → motion-BehsX88u.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-CRmwRuah.js → move-BPx2YsqY.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-BQq5_Znx.js → mtc-parser-BSFJgFQA.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-Ch4VNY2j.js → omit-DBN5KUeo.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-CUioBhrz.js → pickAttrs-BDK1yZ-9.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-Hj3o1CrW.js → placementArrow-C4rMmKNJ.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-PfqJrlkI.js → responsiveObserve-BoeoOnxX.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-BXcnR-Lu.js → slide-BKlr1bl9.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-C74a-ofo.js → statusUtils-Ky1apWPI.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-Bq57I3ls.js → styleChecker-DxxR7uXk.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-ITPUjcDw.js → useFlexGapSupport-DmsUqVe9.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-DzPuXDIS.js → useFs-CwkG7sOg.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-DYSgt9L5.js → usePersonalDataHub-CddgeEfF.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-NPs_EJEk.js → vnode-C_-H8i4Y.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-DZzO6KVS.js → zoom-aAsHUMNE.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/commands/permissions.js +27 -2
  155. package/src/data/changelog.json +26 -0
  156. package/src/lib/background-interaction-resolver.js +4 -0
  157. package/src/lib/context-memory-kernel/durable-memory-port.js +71 -15
  158. package/src/lib/context-memory-kernel/index.js +1 -0
  159. package/src/lib/context-memory-kernel/memory-service.js +43 -16
  160. package/src/lib/context-memory-kernel/runtime.js +5 -3
  161. package/src/lib/context-memory-kernel/segmented-memory-port.js +687 -0
  162. package/src/lib/packer/pack-update-applier.js +41 -17
  163. package/src/lib/packer/pkg-config-generator.js +119 -2
  164. package/src/lib/packer/pkg-runner.js +7 -0
  165. package/src/lib/packer/pkg-targets.js +83 -0
  166. package/src/lib/packer/web-panel-builder.js +7 -0
  167. package/src/lib/permission-authority.js +157 -13
  168. package/src/lib/process-execution-broker/index.js +11 -0
  169. package/src/lib/process-execution-broker/linux-subreaper-helper.js +87 -13
  170. package/src/lib/process-execution-broker/packer-build-policy.js +31 -0
  171. package/src/lib/process-execution-broker/platform-sandbox.js +11 -3
  172. package/src/lib/sandbox-egress-authority-monitor.js +62 -5
  173. package/src/lib/scoped-permission-store.js +119 -6
  174. package/src/lib/settings-authority-domain.cjs +37 -2
  175. package/src/lib/settings-loader.cjs +112 -42
  176. package/src/lib/settings-permission-authority.cjs +373 -0
  177. package/src/runtime/agent-core.js +79 -383
  178. package/src/runtime/headless-runner.js +8 -3
  179. package/src/runtime/headless-stream.js +10 -3
  180. package/src/runtime/permission-authority-host.js +137 -0
  181. package/src/runtime/provider-stream-state.js +379 -0
  182. package/src/assets/web-panel/assets/MarkdownRenderer-qNJS3ygh.js +0 -1
  183. package/src/assets/web-panel/assets/Skills-QqEyIKOI.js +0 -1
  184. package/src/assets/web-panel/assets/Tasks-BbVAg9Z7.js +0 -1
  185. package/src/assets/web-panel/assets/devWarning-BoDvrsgY.js +0 -1
  186. package/src/assets/web-panel/assets/index-BhqRbl5P.js +0 -1
  187. package/src/assets/web-panel/assets/index-DreT6MIR.js +0 -1
@@ -207,7 +207,7 @@ export async function scheduleReplace(ctx = {}) {
207
207
  : null;
208
208
 
209
209
  if (platform === "win32") {
210
- return scheduleWindowsTransaction({
210
+ return await scheduleWindowsTransaction({
211
211
  operation: "update",
212
212
  resolvedNewPath,
213
213
  layout,
@@ -681,7 +681,7 @@ function rollbackFailedPosixUpdate(ctx) {
681
681
  }
682
682
  }
683
683
 
684
- function scheduleWindowsTransaction(ctx) {
684
+ async function scheduleWindowsTransaction(ctx) {
685
685
  const {
686
686
  operation,
687
687
  resolvedNewPath,
@@ -771,26 +771,39 @@ function scheduleWindowsTransaction(ctx) {
771
771
 
772
772
  let ready = false;
773
773
  let childError = null;
774
+ let childExited = false;
775
+ const onChildExit = () => {
776
+ childExited = true;
777
+ };
778
+ const hasChildFailed = () =>
779
+ Boolean(childError) ||
780
+ childExited ||
781
+ child.exitCode != null ||
782
+ child.signalCode != null;
774
783
  if (typeof child.once === "function") {
775
784
  // Native spawn failures are normally emitted asynchronously. Attach
776
- // before the synchronous readiness wait so both the no-ready and the
785
+ // before yielding for readiness so both the no-ready and the
777
786
  // post-transfer paths always consume the ChildProcess error event.
778
787
  child.once("error", (error) => {
779
788
  childError = error;
780
789
  });
790
+ child.once("exit", onChildExit);
781
791
  }
782
792
  try {
783
793
  ready =
784
794
  Boolean(
785
- waitForReadyImpl({
795
+ await waitForReadyImpl({
786
796
  readyPath,
787
797
  transactionId,
788
798
  timeoutMs: WINDOWS_SIDECAR_READY_TIMEOUT_MS,
789
799
  child,
800
+ hasChildFailed,
790
801
  }),
791
- ) && !childError;
802
+ ) && !hasChildFailed();
792
803
  } catch {
793
804
  ready = false;
805
+ } finally {
806
+ child.removeListener?.("exit", onChildExit);
794
807
  }
795
808
  if (!ready) {
796
809
  try {
@@ -840,6 +853,11 @@ function scheduleWindowsTransaction(ctx) {
840
853
  function windowsGenerationJournalHelperSource() {
841
854
  return String.raw`param()
842
855
  $ErrorActionPreference = 'Stop'
856
+ # The journal contains only JSON primitives. Use the inbox .NET serializer and
857
+ # file/path APIs so each phase does not repeat PowerShell module discovery.
858
+ # Keep the same synchronous atomic replacement and durable flush protocol.
859
+ [void][Reflection.Assembly]::Load('System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35')
860
+ $Serializer = [Web.Script.Serialization.JavaScriptSerializer]::new()
843
861
  $Action = [string]$env:JOURNAL_ACTION
844
862
  $JournalPath = [string]$env:JOURNAL_FILE
845
863
  $RetiredPath = [string]$env:JOURNAL_RETIRED
@@ -859,7 +877,7 @@ if (($Phase -eq 'committed') -ne ($Decision -eq 'commit')) { throw 'Invalid phas
859
877
  if ($Action -eq 'write' -and $Phase -eq 'prepared') {
860
878
  if ([IO.File]::Exists($JournalPath)) { throw 'A generation journal already exists' }
861
879
  $Bytes = [Convert]::FromBase64String([string]$env:JOURNAL_INITIAL_BASE64)
862
- $Journal = [Text.Encoding]::UTF8.GetString($Bytes) | ConvertFrom-Json
880
+ $Journal = $Serializer.DeserializeObject([Text.Encoding]::UTF8.GetString($Bytes))
863
881
  $ParentSource = @'
864
882
  using System;
865
883
  using System.Runtime.InteropServices;
@@ -887,10 +905,10 @@ public static class CcParentProcess {
887
905
  }
888
906
  '@
889
907
  Add-Type -TypeDefinition $ParentSource -Language CSharp
890
- $Journal | Add-Member -NotePropertyName ownerPid -NotePropertyValue ([CcParentProcess]::Get())
908
+ $Journal.Add('ownerPid', [CcParentProcess]::Get())
891
909
  } else {
892
910
  if (-not [IO.File]::Exists($JournalPath)) { throw 'Generation journal is missing' }
893
- $Journal = Get-Content -Raw -LiteralPath $JournalPath | ConvertFrom-Json
911
+ $Journal = $Serializer.DeserializeObject([IO.File]::ReadAllText($JournalPath, [Text.Encoding]::UTF8))
894
912
  }
895
913
 
896
914
  $ValidBefore = {
@@ -918,7 +936,7 @@ if ($Action -eq 'retire') {
918
936
  if ([string]$Journal.phase -ne $Phase -or [string]$Journal.decision -ne $Decision) {
919
937
  throw 'Generation journal decision changed before retirement'
920
938
  }
921
- $ReplacedPath = Join-Path (Split-Path -Parent $JournalPath) ('.chainlesschain.journal-retired-previous-' + [guid]::NewGuid().ToString('N'))
939
+ $ReplacedPath = [IO.Path]::Combine([IO.Path]::GetDirectoryName($JournalPath), ('.chainlesschain.journal-retired-previous-' + [guid]::NewGuid().ToString('N')))
922
940
  try {
923
941
  if ([IO.File]::Exists($RetiredPath)) {
924
942
  [IO.File]::Replace($JournalPath, $RetiredPath, $ReplacedPath, $true)
@@ -972,11 +990,11 @@ if ($Phase -ne 'prepared') {
972
990
  $Journal.phase = $Phase
973
991
  $Journal.decision = $Decision
974
992
  $Journal.updatedAt = [DateTimeOffset]::UtcNow.ToString('o')
975
- $Directory = Split-Path -Parent $JournalPath
976
- $StagingPath = Join-Path $Directory ('.chainlesschain.journal-' + [guid]::NewGuid().ToString('N'))
977
- $ReplacedPath = Join-Path $Directory ('.chainlesschain.journal-previous-' + [guid]::NewGuid().ToString('N'))
993
+ $Directory = [IO.Path]::GetDirectoryName($JournalPath)
994
+ $StagingPath = [IO.Path]::Combine($Directory, ('.chainlesschain.journal-' + [guid]::NewGuid().ToString('N')))
995
+ $ReplacedPath = [IO.Path]::Combine($Directory, ('.chainlesschain.journal-previous-' + [guid]::NewGuid().ToString('N')))
978
996
  try {
979
- $Payload = $Journal | ConvertTo-Json -Compress -Depth 8
997
+ $Payload = $Serializer.Serialize($Journal)
980
998
  $PayloadBytes = [Text.UTF8Encoding]::new($false).GetBytes($Payload + [Environment]::NewLine)
981
999
  $Stream = [IO.File]::Open($StagingPath, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None)
982
1000
  try {
@@ -1982,7 +2000,7 @@ export async function rollbackLastKnownGood(ctx = {}) {
1982
2000
  : null;
1983
2001
 
1984
2002
  if (platform === "win32") {
1985
- const result = scheduleWindowsTransaction({
2003
+ const result = await scheduleWindowsTransaction({
1986
2004
  operation: "rescue",
1987
2005
  resolvedNewPath: rescueStagingPath,
1988
2006
  layout,
@@ -2752,10 +2770,15 @@ function restartVerifiedExecutable(targetPath, restart, spawnImpl) {
2752
2770
  }
2753
2771
  }
2754
2772
 
2755
- function waitForWindowsSidecarReady({ readyPath, transactionId, timeoutMs }) {
2773
+ async function waitForWindowsSidecarReady({
2774
+ readyPath,
2775
+ transactionId,
2776
+ timeoutMs,
2777
+ hasChildFailed,
2778
+ }) {
2756
2779
  const deadline = Date.now() + timeoutMs;
2757
- const waitCell = new Int32Array(new SharedArrayBuffer(4));
2758
2780
  while (Date.now() < deadline) {
2781
+ if (hasChildFailed()) return false;
2759
2782
  const stat = lstatOrNull(readyPath);
2760
2783
  if (stat) {
2761
2784
  try {
@@ -2769,7 +2792,8 @@ function waitForWindowsSidecarReady({ readyPath, transactionId, timeoutMs }) {
2769
2792
  return false;
2770
2793
  }
2771
2794
  }
2772
- Atomics.wait(waitCell, 0, 0, 25);
2795
+ // Yield so native spawn errors and early exits can abort the handshake.
2796
+ await new Promise((resolve) => setTimeout(resolve, 25));
2773
2797
  }
2774
2798
  return false;
2775
2799
  }
@@ -20,6 +20,104 @@
20
20
  import fs from "node:fs";
21
21
  import { createRequire } from "node:module";
22
22
  import path from "node:path";
23
+ import { linuxSubreaperTargetArchitectures } from "./pkg-targets.js";
24
+ import {
25
+ LINUX_SUBREAPER_SOURCE_DIGEST,
26
+ MAX_SUBREAPER_IMAGE_BYTES,
27
+ MAX_SUBREAPER_MANIFEST_BYTES,
28
+ subreaperDigest,
29
+ validateLinuxSubreaperArtifact,
30
+ } from "../process-execution-broker/linux-subreaper-artifact.js";
31
+
32
+ function linuxSubreaperAssets(cliRoot, targets) {
33
+ const architectures = linuxSubreaperTargetArchitectures(targets);
34
+ if (!architectures.length) return [];
35
+ const root = fs.realpathSync(cliRoot);
36
+ const assets = [];
37
+ function read(relative, maximum) {
38
+ const file = path.join(root, relative);
39
+ let directory = path.dirname(file);
40
+ while (directory !== root) {
41
+ const stat = fs.lstatSync(directory);
42
+ if (stat.isSymbolicLink() || !stat.isDirectory())
43
+ throw new Error("Invalid standalone Linux helper directory");
44
+ directory = path.dirname(directory);
45
+ }
46
+ const before = fs.lstatSync(file, { bigint: true });
47
+ if (
48
+ !before.isFile() ||
49
+ before.isSymbolicLink() ||
50
+ before.nlink !== 1n ||
51
+ before.size < 1n ||
52
+ before.size > BigInt(maximum)
53
+ )
54
+ throw new Error("Invalid standalone Linux helper asset");
55
+ const fd = fs.openSync(
56
+ file,
57
+ fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0),
58
+ );
59
+ try {
60
+ const opened = fs.fstatSync(fd, { bigint: true });
61
+ if (
62
+ opened.dev !== before.dev ||
63
+ opened.ino !== before.ino ||
64
+ opened.size !== before.size
65
+ )
66
+ throw new Error("Standalone Linux helper asset changed");
67
+ const bytes = Buffer.alloc(Number(opened.size));
68
+ let offset = 0;
69
+ while (offset < bytes.length) {
70
+ const count = fs.readSync(
71
+ fd,
72
+ bytes,
73
+ offset,
74
+ bytes.length - offset,
75
+ offset,
76
+ );
77
+ if (count <= 0) throw new Error("Short standalone Linux helper asset");
78
+ offset += count;
79
+ }
80
+ const after = fs.fstatSync(fd, { bigint: true });
81
+ if (
82
+ after.size !== before.size ||
83
+ after.mtimeNs !== before.mtimeNs ||
84
+ after.ctimeNs !== before.ctimeNs ||
85
+ after.nlink !== 1n
86
+ )
87
+ throw new Error("Standalone Linux helper asset changed");
88
+ assets.push(file);
89
+ return bytes;
90
+ } finally {
91
+ fs.closeSync(fd);
92
+ }
93
+ }
94
+ try {
95
+ const source = read(
96
+ "src/lib/process-execution-broker/linux-subreaper-supervisor.c",
97
+ 128 * 1024,
98
+ );
99
+ if (
100
+ subreaperDigest(
101
+ Buffer.from(source.toString("utf8").replace(/\r\n/g, "\n")),
102
+ ) !== LINUX_SUBREAPER_SOURCE_DIGEST
103
+ )
104
+ throw new Error("Standalone Linux helper source digest mismatch");
105
+ for (const arch of architectures) {
106
+ const directory = `src/assets/linux-subreaper/linux-${arch}`;
107
+ validateLinuxSubreaperArtifact(
108
+ read(`${directory}/manifest.json`, MAX_SUBREAPER_MANIFEST_BYTES),
109
+ read(`${directory}/supervisor`, MAX_SUBREAPER_IMAGE_BYTES),
110
+ { arch },
111
+ );
112
+ }
113
+ return assets.map(posixify);
114
+ } catch (cause) {
115
+ throw new Error(
116
+ "Standalone Linux targets require valid precompiled process supervision assets for every target architecture",
117
+ { cause },
118
+ );
119
+ }
120
+ }
23
121
 
24
122
  function capsuleBuilderAssets(cliRoot) {
25
123
  const requireFromCli = createRequire(path.join(cliRoot, "package.json"));
@@ -108,6 +206,13 @@ export function generatePkgConfig(ctx) {
108
206
  updatePublicKey = null,
109
207
  } = ctx;
110
208
 
209
+ // pkg silently turns an empty target list into a host build. Require an
210
+ // explicit target so asset admission and the emitted build cannot diverge.
211
+ if (!Array.isArray(targets) || targets.length === 0)
212
+ throw new Error(
213
+ "Standalone builds require at least one explicit pkg target",
214
+ );
215
+
111
216
  const bakedTokenMode =
112
217
  typeof runtime.token === "string" ? runtime.token : "auto";
113
218
  const bakedHost =
@@ -432,6 +537,7 @@ export function generatePkgConfig(ctx) {
432
537
  `${posixify(cliRoot)}/src/lib/process-execution-broker/macos-mcp-launcher-protocol.json`,
433
538
  `${posixify(cliRoot)}/install/install.ps1`,
434
539
  ...capsuleBuilderAssets(cliRoot).map(posixify),
540
+ ...linuxSubreaperAssets(cliRoot, targets),
435
541
  ];
436
542
  if (prebuildsDir) assets.push(`${posixify(prebuildsDir)}/**/*`);
437
543
  // Project mode: bundle the collected .chainlesschain/ snapshot as an asset.
@@ -450,8 +556,8 @@ export function generatePkgConfig(ctx) {
450
556
  `${posixify(cliRoot)}/src/**/*.js`,
451
557
  `${posixify(cliRoot)}/src/**/*.cjs`,
452
558
  `${posixify(cliRoot)}/bin/**/*.js`,
453
- ],
454
- assets,
559
+ ].map((pattern) => pkgRelativePath(pkgConfigDir, pattern)),
560
+ assets: assets.map((pattern) => pkgRelativePath(pkgConfigDir, pattern)),
455
561
  targets,
456
562
  outputPath: path.dirname(outputPath),
457
563
  compress: compress ? "GZip" : "None",
@@ -476,3 +582,14 @@ export function generatePkgConfig(ctx) {
476
582
  function posixify(p) {
477
583
  return p.replace(/\\/g, "/");
478
584
  }
585
+
586
+ function pkgRelativePath(directory, pattern) {
587
+ // pkg's walker joins every pattern to its config directory, even when the
588
+ // pattern is absolute. Emit relative globs so assets are actually embedded.
589
+ const relative = path.relative(directory, pattern);
590
+ if (path.isAbsolute(relative) || path.win32.isAbsolute(relative))
591
+ throw new Error(
592
+ "Standalone build assets and temporary config must be on the same filesystem drive",
593
+ );
594
+ return posixify(relative);
595
+ }
@@ -16,6 +16,10 @@ import path from "node:path";
16
16
  import { createRequire } from "node:module";
17
17
  import { PackError, EXIT } from "./errors.js";
18
18
  import executionBroker from "../process-execution-broker/index.js";
19
+ import {
20
+ createPackerBuildPolicy,
21
+ PACKER_BUILD_TIMEOUT_MS,
22
+ } from "../process-execution-broker/packer-build-policy.js";
19
23
 
20
24
  export const _deps = {
21
25
  spawnSync: (...args) => executionBroker.spawnSync(...args),
@@ -55,6 +59,9 @@ export function runPkg(ctx) {
55
59
  origin: "packer:pkg",
56
60
  scope: "pack",
57
61
  policy: "allow",
62
+ sandboxPolicy: createPackerBuildPolicy("packer:pkg"),
63
+ timeout: PACKER_BUILD_TIMEOUT_MS,
64
+ killSignal: "SIGKILL",
58
65
  shell: false,
59
66
  cwd: path.dirname(pkgConfigFile),
60
67
  stdio: "inherit",
@@ -0,0 +1,83 @@
1
+ // Only resolve the platform/architecture needed to admit helper assets. Loading
2
+ // pkg's config module also loads pkg-fetch, whose Linux host probe runs ldd at
3
+ // import time. Target admission must remain free of subprocess side effects.
4
+ // Keep these token aliases aligned with the installed pkg parser via the
5
+ // differential tests. Linux, Alpine and linuxstatic need the same static ELF.
6
+ // ARM32 hosts require an explicit supported target architecture: unlike pkg,
7
+ // admission never invokes uname to infer a different ABI from the kernel.
8
+ const PLATFORMS = new Set([
9
+ "alpine",
10
+ "freebsd",
11
+ "linux",
12
+ "linuxstatic",
13
+ "macos",
14
+ "win",
15
+ ]);
16
+ const ARCHITECTURES = new Set([
17
+ "x64",
18
+ "x86",
19
+ "armv7",
20
+ "arm64",
21
+ "ppc64",
22
+ "s390x",
23
+ "riscv64",
24
+ "loong64",
25
+ ]);
26
+ const PLATFORM_ALIASES = new Map([
27
+ ["darwin", "macos"],
28
+ ["lin", "linux"],
29
+ ["mac", "macos"],
30
+ ["osx", "macos"],
31
+ ["win32", "win"],
32
+ ["windows", "win"],
33
+ ]);
34
+ const ARCHITECTURE_ALIASES = new Map([
35
+ ["ia32", "x86"],
36
+ ["x86_64", "x64"],
37
+ ]);
38
+
39
+ export function linuxSubreaperTargetArchitectures(
40
+ targets,
41
+ { platform = process.platform, arch = process.arch } = {},
42
+ ) {
43
+ if (
44
+ !Array.isArray(targets) ||
45
+ targets.length === 0 ||
46
+ targets.some(
47
+ (target) =>
48
+ typeof target !== "string" || !target.trim().replace(/-/g, ""),
49
+ )
50
+ ) {
51
+ throw new Error("Standalone builds require explicit nonempty pkg targets");
52
+ }
53
+ const architectures = new Set();
54
+ for (const rawTarget of targets) {
55
+ const target = rawTarget.trim();
56
+ let targetPlatform = PLATFORM_ALIASES.get(platform) || platform;
57
+ let targetArch = ARCHITECTURE_ALIASES.get(arch) || arch;
58
+ if (target !== "host") {
59
+ for (const token of target.split("-")) {
60
+ if (!token || token === "latest" || token.startsWith("node")) continue;
61
+ const resolvedPlatform = PLATFORM_ALIASES.get(token) || token;
62
+ if (PLATFORMS.has(resolvedPlatform)) {
63
+ targetPlatform = resolvedPlatform;
64
+ continue;
65
+ }
66
+ const resolvedArch = ARCHITECTURE_ALIASES.get(token) || token;
67
+ if (ARCHITECTURES.has(resolvedArch)) {
68
+ targetArch = resolvedArch;
69
+ continue;
70
+ }
71
+ throw new Error(`Unknown token '${token}' in '${target}'`);
72
+ }
73
+ }
74
+ if (!["linux", "alpine", "linuxstatic"].includes(targetPlatform)) continue;
75
+ if (targetArch !== "x64" && targetArch !== "arm64") {
76
+ throw new Error(
77
+ `Unsupported standalone Linux process supervision architecture: ${targetArch}`,
78
+ );
79
+ }
80
+ architectures.add(targetArch);
81
+ }
82
+ return [...architectures];
83
+ }
@@ -12,6 +12,10 @@ import fs from "node:fs";
12
12
  import path from "node:path";
13
13
  import { PackError, EXIT } from "./errors.js";
14
14
  import executionBroker from "../process-execution-broker/index.js";
15
+ import {
16
+ createPackerBuildPolicy,
17
+ PACKER_BUILD_TIMEOUT_MS,
18
+ } from "../process-execution-broker/packer-build-policy.js";
15
19
 
16
20
  export const _deps = {
17
21
  spawnSync: (...args) => executionBroker.spawnSync(...args),
@@ -61,6 +65,9 @@ export function ensureWebPanel(ctx) {
61
65
  origin: "packer:web-panel-build",
62
66
  scope: "pack",
63
67
  policy: "allow",
68
+ sandboxPolicy: createPackerBuildPolicy("packer:web-panel-build"),
69
+ timeout: PACKER_BUILD_TIMEOUT_MS,
70
+ killSignal: "SIGKILL",
64
71
  shell: false,
65
72
  cwd: cliRoot,
66
73
  stdio: "inherit",
@@ -1,16 +1,19 @@
1
1
  /** Resolve static settings plus CLI-owned workspace-scoped permission rules. */
2
2
 
3
3
  import settingsLoader from "./settings-loader.cjs";
4
+ import persistentSettings from "./settings-permission-authority.cjs";
4
5
  import path from "node:path";
5
6
  import {
6
7
  ScopedPermissionStore,
7
8
  getScopedPermissionRevision,
8
9
  subscribeScopedPermissionRevision,
10
+ projectScopedPermissionObservation,
9
11
  } from "./scoped-permission-store.js";
10
12
 
11
13
  const {
12
14
  applyManagedPermissionPolicy,
13
- loadSettings,
15
+ inspectSettingsSources,
16
+ projectSettingsObservation,
14
17
  getSettingsPermissionRevision,
15
18
  subscribeSettingsPermissionRevision,
16
19
  } = settingsLoader;
@@ -47,9 +50,14 @@ const combinedAuthority = Object.freeze({
47
50
  });
48
51
 
49
52
  function freezeSnapshot(value) {
50
- if (value && typeof value === "object") {
51
- for (const child of Object.values(value)) freezeSnapshot(child);
52
- Object.freeze(value);
53
+ const pending = [value];
54
+ const seen = new WeakSet();
55
+ while (pending.length) {
56
+ const current = pending.pop();
57
+ if (!current || typeof current !== "object" || seen.has(current)) continue;
58
+ seen.add(current);
59
+ for (const child of Object.values(current)) pending.push(child);
60
+ Object.freeze(current);
53
61
  }
54
62
  return value;
55
63
  }
@@ -80,15 +88,41 @@ export function loadPermissionAuthority({
80
88
  env = process.env,
81
89
  baseRules = null,
82
90
  scopedStore = null,
91
+ settingsAuthority: persistentAuthority = null,
83
92
  } = {}) {
84
93
  const settingsRevision = getSettingsPermissionRevision();
85
94
  const scopedRevision = baseRules ? null : getScopedPermissionRevision();
86
- const loaded = loadSettings({
87
- cwd,
88
- settingsFile,
89
- managedSettingsFile,
90
- env,
91
- });
95
+ const persistent = persistentAuthority
96
+ ? persistentSettings.readSettingsPermissionAuthority(persistentAuthority)
97
+ : null;
98
+ if (persistentAuthority) {
99
+ const bound =
100
+ persistentSettings.settingsPermissionAuthorityOptions(
101
+ persistentAuthority,
102
+ );
103
+ if (
104
+ path.resolve(cwd) !== bound.cwd ||
105
+ (settingsFile &&
106
+ path.resolve(cwd, settingsFile) !== bound.settingsFile) ||
107
+ (managedSettingsFile &&
108
+ path.resolve(managedSettingsFile) !== bound.managedSettingsFile)
109
+ )
110
+ throw new TypeError("persistent settings authority options mismatch");
111
+ }
112
+ const settingsObservation =
113
+ persistent?.observation ||
114
+ inspectSettingsSources({
115
+ cwd,
116
+ settingsFile,
117
+ managedSettingsFile,
118
+ env,
119
+ });
120
+ const loaded = persistent
121
+ ? persistentSettings.projectSettingsPermissionObservation(
122
+ settingsObservation,
123
+ { env },
124
+ )
125
+ : projectSettingsObservation(settingsObservation, { env });
92
126
  const sources = baseRules ? {} : { ...loaded.sources };
93
127
  let rules = baseRules
94
128
  ? applyManagedPermissionPolicy(baseRules, loaded.managed, sources)
@@ -98,7 +132,29 @@ export function loadPermissionAuthority({
98
132
  // Explicit caller rules retain their historical replacement semantics.
99
133
  // Managed-only policy also suppresses every user-owned scoped grant.
100
134
  if (!baseRules) {
101
- scoped = (scopedStore || new ScopedPermissionStore({ cwd })).list();
135
+ const bound = persistentAuthority
136
+ ? persistentSettings.settingsPermissionAuthorityOptions(
137
+ persistentAuthority,
138
+ )
139
+ : null;
140
+ const store =
141
+ scopedStore ||
142
+ new ScopedPermissionStore({
143
+ cwd,
144
+ ...(bound
145
+ ? {
146
+ filePath: bound.scopedFile,
147
+ settingsAuthority: persistentAuthority,
148
+ }
149
+ : {}),
150
+ });
151
+ scoped = persistent
152
+ ? projectScopedPermissionObservation(
153
+ store,
154
+ persistentAuthority,
155
+ persistent.scopedObservation,
156
+ )
157
+ : store.list();
102
158
  const managedOnly =
103
159
  loaded.managed?.allowManagedPermissionRulesOnly === true;
104
160
  scoped = {
@@ -136,6 +192,17 @@ export function loadPermissionAuthority({
136
192
  error.code = "CC_SCOPED_PERMISSION_AUTHORITY_CHANGED";
137
193
  throw error;
138
194
  }
195
+ if (
196
+ persistent &&
197
+ persistentSettings.readSettingsPermissionAuthority(persistentAuthority)
198
+ .snapshot !== persistent.snapshot
199
+ ) {
200
+ const error = new Error(
201
+ "persistent settings changed while permission authority was loading",
202
+ );
203
+ error.code = "CC_SETTINGS_PERMISSION_AUTHORITY_CHANGED";
204
+ throw error;
205
+ }
139
206
  return freezeSnapshot({
140
207
  ...loaded,
141
208
  rules,
@@ -143,12 +210,18 @@ export function loadPermissionAuthority({
143
210
  scoped,
144
211
  hasRules: hasRules(rules),
145
212
  settingsRevision,
213
+ settingsObservation,
214
+ ...(persistent ? { persistentSettingsRevision: persistent.snapshot } : {}),
146
215
  ...(scopedRevision ? { scopedRevision } : {}),
147
216
  });
148
217
  }
149
218
 
150
219
  export function createPermissionRulesProvider(options = {}) {
151
- const cwd = path.resolve(options.cwd || process.cwd());
220
+ const persistentAuthority = options.settingsAuthority ?? null;
221
+ const bound = persistentAuthority
222
+ ? persistentSettings.settingsPermissionAuthorityOptions(persistentAuthority)
223
+ : null;
224
+ const cwd = path.resolve(options.cwd || bound?.cwd || process.cwd());
152
225
  const captured = Object.freeze({
153
226
  cwd,
154
227
  settingsFile: options.settingsFile
@@ -164,15 +237,86 @@ export function createPermissionRulesProvider(options = {}) {
164
237
  ? freezeSnapshot(cloneRules(options.baseRules))
165
238
  : null,
166
239
  scopedStore: options.scopedStore || null,
240
+ settingsAuthority: persistentAuthority,
167
241
  });
242
+ if (
243
+ bound &&
244
+ (cwd !== bound.cwd ||
245
+ (captured.settingsFile && captured.settingsFile !== bound.settingsFile) ||
246
+ (captured.managedSettingsFile &&
247
+ captured.managedSettingsFile !== bound.managedSettingsFile))
248
+ )
249
+ throw new TypeError("persistent settings authority options mismatch");
168
250
  const provider = () => loadPermissionAuthority(captured);
251
+ const localAuthority = captured.baseRules
252
+ ? settingsAuthority
253
+ : combinedAuthority;
169
254
  providers.set(
170
255
  provider,
171
- captured.baseRules ? settingsAuthority : combinedAuthority,
256
+ persistentAuthority
257
+ ? persistentProviderAuthority(persistentAuthority, localAuthority)
258
+ : localAuthority,
172
259
  );
173
260
  return Object.freeze(provider);
174
261
  }
175
262
 
263
+ function persistentProviderAuthority(binding, localAuthority) {
264
+ let last = null;
265
+ function getSnapshot() {
266
+ const local = localAuthority.getSnapshot();
267
+ const durable =
268
+ persistentSettings.readSettingsPermissionAuthority(binding).snapshot;
269
+ if (last?.local !== local || last?.durable !== durable)
270
+ last = Object.freeze({ local, durable });
271
+ return last;
272
+ }
273
+ return Object.freeze({
274
+ getSnapshot,
275
+ assertWorkspace(cwd) {
276
+ if (
277
+ path.resolve(cwd) !==
278
+ persistentSettings.settingsPermissionAuthorityOptions(binding).cwd
279
+ )
280
+ throw Object.assign(
281
+ new Error("persistent permission authority workspace mismatch"),
282
+ { code: "CC_SETTINGS_AUTHORITY_BINDING_CHANGED" },
283
+ );
284
+ },
285
+ assertWritableRoots(roots) {
286
+ persistentSettings.assertSettingsPermissionWritableRoots(binding, roots);
287
+ },
288
+ subscribePolicyRevision(listener) {
289
+ if (typeof listener !== "function")
290
+ throw new TypeError("permission revision listener must be a function");
291
+ let known = getSnapshot();
292
+ const remove = localAuthority.subscribePolicyRevision(listener);
293
+ // Persistence prevents official ABA loss between polls. Timers provide
294
+ // bounded observation, not a promise that another process has stopped
295
+ // before synchronous addRule returns.
296
+ const timer = setInterval(() => {
297
+ let next;
298
+ try {
299
+ next = getSnapshot();
300
+ } catch {
301
+ next = null;
302
+ }
303
+ if (next === known) return;
304
+ known = next;
305
+ try {
306
+ listener(next);
307
+ } catch {
308
+ /* isolate observers */
309
+ }
310
+ }, 100);
311
+ timer.unref?.();
312
+ return () => {
313
+ clearInterval(timer);
314
+ remove();
315
+ };
316
+ },
317
+ });
318
+ }
319
+
176
320
  // A legacy callback cannot advertise a trusted revision subscription simply
177
321
  // by copying properties onto itself. It still receives sampled revalidation.
178
322
  export function permissionRulesProviderAuthority(provider) {