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
@@ -50,6 +50,7 @@ const _deps = {
50
50
  writeSecurityStore: (...args) => securityStore().writeSecurityStore(...args),
51
51
  withFileLock: (...args) => lockHelper()(...args),
52
52
  };
53
+ const settingsObservations = new WeakSet();
53
54
 
54
55
  // A process-wide owner deliberately revokes unrelated projects too. Matching
55
56
  // only contributing paths misses absent files, parent discovery and aliases.
@@ -145,9 +146,11 @@ function accrete(target, sources, arr, file, kind) {
145
146
  }
146
147
 
147
148
  /** The ordered list of candidate settings files for a cwd. */
148
- function settingsPaths(cwd, explicitFile) {
149
+ function settingsPaths(cwd, explicitFile, userSettingsFile = null) {
149
150
  const home = _deps.homedir();
150
- const list = [path.join(home, ".claude", "settings.json")];
151
+ const list = [
152
+ userSettingsFile || path.join(home, ".claude", "settings.json"),
153
+ ];
151
154
  // When run from a subdirectory, the project-root `.claude` sits BELOW cwd's
152
155
  // own (closest wins) but ABOVE the user layer — so its rules apply yet a
153
156
  // cwd-local settings file still overrides them. Null when cwd IS the root.
@@ -170,10 +173,10 @@ function settingsPaths(cwd, explicitFile) {
170
173
  function inspectSettingsSources(opts = {}) {
171
174
  const cwd = path.resolve(opts.cwd || process.cwd());
172
175
  const candidates = [
173
- ...settingsPaths(cwd, opts.settingsFile),
176
+ ...settingsPaths(cwd, opts.settingsFile, opts.userSettingsFile),
174
177
  managedSettingsPath(opts),
175
178
  ];
176
- return Object.freeze({
179
+ const observation = Object.freeze({
177
180
  schema: "chainlesschain.settings-source-observation/v1",
178
181
  cwd,
179
182
  sources: observeSettingsSources(candidates, {
@@ -181,6 +184,8 @@ function inspectSettingsSources(opts = {}) {
181
184
  maxBytes: opts.maxBytes,
182
185
  }),
183
186
  });
187
+ settingsObservations.add(observation);
188
+ return observation;
184
189
  }
185
190
 
186
191
  /** Organization-controlled settings file. This layer is always highest. */
@@ -273,13 +278,50 @@ function loadSettings(opts = {}) {
273
278
  const cwd = opts.cwd || process.cwd();
274
279
  const env = opts.env || process.env;
275
280
  const onWarn = opts.onWarn;
281
+ function* layers() {
282
+ for (const file of settingsPaths(cwd, opts.settingsFile)) {
283
+ yield { file, settings: readSettingsFile(file, { onWarn }) };
284
+ }
285
+ }
286
+ return mergePermissionSettings(layers(), env, () =>
287
+ loadManagedSettings({
288
+ env,
289
+ managedSettingsFile: opts.managedSettingsFile,
290
+ onWarn,
291
+ }),
292
+ );
293
+ }
276
294
 
295
+ /**
296
+ * Project the exact JSON trees read and hashed by inspectSettingsSources.
297
+ * No path discovery or filesystem read occurs here. The final source has the
298
+ * managed role even when an earlier explicit/user/project source has the same
299
+ * path; retaining both positions preserves precedence and provenance.
300
+ *
301
+ * This is a byte-bound projection, not a fresh authority check or an atomic
302
+ * inventory snapshot. Environment inputs retain their existing sampling rules.
303
+ */
304
+ function projectSettingsObservation(observation, { env = {} } = {}) {
305
+ if (!settingsObservations.has(observation)) {
306
+ throw new TypeError("A strict settings source observation is required");
307
+ }
308
+ const managed = observation.sources[observation.sources.length - 1];
309
+ return mergePermissionSettings(
310
+ observation.sources.slice(0, -1).map((source) => ({
311
+ file: source.logicalPath,
312
+ settings: source.settings,
313
+ })),
314
+ env,
315
+ () => ({ file: managed.logicalPath, settings: managed.settings }),
316
+ );
317
+ }
318
+
319
+ function mergePermissionSettings(layers, env, readManaged) {
277
320
  let rules = emptyRules();
278
321
  const sources = {};
279
322
  const files = [];
280
323
 
281
- for (const file of settingsPaths(cwd, opts.settingsFile)) {
282
- const data = readSettingsFile(file, { onWarn });
324
+ for (const { file, settings: data } of layers) {
283
325
  if (!data) continue;
284
326
  const perms =
285
327
  data.permissions && typeof data.permissions === "object"
@@ -308,11 +350,7 @@ function loadSettings(opts = {}) {
308
350
  }
309
351
  if (envContributed) files.push("<env>");
310
352
 
311
- const managedLoaded = loadManagedSettings({
312
- env,
313
- managedSettingsFile: opts.managedSettingsFile,
314
- onWarn,
315
- });
353
+ const managedLoaded = readManaged();
316
354
  if (managedLoaded.settings) {
317
355
  rules = applyManagedPermissionPolicy(
318
356
  rules,
@@ -354,7 +392,38 @@ function scopeFile(cwd, scope) {
354
392
  * @returns {{ file:string, added:boolean }} added=false → already present.
355
393
  * @throws if the target file exists but is malformed JSON (refuse to clobber).
356
394
  */
357
- function addRule({ cwd = process.cwd(), kind, rule, scope = "project" } = {}) {
395
+ function appendPermissionRule(data, file, kind, rule) {
396
+ if (
397
+ Object.hasOwn(data, "permissions") &&
398
+ (!data.permissions ||
399
+ typeof data.permissions !== "object" ||
400
+ Array.isArray(data.permissions))
401
+ )
402
+ throw new TypeError(`refusing to overwrite malformed permissions: ${file}`);
403
+ data.permissions ??= {};
404
+ for (const decision of KINDS) {
405
+ if (
406
+ Object.hasOwn(data.permissions, decision) &&
407
+ (!Array.isArray(data.permissions[decision]) ||
408
+ data.permissions[decision].some((entry) => typeof entry !== "string"))
409
+ )
410
+ throw new TypeError(
411
+ `refusing to overwrite malformed permission rules: ${file}`,
412
+ );
413
+ }
414
+ data.permissions[kind] ??= [];
415
+ if (data.permissions[kind].includes(rule)) return false;
416
+ data.permissions[kind].push(rule);
417
+ return true;
418
+ }
419
+
420
+ function addRule({
421
+ cwd = process.cwd(),
422
+ kind,
423
+ rule,
424
+ scope = "project",
425
+ settingsAuthority = null,
426
+ } = {}) {
358
427
  const entryCwd = process.cwd();
359
428
  const absoluteCwd = path.resolve(entryCwd, cwd);
360
429
  // Check before lock acquisition: notification callbacks may reenter here.
@@ -368,11 +437,38 @@ function addRule({ cwd = process.cwd(), kind, rule, scope = "project" } = {}) {
368
437
  if (!["project", "local", "user"].includes(scope)) {
369
438
  throw new TypeError("scope must be project | local | user");
370
439
  }
371
- const file = path.resolve(entryCwd, scopeFile(absoluteCwd, scope));
372
- _deps.fs.mkdirSync(path.dirname(file), { recursive: true });
440
+ let file = path.resolve(entryCwd, scopeFile(absoluteCwd, scope));
441
+ if (settingsAuthority !== null) {
442
+ const authority = require("./settings-permission-authority.cjs");
443
+ const bound =
444
+ authority.settingsPermissionAuthorityOptions(settingsAuthority);
445
+ if (absoluteCwd !== bound.cwd)
446
+ throw new TypeError("settings authority cwd mismatch");
447
+ if (scope === "user") file = bound.userSettingsFile;
448
+ } else _deps.fs.mkdirSync(path.dirname(file), { recursive: true });
373
449
  let began = false;
374
450
  let committed = false;
375
451
  try {
452
+ if (settingsAuthority !== null) {
453
+ const authority = require("./settings-permission-authority.cjs");
454
+ const result = authority.mutateSettingsPermissionSource(
455
+ settingsAuthority,
456
+ {
457
+ logicalPath: file,
458
+ prepare: (data) => appendPermissionRule(data, file, kind, rule),
459
+ revokeLocal() {
460
+ beginSettingsMutation();
461
+ began = true;
462
+ },
463
+ replace(physicalPath, data) {
464
+ _deps.writeSecurityStore(physicalPath, "settings", data);
465
+ committed = true;
466
+ },
467
+ },
468
+ );
469
+ if (began) finishSettingsMutation("ready");
470
+ return { file, added: result.changed };
471
+ }
376
472
  const result = _deps.withFileLock(
377
473
  file,
378
474
  () => {
@@ -387,35 +483,8 @@ function addRule({ cwd = process.cwd(), kind, rule, scope = "project" } = {}) {
387
483
  );
388
484
  }
389
485
  const data = _deps.readSecurityStore(file, "settings");
390
- if (
391
- Object.hasOwn(data, "permissions") &&
392
- (!data.permissions ||
393
- typeof data.permissions !== "object" ||
394
- Array.isArray(data.permissions))
395
- ) {
396
- throw new TypeError(
397
- `refusing to overwrite malformed permissions: ${file}`,
398
- );
399
- }
400
- data.permissions ??= {};
401
- // Refuse to hide a damaged rule list, including a different decision.
402
- for (const decision of KINDS) {
403
- if (
404
- Object.hasOwn(data.permissions, decision) &&
405
- (!Array.isArray(data.permissions[decision]) ||
406
- data.permissions[decision].some(
407
- (entry) => typeof entry !== "string",
408
- ))
409
- ) {
410
- throw new TypeError(
411
- `refusing to overwrite malformed permission rules: ${file}`,
412
- );
413
- }
414
- }
415
- data.permissions[kind] ??= [];
416
- if (data.permissions[kind].includes(rule))
486
+ if (!appendPermissionRule(data, file, kind, rule))
417
487
  return { file, added: false };
418
- data.permissions[kind].push(rule);
419
488
  beginSettingsMutation();
420
489
  began = true;
421
490
  _deps.writeSecurityStore(file, "settings", data);
@@ -662,6 +731,7 @@ function readStringArraySetting(key, opts = {}) {
662
731
 
663
732
  module.exports = {
664
733
  inspectSettingsSources,
734
+ projectSettingsObservation,
665
735
  loadSettings,
666
736
  loadSettingsConfig,
667
737
  readSettingsFile,
@@ -0,0 +1,373 @@
1
+ "use strict";
2
+
3
+ // Trusted-host, cooperative Linux binding. Only explicit provisioning creates
4
+ // an authority; a reader/writer never registers or repairs a missing domain.
5
+ const path = require("node:path");
6
+ const { types } = require("node:util");
7
+ const { createHash } = require("node:crypto");
8
+ const fs = require("node:fs");
9
+ const { observeSettingsSources } = require("./settings-source-observation.cjs");
10
+ const domain = require("./settings-authority-domain.cjs");
11
+ const records = require("./settings-authority-record.cjs");
12
+ const bindings = new WeakMap();
13
+ const SCHEMA = "chainlesschain.settings-permission-launch/v1";
14
+ const loader = () => require("./settings-loader.cjs");
15
+
16
+ function fail(code = "CC_SETTINGS_PERMISSION_BINDING_INVALID") {
17
+ throw Object.assign(
18
+ new Error("Persistent settings permission binding is unavailable"),
19
+ { code },
20
+ );
21
+ }
22
+
23
+ function dataCopy(input, depth = 0) {
24
+ if (input === null || ["string", "boolean", "number"].includes(typeof input))
25
+ return input;
26
+ if (!input || typeof input !== "object" || types.isProxy(input) || depth > 12)
27
+ fail();
28
+ const array = Array.isArray(input);
29
+ if (
30
+ !array &&
31
+ ![Object.prototype, null].includes(Object.getPrototypeOf(input))
32
+ )
33
+ fail();
34
+ const output = array ? [] : {};
35
+ const keys = Reflect.ownKeys(input);
36
+ if (keys.length > 1024) fail();
37
+ for (const key of keys) {
38
+ if (array && key === "length") continue;
39
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
40
+ if (
41
+ typeof key !== "string" ||
42
+ !descriptor ||
43
+ !Object.hasOwn(descriptor, "value") ||
44
+ key === "__proto__" ||
45
+ (array && !/^(0|[1-9][0-9]*)$/.test(key))
46
+ )
47
+ fail();
48
+ output[key] = dataCopy(descriptor.value, depth + 1);
49
+ }
50
+ if (
51
+ array &&
52
+ (output.length !== input.length ||
53
+ Object.keys(output).length !== input.length)
54
+ )
55
+ fail();
56
+ return Object.freeze(output);
57
+ }
58
+
59
+ function exactKeys(value, keys) {
60
+ if (
61
+ !value ||
62
+ Array.isArray(value) ||
63
+ Object.keys(value).sort().join(",") !== [...keys].sort().join(",")
64
+ )
65
+ fail();
66
+ }
67
+
68
+ function absolute(value) {
69
+ if (
70
+ typeof value !== "string" ||
71
+ !value ||
72
+ !path.isAbsolute(value) ||
73
+ path.resolve(value) !== value
74
+ )
75
+ fail();
76
+ return value;
77
+ }
78
+
79
+ function captureContexts(input, captured = false) {
80
+ const contexts = dataCopy(input);
81
+ if (!Array.isArray(contexts) || !contexts.length || contexts.length > 64)
82
+ fail();
83
+ const ids = new Set();
84
+ return Object.freeze(
85
+ contexts.map((context) => {
86
+ const fields = [
87
+ "contextId",
88
+ "cwd",
89
+ "settingsFile",
90
+ "managedSettingsFile",
91
+ "userSettingsFile",
92
+ "scopedFile",
93
+ ];
94
+ if (captured) exactKeys(context, fields);
95
+ else if (Object.keys(context).some((key) => !fields.includes(key)))
96
+ fail();
97
+ if (
98
+ typeof context.contextId !== "string" ||
99
+ !/^[a-zA-Z0-9._:-]{1,128}$/.test(context.contextId) ||
100
+ ids.has(context.contextId)
101
+ )
102
+ fail();
103
+ ids.add(context.contextId);
104
+ const cwd = captured
105
+ ? absolute(context.cwd)
106
+ : path.resolve(context.cwd || process.cwd());
107
+ const settingsFile =
108
+ context.settingsFile === null || context.settingsFile === undefined
109
+ ? null
110
+ : captured
111
+ ? absolute(context.settingsFile)
112
+ : path.resolve(cwd, context.settingsFile);
113
+ const managedSettingsFile = captured
114
+ ? absolute(context.managedSettingsFile)
115
+ : loader().managedSettingsPath(context);
116
+ const userSettingsFile = captured
117
+ ? absolute(context.userSettingsFile)
118
+ : context.userSettingsFile
119
+ ? path.resolve(context.userSettingsFile)
120
+ : loader().scopeFile(cwd, "user");
121
+ const scopedFile = captured
122
+ ? absolute(context.scopedFile)
123
+ : context.scopedFile
124
+ ? path.resolve(context.scopedFile)
125
+ : require("./scoped-permission-store.js").defaultScopedPermissionStatePath(
126
+ cwd,
127
+ );
128
+ return Object.freeze({
129
+ contextId: context.contextId,
130
+ cwd,
131
+ settingsFile,
132
+ managedSettingsFile: path.resolve(managedSettingsFile),
133
+ userSettingsFile: path.resolve(userSettingsFile),
134
+ scopedFile,
135
+ });
136
+ }),
137
+ );
138
+ }
139
+
140
+ function observe(contexts) {
141
+ const observations = new Map();
142
+ const manifests = contexts.map((context) => {
143
+ const observation = loader().inspectSettingsSources(context);
144
+ const scopedObservation = observeSettingsSources([context.scopedFile])[0];
145
+ observations.set(context.contextId, { observation, scopedObservation });
146
+ return records.createManifest({
147
+ contextId: context.contextId,
148
+ discovery: [
149
+ context.cwd,
150
+ context.userSettingsFile,
151
+ context.managedSettingsFile,
152
+ ...(context.settingsFile ? [context.settingsFile] : []),
153
+ context.scopedFile,
154
+ ],
155
+ sources: [...observation.sources, scopedObservation],
156
+ });
157
+ });
158
+ return { observations, manifests };
159
+ }
160
+
161
+ function observer(contexts, save = () => {}) {
162
+ return (expected) => {
163
+ if (
164
+ expected.length !== contexts.length ||
165
+ expected.some(
166
+ (context, index) => context.contextId !== contexts[index].contextId,
167
+ )
168
+ )
169
+ fail("CC_SETTINGS_AUTHORITY_BINDING_CHANGED");
170
+ // Recompute the complete discovery route, never reuse ledger source paths.
171
+ const current = observe(contexts);
172
+ save(current.observations);
173
+ return current.manifests;
174
+ };
175
+ }
176
+
177
+ /** Explicit administrator/host provisioning. Parent directories must exist. */
178
+ function initializeSettingsPermissionAuthority({
179
+ directory,
180
+ forbiddenRoots,
181
+ contexts,
182
+ }) {
183
+ const captured = captureContexts(contexts);
184
+ const initial = observe(captured);
185
+ const handle = domain.initializeSettingsAuthorityDomain({
186
+ directory,
187
+ forbiddenRoots: [
188
+ ...new Set([
189
+ ...forbiddenRoots,
190
+ ...captured.map((context) => context.cwd),
191
+ ]),
192
+ ],
193
+ contexts: initial.manifests,
194
+ observeContexts: observer(captured),
195
+ });
196
+ try {
197
+ return dataCopy({
198
+ schema: SCHEMA,
199
+ domain: domain.exportSettingsAuthorityDomain(handle),
200
+ contexts: captured,
201
+ });
202
+ } finally {
203
+ domain.closeSettingsAuthorityDomain(handle);
204
+ }
205
+ }
206
+
207
+ /** Reopen a fixed launch descriptor in a process or Worker; never bootstrap. */
208
+ function openSettingsPermissionAuthority({ launch, contextId }) {
209
+ launch = dataCopy(launch);
210
+ exactKeys(launch, ["schema", "domain", "contexts"]);
211
+ if (launch.schema !== SCHEMA) fail();
212
+ const contexts = captureContexts(launch.contexts, true);
213
+ const selected = contexts.find((context) => context.contextId === contextId);
214
+ if (!selected) fail("CC_SETTINGS_AUTHORITY_CONTEXT_UNREGISTERED");
215
+ const handle = domain.reopenSettingsAuthorityDomain({
216
+ descriptor: launch.domain,
217
+ });
218
+ const binding = Object.freeze({});
219
+ const state = { handle, launch, contexts, selected, closed: false };
220
+ bindings.set(binding, state);
221
+ try {
222
+ readSettingsPermissionAuthority(binding);
223
+ return binding;
224
+ } catch (error) {
225
+ closeSettingsPermissionAuthority(binding);
226
+ throw error;
227
+ }
228
+ }
229
+
230
+ function stateFor(binding) {
231
+ const state = bindings.get(binding);
232
+ if (!state || state.closed) fail();
233
+ return state;
234
+ }
235
+
236
+ function settingsPermissionAuthorityOptions(binding) {
237
+ return stateFor(binding).selected;
238
+ }
239
+
240
+ function exportSettingsPermissionAuthority(binding) {
241
+ const state = stateFor(binding);
242
+ domain.exportSettingsAuthorityDomain(state.handle); // Verify namespace first.
243
+ return state.launch;
244
+ }
245
+
246
+ function readSettingsPermissionAuthority(binding, { forUpdate = false } = {}) {
247
+ const state = stateFor(binding);
248
+ let observations;
249
+ const read = forUpdate
250
+ ? domain.readSettingsAuthorityForUpdate
251
+ : domain.readSettingsAuthority;
252
+ const result = read(state.handle, {
253
+ observeContexts: observer(state.contexts, (value) => {
254
+ observations = value;
255
+ }),
256
+ localRevision: () => localRevision(state),
257
+ });
258
+ const observed = observations.get(state.selected.contextId);
259
+ return Object.freeze({
260
+ snapshot: result.snapshot,
261
+ observation: observed.observation,
262
+ scopedObservation: observed.scopedObservation,
263
+ });
264
+ }
265
+
266
+ function localRevision(state) {
267
+ const settings = loader().getSettingsPermissionRevision();
268
+ const scoped =
269
+ require("./scoped-permission-store.js").getScopedPermissionRevision();
270
+ if (
271
+ state.localRevision?.settings !== settings ||
272
+ state.localRevision?.scoped !== scoped
273
+ )
274
+ state.localRevision = Object.freeze({ settings, scoped });
275
+ return state.localRevision;
276
+ }
277
+
278
+ // Keep the observation's minting and projection within one loader instance.
279
+ // Hosts with an ESM transform loader can also load CJS through native require;
280
+ // the native observation must still pass its original private WeakSet brand.
281
+ function projectSettingsPermissionObservation(observation, options) {
282
+ return loader().projectSettingsObservation(observation, options);
283
+ }
284
+
285
+ function assertSettingsPermissionWritableRoots(binding, roots) {
286
+ const state = stateFor(binding);
287
+ const directory = domain.exportSettingsAuthorityDomain(
288
+ state.handle,
289
+ ).directory;
290
+ for (const root of roots) {
291
+ const canonical = fs.realpathSync(absolute(root));
292
+ const overlaps = [
293
+ path.relative(canonical, directory),
294
+ path.relative(directory, canonical),
295
+ ].some(
296
+ (relative) =>
297
+ relative === "" ||
298
+ (!relative.startsWith(".." + path.sep) &&
299
+ relative !== ".." &&
300
+ !path.isAbsolute(relative)),
301
+ );
302
+ if (overlaps) fail("CC_SETTINGS_AUTHORITY_DOMAIN_OVERLAP");
303
+ }
304
+ }
305
+
306
+ // Used only by the official synchronous settings writer. Callbacks remain a
307
+ // trusted host contract; no callback or arbitrary source enters launch data.
308
+ function mutateSettingsPermissionSource(
309
+ binding,
310
+ { logicalPath, prepare, revokeLocal, replace },
311
+ ) {
312
+ const state = stateFor(binding);
313
+ logicalPath = absolute(logicalPath);
314
+ for (let attempt = 0; attempt < 8; attempt++) {
315
+ const current = readSettingsPermissionAuthority(binding, {
316
+ forUpdate: true,
317
+ });
318
+ const source = [
319
+ ...current.observation.sources,
320
+ current.scopedObservation,
321
+ ].find((item) => item.logicalPath === logicalPath);
322
+ if (!source) fail("CC_SETTINGS_AUTHORITY_SOURCE_UNREGISTERED");
323
+ const data = JSON.parse(JSON.stringify(source.settings || {}));
324
+ if (prepare(data) === false)
325
+ return { changed: false, snapshot: current.snapshot };
326
+ const bytes = Buffer.from(`${JSON.stringify(data, null, 2)}\n`);
327
+ if (bytes.length > 1024 * 1024) fail("CC_SETTINGS_SOURCE_TOO_LARGE");
328
+ try {
329
+ return domain.transitionSettingsAuthority(state.handle, {
330
+ intent: {
331
+ kind: "write",
332
+ physicalPath: source.physicalPath,
333
+ after: {
334
+ exists: true,
335
+ byteLength: bytes.length,
336
+ digest: createHash("sha256").update(bytes).digest("hex"),
337
+ },
338
+ },
339
+ expectedSnapshot: current.snapshot,
340
+ localRevision: () => localRevision(state),
341
+ observeContexts: observer(state.contexts),
342
+ revokeLocal,
343
+ replace: () => replace(source.physicalPath, data),
344
+ });
345
+ } catch (error) {
346
+ if (
347
+ error.cause?.code !== "CC_SETTINGS_AUTHORITY_CONFLICT" ||
348
+ error.commitState !== "not-committed" ||
349
+ error.authorityState !== "unchanged"
350
+ )
351
+ throw error;
352
+ }
353
+ }
354
+ fail("CC_SETTINGS_AUTHORITY_CONFLICT");
355
+ }
356
+
357
+ function closeSettingsPermissionAuthority(binding) {
358
+ const state = stateFor(binding);
359
+ domain.closeSettingsAuthorityDomain(state.handle);
360
+ state.closed = true;
361
+ }
362
+
363
+ module.exports = {
364
+ initializeSettingsPermissionAuthority,
365
+ openSettingsPermissionAuthority,
366
+ exportSettingsPermissionAuthority,
367
+ closeSettingsPermissionAuthority,
368
+ settingsPermissionAuthorityOptions,
369
+ readSettingsPermissionAuthority,
370
+ projectSettingsPermissionObservation,
371
+ mutateSettingsPermissionSource,
372
+ assertSettingsPermissionWritableRoots,
373
+ };