chainlesschain 0.166.85 → 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 (178) hide show
  1. package/README.md +8 -0
  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-Cgo7OFpi.js → AIOps-DZwiXAIh.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-lUMnE2Ln.js → ActionButton-aNzXoIJP.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-CsvKzTot.js → Analytics-ETQk4ZXt.js} +3 -3
  8. package/src/assets/web-panel/assets/{AppLayout-C2z1h1Pv.js → AppLayout-D5oEbeEC.js} +3 -3
  9. package/src/assets/web-panel/assets/{Artifacts-C5Q0ZhNb.js → Artifacts-ja48FAne.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-DXnfPKsI.js → Audit-DlN0YnDt.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-DjbGcLm3.js → BackgroundAgents-CzvyNsRg.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-9bVrKvXE.js → Backup-DXgEC2MQ.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-bx5ri7BD.js → BaseInput-D9XqejLx.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-CCFmeUMY.js → Chat-BTwuYUlN.js} +4 -4
  15. package/src/assets/web-panel/assets/ChatBubbleRenderer-0anIC2Wn.js +1 -0
  16. package/src/assets/web-panel/assets/{Checkbox-ei7MQXe9.js → Checkbox-CLuaQbDt.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-CXgBJ9iu.js → Codegen-KwQUZs8A.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-DMkMsizS.js → Col-BCmROkzc.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-CvpB_UFR.js → Community-CXlUEHR4.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-gay5hbFX.js → Compact-Dzx-lWI2.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-dG8JQyRL.js → Compliance-CfXW82FF.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-BA3xfaTW.js → Cowork-DIISTRm2.js} +4 -4
  23. package/src/assets/web-panel/assets/{Cron-D1_HsxyY.js → Cron-nyJ-KcFE.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-DZqoqoy5.js → Crosschain-Dtv1E4cV.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-Cwnr8hCO.js → DID-BCTpektS.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-CxevypiI.js → Dashboard-C1Q_m0So.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-Bs7-yWgr.js → Dropdown-BrQ5_PPL.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-CcEs2Puo.js → EmailListRenderer-DjoNlONP.js} +1 -1
  29. package/src/assets/web-panel/assets/{EvolutionSettings-C9ipBzAQ.js → EvolutionSettings-DaGaE6bX.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Dt6--mGn.js → FamilyGuardDashboard-nJDWBECt.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-CD0JN5FF.js → Federation-DhMUY_wG.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-POypz3Xv.js → FormItemContext-CbONJPuc.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-Dv9f-0F5.js → GenericCardRenderer-CDdX4dX-.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-BbTh5Atv.js → Git-DXbDVJdR.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-C71dgoir.js → Governance-DbAfAvs1.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-DwSHxbJB.js → Inference-Dy-fGea2.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-Cx1oQYQZ.js → KnowledgeGraph-DAxRpGUF.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-BAJWe00o.js → Logs-5L1UCR8X.js} +2 -2
  39. package/src/assets/web-panel/assets/{MarkdownRenderer-CD8a7lHT.js → MarkdownRenderer-u0FPafvT.js} +1 -1
  40. package/src/assets/web-panel/assets/{Marketplace-fwuY7P7Y.js → Marketplace-DUk_McDG.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-Di4693uc.js → McpTools-B6PNsZ_p.js} +2 -2
  42. package/src/assets/web-panel/assets/{Memory-Cz3L-GUB.js → Memory-BUUs8j5F.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-BuXKIkSb.js → MobileBridge-pXRsgyuO.js} +1 -1
  44. package/src/assets/web-panel/assets/MobileProjects-D7v-2Hwm.js +1 -0
  45. package/src/assets/web-panel/assets/{Mtc-LnACMFd0.js → Mtc-Deaj4faB.js} +6 -6
  46. package/src/assets/web-panel/assets/{MtcAudit-BZy7yE0t.js → MtcAudit-wUV0VUH1.js} +2 -2
  47. package/src/assets/web-panel/assets/{Multisig-BFG5iVrE.js → Multisig-C67IIkts.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-DgXi04vJ.js → NLProgramming-BCA_CWrW.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-BAj0Kgg4.js → Notes-D9eu14hQ.js} +4 -4
  50. package/src/assets/web-panel/assets/{NotificationSettings-AixrsHw5.js → NotificationSettings-Cnd-J4LM.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-Dwqk3FlG.js → OrderTableRenderer-C4pHUuyZ.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-UNCBC3ux.js → Organization-4YExAbnp.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-Drh8oAFT.js → Overflow-BF-wFuZz.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-bc2q0xEB.js → P2P-CdiajqCT.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-BGwKxcDM.js → PdhVaultBrowser-BkjfS6Th.js} +3 -3
  56. package/src/assets/web-panel/assets/{Permissions-DOnUxUzu.js → Permissions-DrZ4D78_.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-B3qPZkrr.js → PersonalDataHub-lDc6OXjz.js} +6 -6
  58. package/src/assets/web-panel/assets/{Pipeline-B6hfa4-i.js → Pipeline-DqDS9ON1.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-Bun3c35F.js → Privacy-BMekZRZu.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-CCObdzk9.js → ProjectInit-LYS7NFvJ.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-C6lKrSXY.js → ProjectSettings-CcJNHk9A.js} +2 -2
  62. package/src/assets/web-panel/assets/{Projects-DYcta7MZ.js → Projects-DrfAHgdF.js} +1 -1
  63. package/src/assets/web-panel/assets/{Providers-BtykxKCe.js → Providers-aqsly9SR.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-D9CDyroY.js → QrScannerModal-ZWvR3zqg.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-CJauCxXx.js → QuickAsk-BT5CgsvW.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-BbiYZofR.js → Recommend-Cz8Dz2RN.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-BymCrgFW.js → RemoteSession-BlxPVbtQ.js} +2 -2
  68. package/src/assets/web-panel/assets/{Reputation-BWrGpMpF.js → Reputation-B3VGBE-1.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-9pOYIhiW.js → Row-Bwv7vSEA.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-D3bQJ4qB.js → RssFeed-DHI085r_.js} +2 -2
  71. package/src/assets/web-panel/assets/{Search-BpKOOFE0.js → Search-DBler5T0.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-DC6C0NSz.js → Security-BZdw43NW.js} +4 -4
  73. package/src/assets/web-panel/assets/{Services-Lkf1046J.js → Services-Cslk5lUj.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-eCQ_SsDb.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-BQbKy8XJ.js → Sla-B1OWgQ51.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-DHsRx-XA.js → SpeechSettings-CaBcEaV4.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-CC281X_J.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-Cr2vC0tg.js → Templates-VBBOZ1H5.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-DeYO5pIG.js → Tenant-CtQTj7IF.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-DBNgbfPr.js → Terminal-DXVTQjnl.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-MoxgaF3x.js → TimelineRenderer-BRIno0aN.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-CT2QfAN7.js → Tokens-uiiAfkYN.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-DPUnomxs.js → Trigger-CPApN8o6.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-DnufuaTx.js → Trust-T2jKVwCE.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-CJ6iOdPs.js → UkeySign-C2U_gbzP.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-xBWkH8bL.js → VideoEditing-BCdlr3Kj.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-WYV-4hMR.js → Wallet-CWnjoSc1.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-DJ5Mhd9L.js → WebAuthn-CqSbYqHq.js} +4 -4
  91. package/src/assets/web-panel/assets/{WorkflowEditor-B__UdOjN.js → WorkflowEditor-Dr_I3jcr.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-CLlmBVhp.js → chat-Cg21FKdE.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-B8zoPQYm.js → colors-CK85tY9e.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-CvJ-dUsH.js → compact-item-m-XYPC7W.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-LAtMm8SC.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-CLYvg42H.js → hasIn-CCw4KAoc.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-B02qgCao.js → index-6jXf-Lln.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-B6zlMzZz.js → index-8KzhlvJ9.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BCzyJ1xS.js → index-B7xJkhtJ.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Di9vKira.js → index-B8IC9ZeZ.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DfOOACfd.js → index-BGUYgc96.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DDm9m4vC.js → index-BGb1j2z9.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DX0aHEHL.js → index-BHsiSoGY.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BUVrYDNV.js → index-BIOTRzaF.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CUcE6rIa.js → index-BStyjmLG.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DNQUdPam.js → index-BUYyhYP6.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CxJ4NPVq.js → index-BcfVHmFK.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-4rr5jFOL.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-C3uD5H7u.js → index-Br0ZNdu1.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-C5v7cEtm.js → index-Br9t_I1X.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DX99e8Xy.js → index-BtvVupUL.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BGrQ2uEl.js → index-Bv90WEWa.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Cpda0SAz.js → index-BvjC3t35.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Cv7vwX3z.js → index-ByVvAXdl.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-D0Z13xcZ.js → index-CBcgIWOe.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DYW4Z3y-.js → index-CN2xbLGz.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-f1ptYBMr.js → index-CYEzWoyZ.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-C_dq-6_3.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-C2KFnUsv.js → index-D1sU5-XA.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-B_RPWP_5.js → index-D7gS_TJl.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-iJ6s24bA.js → index-DAlGVix-.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Bu7-I2L4.js → index-DI9GJFcn.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-C6PETyel.js → index-DIlGxS2W.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CVDcRQuX.js → index-DMbrl43d.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BkcQJvsz.js → index-DmSgfXuz.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DHq2Of6z.js → index-DpIy2HzE.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BM2HqHdT.js → index-DxRhnsEC.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-GR3omApz.js → index-IskAKXhx.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-B1hHp9_M.js → index-J81uvUMN.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DpeV-6y4.js → index-OIbfsi-o.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-oX216HRH.js → index-lhoexBDH.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CnLXLaEF.js → index-sWUmvT8H.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-09YzRb6Z.js → index-yYmjkf38.js} +3 -3
  137. package/src/assets/web-panel/assets/{initDefaultProps-QD-koeaw.js → initDefaultProps-Cc1XOmNe.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-CTItLW4-.js → motion-BehsX88u.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-CGsdTOdN.js → move-BPx2YsqY.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-C1kW_CNU.js → mtc-parser-BSFJgFQA.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-B4PilaYy.js → omit-DBN5KUeo.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-DBGUE12e.js → pickAttrs-BDK1yZ-9.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-DZxCLjuo.js → placementArrow-C4rMmKNJ.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-CZjQiocN.js → responsiveObserve-BoeoOnxX.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-Btz5hh2_.js → slide-BKlr1bl9.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-CfuxuLIt.js → statusUtils-Ky1apWPI.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-DdO0Obo_.js → styleChecker-DxxR7uXk.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-CxftJ-Zs.js → useFlexGapSupport-DmsUqVe9.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-pVJChh6K.js → useFs-CwkG7sOg.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-wv7Q0uNE.js → usePersonalDataHub-CddgeEfF.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-DVOVfAqC.js → vnode-C_-H8i4Y.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-B5tebZuT.js → zoom-aAsHUMNE.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/data/changelog.json +13 -0
  155. package/src/lib/context-memory-kernel/durable-memory-port.js +5 -2
  156. package/src/lib/context-memory-kernel/index.js +1 -0
  157. package/src/lib/context-memory-kernel/memory-service.js +43 -16
  158. package/src/lib/context-memory-kernel/runtime.js +5 -3
  159. package/src/lib/context-memory-kernel/segmented-memory-port.js +687 -0
  160. package/src/lib/packer/pack-update-applier.js +13 -8
  161. package/src/lib/permission-authority.js +146 -10
  162. package/src/lib/sandbox-egress-authority-monitor.js +62 -5
  163. package/src/lib/scoped-permission-store.js +104 -1
  164. package/src/lib/settings-authority-domain.cjs +37 -2
  165. package/src/lib/settings-loader.cjs +67 -34
  166. package/src/lib/settings-permission-authority.cjs +373 -0
  167. package/src/runtime/agent-core.js +79 -383
  168. package/src/runtime/headless-runner.js +8 -3
  169. package/src/runtime/headless-stream.js +10 -3
  170. package/src/runtime/permission-authority-host.js +137 -0
  171. package/src/runtime/provider-stream-state.js +379 -0
  172. package/src/assets/web-panel/assets/ChatBubbleRenderer-ClqsIfTT.js +0 -1
  173. package/src/assets/web-panel/assets/MobileProjects-Bk-lK4Um.js +0 -1
  174. package/src/assets/web-panel/assets/Skills-BvDSQ3-d.js +0 -1
  175. package/src/assets/web-panel/assets/Tasks-CHHSWHom.js +0 -1
  176. package/src/assets/web-panel/assets/devWarning-DgZ4fdGI.js +0 -1
  177. package/src/assets/web-panel/assets/index-jLEdASOV.js +0 -1
  178. package/src/assets/web-panel/assets/index-jesGo4rl.js +0 -1
@@ -146,9 +146,11 @@ function accrete(target, sources, arr, file, kind) {
146
146
  }
147
147
 
148
148
  /** The ordered list of candidate settings files for a cwd. */
149
- function settingsPaths(cwd, explicitFile) {
149
+ function settingsPaths(cwd, explicitFile, userSettingsFile = null) {
150
150
  const home = _deps.homedir();
151
- const list = [path.join(home, ".claude", "settings.json")];
151
+ const list = [
152
+ userSettingsFile || path.join(home, ".claude", "settings.json"),
153
+ ];
152
154
  // When run from a subdirectory, the project-root `.claude` sits BELOW cwd's
153
155
  // own (closest wins) but ABOVE the user layer — so its rules apply yet a
154
156
  // cwd-local settings file still overrides them. Null when cwd IS the root.
@@ -171,7 +173,7 @@ function settingsPaths(cwd, explicitFile) {
171
173
  function inspectSettingsSources(opts = {}) {
172
174
  const cwd = path.resolve(opts.cwd || process.cwd());
173
175
  const candidates = [
174
- ...settingsPaths(cwd, opts.settingsFile),
176
+ ...settingsPaths(cwd, opts.settingsFile, opts.userSettingsFile),
175
177
  managedSettingsPath(opts),
176
178
  ];
177
179
  const observation = Object.freeze({
@@ -390,7 +392,38 @@ function scopeFile(cwd, scope) {
390
392
  * @returns {{ file:string, added:boolean }} added=false → already present.
391
393
  * @throws if the target file exists but is malformed JSON (refuse to clobber).
392
394
  */
393
- 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
+ } = {}) {
394
427
  const entryCwd = process.cwd();
395
428
  const absoluteCwd = path.resolve(entryCwd, cwd);
396
429
  // Check before lock acquisition: notification callbacks may reenter here.
@@ -404,11 +437,38 @@ function addRule({ cwd = process.cwd(), kind, rule, scope = "project" } = {}) {
404
437
  if (!["project", "local", "user"].includes(scope)) {
405
438
  throw new TypeError("scope must be project | local | user");
406
439
  }
407
- const file = path.resolve(entryCwd, scopeFile(absoluteCwd, scope));
408
- _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 });
409
449
  let began = false;
410
450
  let committed = false;
411
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
+ }
412
472
  const result = _deps.withFileLock(
413
473
  file,
414
474
  () => {
@@ -423,35 +483,8 @@ function addRule({ cwd = process.cwd(), kind, rule, scope = "project" } = {}) {
423
483
  );
424
484
  }
425
485
  const data = _deps.readSecurityStore(file, "settings");
426
- if (
427
- Object.hasOwn(data, "permissions") &&
428
- (!data.permissions ||
429
- typeof data.permissions !== "object" ||
430
- Array.isArray(data.permissions))
431
- ) {
432
- throw new TypeError(
433
- `refusing to overwrite malformed permissions: ${file}`,
434
- );
435
- }
436
- data.permissions ??= {};
437
- // Refuse to hide a damaged rule list, including a different decision.
438
- for (const decision of KINDS) {
439
- if (
440
- Object.hasOwn(data.permissions, decision) &&
441
- (!Array.isArray(data.permissions[decision]) ||
442
- data.permissions[decision].some(
443
- (entry) => typeof entry !== "string",
444
- ))
445
- ) {
446
- throw new TypeError(
447
- `refusing to overwrite malformed permission rules: ${file}`,
448
- );
449
- }
450
- }
451
- data.permissions[kind] ??= [];
452
- if (data.permissions[kind].includes(rule))
486
+ if (!appendPermissionRule(data, file, kind, rule))
453
487
  return { file, added: false };
454
- data.permissions[kind].push(rule);
455
488
  beginSettingsMutation();
456
489
  began = true;
457
490
  _deps.writeSecurityStore(file, "settings", data);
@@ -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
+ };