chainlesschain 0.166.85 → 0.166.87

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 (201) 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-DdQjhXg9.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-lUMnE2Ln.js → ActionButton-CuwA3hPA.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-CsvKzTot.js → Analytics-DTVXJSAC.js} +3 -3
  8. package/src/assets/web-panel/assets/{AppLayout-C2z1h1Pv.js → AppLayout-j06JBfEQ.js} +5 -5
  9. package/src/assets/web-panel/assets/{Artifacts-C5Q0ZhNb.js → Artifacts-BFAK8LEW.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-DXnfPKsI.js → Audit-BiGxRpOf.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-DjbGcLm3.js → BackgroundAgents-B6uHaPkZ.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-9bVrKvXE.js → Backup-BNAesB8q.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-bx5ri7BD.js → BaseInput-BOuetfCd.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-CCFmeUMY.js → Chat-cVzo3m_D.js} +4 -4
  15. package/src/assets/web-panel/assets/ChatBubbleRenderer-DZ2Ys-CI.js +1 -0
  16. package/src/assets/web-panel/assets/{Checkbox-ei7MQXe9.js → Checkbox-BaCTS-H8.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-CXgBJ9iu.js → Codegen-CBE8NiYh.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-DMkMsizS.js → Col-C9wLiUTN.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-CvpB_UFR.js → Community-jdClBSIp.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-gay5hbFX.js → Compact-BIjauIlM.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-dG8JQyRL.js → Compliance-CZ-AoaNj.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-BA3xfaTW.js → Cowork-BbTKtDS4.js} +4 -4
  23. package/src/assets/web-panel/assets/{Cron-D1_HsxyY.js → Cron-ImKdtwNi.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-DZqoqoy5.js → Crosschain-B54GSHf5.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-Cwnr8hCO.js → DID-BQt5WUzs.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-CxevypiI.js → Dashboard--gNOffif.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-Bs7-yWgr.js → Dropdown-342A5YJw.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-CcEs2Puo.js → EmailListRenderer-CtIZ3ZHd.js} +1 -1
  29. package/src/assets/web-panel/assets/{EvolutionSettings-C9ipBzAQ.js → EvolutionSettings-CyZPoTio.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Dt6--mGn.js → FamilyGuardDashboard-B1MCkFEu.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-CD0JN5FF.js → Federation-DZWoERUy.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-POypz3Xv.js → FormItemContext-Onmb0mp3.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-Dv9f-0F5.js → GenericCardRenderer-u1qhNNc7.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-BbTh5Atv.js → Git-BEowR1C5.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-C71dgoir.js → Governance-BiYd3AAg.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-DwSHxbJB.js → Inference-CsC4Vjh5.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-Cx1oQYQZ.js → KnowledgeGraph-BXA7ikPM.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-BAJWe00o.js → Logs-C9WoplQm.js} +2 -2
  39. package/src/assets/web-panel/assets/{MarkdownRenderer-CD8a7lHT.js → MarkdownRenderer-DecEsXsy.js} +1 -1
  40. package/src/assets/web-panel/assets/{Marketplace-fwuY7P7Y.js → Marketplace-CCoJBfRL.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-Di4693uc.js → McpTools-CzaOWBoA.js} +3 -3
  42. package/src/assets/web-panel/assets/{Memory-Cz3L-GUB.js → Memory-BPwh7u1-.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-BuXKIkSb.js → MobileBridge-Bk47v6z2.js} +1 -1
  44. package/src/assets/web-panel/assets/MobileProjects-D-CY--Sb.js +1 -0
  45. package/src/assets/web-panel/assets/{Mtc-LnACMFd0.js → Mtc-ExVyAGlW.js} +5 -5
  46. package/src/assets/web-panel/assets/{MtcAudit-BZy7yE0t.js → MtcAudit-Rrw10csE.js} +5 -5
  47. package/src/assets/web-panel/assets/{Multisig-BFG5iVrE.js → Multisig-8TxxwxZZ.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-DgXi04vJ.js → NLProgramming-CNckjtnY.js} +1 -1
  49. package/src/assets/web-panel/assets/Notes-C4j6cZTu.js +7 -0
  50. package/src/assets/web-panel/assets/{NotificationSettings-AixrsHw5.js → NotificationSettings-CchU8rWd.js} +1 -1
  51. package/src/assets/web-panel/assets/OrderTableRenderer-ZJBoyJ16.js +1 -0
  52. package/src/assets/web-panel/assets/{Organization-UNCBC3ux.js → Organization-F0Je7EdZ.js} +2 -2
  53. package/src/assets/web-panel/assets/{Overflow-Drh8oAFT.js → Overflow-B2k-JA3u.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-bc2q0xEB.js → P2P-yui9Xxe6.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-BGwKxcDM.js → PdhVaultBrowser-CegM7hT6.js} +3 -3
  56. package/src/assets/web-panel/assets/{Permissions-DOnUxUzu.js → Permissions-CSFL0uvh.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-B3qPZkrr.js → PersonalDataHub-BrMcd-vr.js} +6 -6
  58. package/src/assets/web-panel/assets/{Pipeline-B6hfa4-i.js → Pipeline-fzrliaVP.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-Bun3c35F.js → Privacy-BrkUjJ_g.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-CCObdzk9.js → ProjectInit-CF_QCsW9.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-C6lKrSXY.js → ProjectSettings-Duwrd8aC.js} +2 -2
  62. package/src/assets/web-panel/assets/{Projects-DYcta7MZ.js → Projects-B0dLVXzU.js} +1 -1
  63. package/src/assets/web-panel/assets/{Providers-BtykxKCe.js → Providers-Db8iS1xQ.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-D9CDyroY.js → QrScannerModal-CHbNzEQs.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-CJauCxXx.js → QuickAsk-CIESMfeR.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-BbiYZofR.js → Recommend-BTMbCMtR.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-BymCrgFW.js → RemoteSession-DavyK6J-.js} +3 -3
  68. package/src/assets/web-panel/assets/{Reputation-BWrGpMpF.js → Reputation-BFKtMelA.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-9pOYIhiW.js → Row-BI9AZUvw.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-D3bQJ4qB.js → RssFeed-1nwXt8NY.js} +2 -2
  71. package/src/assets/web-panel/assets/{Search-BpKOOFE0.js → Search-Ck8PDYbk.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-DC6C0NSz.js → Security-BgDqpCbq.js} +4 -4
  73. package/src/assets/web-panel/assets/{Services-Lkf1046J.js → Services-DF8anXm4.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-eCQ_SsDb.js → Skeleton-CoHyY4ZX.js} +1 -1
  75. package/src/assets/web-panel/assets/Skills-dOaD4Ztl.js +1 -0
  76. package/src/assets/web-panel/assets/{Sla-BQbKy8XJ.js → Sla-XuOawvQY.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-DHsRx-XA.js → SpeechSettings-DFJ7nAiu.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-CC281X_J.js → SyncSettings-B2xYj-Pi.js} +2 -2
  79. package/src/assets/web-panel/assets/Tasks-D_SzNTLi.js +1 -0
  80. package/src/assets/web-panel/assets/{Templates-Cr2vC0tg.js → Templates-BXFmKh7W.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-DeYO5pIG.js → Tenant-CRg2AXbd.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-DBNgbfPr.js → Terminal-DQgxnuep.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-MoxgaF3x.js → TimelineRenderer-BrPQhQVc.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-CT2QfAN7.js → Tokens-RByy4USy.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-DPUnomxs.js → Trigger-pE3y8u73.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-DnufuaTx.js → Trust-DhID9C33.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-CJ6iOdPs.js → UkeySign-CPxEPNBH.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-xBWkH8bL.js → VideoEditing-ZD8DUxeQ.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-WYV-4hMR.js → Wallet-Br9JREEd.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-DJ5Mhd9L.js → WebAuthn-Cn-YacJg.js} +4 -4
  91. package/src/assets/web-panel/assets/{WorkflowEditor-B__UdOjN.js → WorkflowEditor-BTu0oqMp.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-CLlmBVhp.js → chat-CEjws_QF.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-B8zoPQYm.js → colors-Bv2RNebW.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-CvJ-dUsH.js → compact-item-gSc3EofP.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-LAtMm8SC.js → createContext-kyFDaqWC.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-Dcc8pILJ.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-CLYvg42H.js → hasIn-BuMWcjhx.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Cv7vwX3z.js → index--nrQmk8X.js} +1 -1
  99. package/src/assets/web-panel/assets/index-05NB95_V.js +1 -0
  100. package/src/assets/web-panel/assets/{index-BUVrYDNV.js → index-42PTBO7a.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-B6zlMzZz.js → index-BCS5j4SQ.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-oX216HRH.js → index-BSSIJ-rv.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-B02qgCao.js → index-BUOCcXou.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B_RPWP_5.js → index-Bg5jis0q.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DX99e8Xy.js → index-C-LPpwe9.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DDm9m4vC.js → index-C56Drzol.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DHq2Of6z.js → index-C7Jo20JB.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-C_dq-6_3.js → index-C9U9SjcT.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-D0Z13xcZ.js → index-CF3TjDNz.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-B1hHp9_M.js → index-CMS3Mk93.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DfOOACfd.js → index-CNt2X-aK.js} +1 -1
  112. package/src/assets/web-panel/assets/index-Cb6q8wlI.js +1 -0
  113. package/src/assets/web-panel/assets/{index-DYW4Z3y-.js → index-Cj4RA7oA.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-C2KFnUsv.js → index-CkfvP2dN.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BkcQJvsz.js → index-Cm8K8Evn.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CVDcRQuX.js → index-CvSuM_CB.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BCzyJ1xS.js → index-Czo_hw97.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Cpda0SAz.js → index-D3m3EXbL.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-iJ6s24bA.js → index-DR561Sqc.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Di9vKira.js → index-DRa4R7vv.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-09YzRb6Z.js → index-DYKSuD02.js} +3 -3
  122. package/src/assets/web-panel/assets/{index-C5v7cEtm.js → index-DYcPWVN9.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BGrQ2uEl.js → index-DbVUA5l3.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-4rr5jFOL.js → index-DbkyTMj-.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-GR3omApz.js → index-Dej4AGOz.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-f1ptYBMr.js → index-EpxWlmip.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-C3uD5H7u.js → index-GpvWtvw3.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-C6PETyel.js → index-NQ0c4hJ1.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CnLXLaEF.js → index-OjZUHz5z.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DpeV-6y4.js → index-U-ytt84A.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DNQUdPam.js → index-_0bfDTDu.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BM2HqHdT.js → index-jmMAqBAm.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-Bu7-I2L4.js → index-nLemnBZ2.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-CUcE6rIa.js → index-q65c1kp8.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-DX0aHEHL.js → index-sJ1F5Txa.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CxJ4NPVq.js → index-xfS_WO9b.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-QD-koeaw.js → initDefaultProps-B-wqiSu7.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-CTItLW4-.js → motion-BK4_B4MD.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-CGsdTOdN.js → move-BU2TgDDO.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-C1kW_CNU.js → mtc-parser-DA3E4_EX.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-B4PilaYy.js → omit-CaHz1e_q.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-DBGUE12e.js → pickAttrs-NXZ19amf.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-DZxCLjuo.js → placementArrow-BCXI9AHO.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-CZjQiocN.js → responsiveObserve-DdlZ9tLk.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-Btz5hh2_.js → slide-DCZm_Vc4.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-CfuxuLIt.js → statusUtils-CuEULKQr.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-DdO0Obo_.js → styleChecker-Bo_UXG8x.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-CxftJ-Zs.js → useFlexGapSupport-B9PaTcFH.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-pVJChh6K.js → useFs-DwSTGdVR.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-wv7Q0uNE.js → usePersonalDataHub-FswSxnDC.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-DVOVfAqC.js → vnode-EUztvYTj.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-B5tebZuT.js → zoom-D41MAgV-.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/command-help-index.json +1 -1
  155. package/src/commands/eval.js +118 -18
  156. package/src/commands/memory.js +34 -15
  157. package/src/commands/sandbox.js +9 -0
  158. package/src/data/changelog.json +26 -0
  159. package/src/lib/causal-observability.js +6 -45
  160. package/src/lib/context-memory-kernel/durable-memory-port.js +54 -12
  161. package/src/lib/context-memory-kernel/index.js +1 -0
  162. package/src/lib/context-memory-kernel/memory-query-index.js +278 -0
  163. package/src/lib/context-memory-kernel/memory-service.js +60 -18
  164. package/src/lib/context-memory-kernel/runtime.js +5 -3
  165. package/src/lib/context-memory-kernel/segmented-memory-port.js +955 -0
  166. package/src/lib/eval/outcomes.js +20 -10
  167. package/src/lib/eval/runner.js +49 -5
  168. package/src/lib/eval/tasks.js +8 -3
  169. package/src/lib/eval/verify01-contracts.js +349 -0
  170. package/src/lib/eval/verify01-execution.js +605 -0
  171. package/src/lib/execution-support.js +58 -0
  172. package/src/lib/llm-pricing.js +98 -11
  173. package/src/lib/model-catalog-review.js +116 -0
  174. package/src/lib/model-context-catalog.js +23 -1
  175. package/src/lib/openai-responses.js +39 -0
  176. package/src/lib/packer/pack-update-applier.js +13 -8
  177. package/src/lib/permission-authority.js +146 -10
  178. package/src/lib/process-execution-broker/index.js +8 -0
  179. package/src/lib/process-execution-broker/linux-subreaper-process.js +24 -2
  180. package/src/lib/process-execution-broker/process-ownership-journal.js +168 -7
  181. package/src/lib/process-execution-broker/process-ownership-quarantine.js +56 -1
  182. package/src/lib/process-execution-broker/process-recovery-cgroup.js +272 -0
  183. package/src/lib/sandbox-egress-authority-monitor.js +62 -5
  184. package/src/lib/scoped-permission-store.js +104 -1
  185. package/src/lib/settings-authority-domain.cjs +37 -2
  186. package/src/lib/settings-loader.cjs +67 -34
  187. package/src/lib/settings-permission-authority.cjs +373 -0
  188. package/src/runtime/agent-core.js +88 -407
  189. package/src/runtime/headless-runner.js +8 -3
  190. package/src/runtime/headless-stream.js +10 -3
  191. package/src/runtime/permission-authority-host.js +151 -0
  192. package/src/runtime/provider-stream-state.js +379 -0
  193. package/src/assets/web-panel/assets/ChatBubbleRenderer-ClqsIfTT.js +0 -1
  194. package/src/assets/web-panel/assets/MobileProjects-Bk-lK4Um.js +0 -1
  195. package/src/assets/web-panel/assets/Notes-BAj0Kgg4.js +0 -7
  196. package/src/assets/web-panel/assets/OrderTableRenderer-Dwqk3FlG.js +0 -1
  197. package/src/assets/web-panel/assets/Skills-BvDSQ3-d.js +0 -1
  198. package/src/assets/web-panel/assets/Tasks-CHHSWHom.js +0 -1
  199. package/src/assets/web-panel/assets/devWarning-DgZ4fdGI.js +0 -1
  200. package/src/assets/web-panel/assets/index-jLEdASOV.js +0 -1
  201. package/src/assets/web-panel/assets/index-jesGo4rl.js +0 -1
@@ -1,3 +1,5 @@
1
+ import { randomUUID } from "node:crypto";
2
+
1
3
  /**
2
4
  * Recheck the live shell authority while a Docker egress session exists. This
3
5
  * uses bounded polling as a fallback. Trusted policy owners also call revoke()
@@ -9,19 +11,35 @@ export function createDockerEgressAuthorityMonitor({
9
11
  abortProxy,
10
12
  intervalMs = 500,
11
13
  checkTimeoutMs = 5_000,
14
+ sessionId = null,
15
+ policyVersion = null,
12
16
  }) {
13
17
  if (typeof revalidate !== "function" || typeof abortProxy !== "function")
14
18
  throw new TypeError("Docker egress authority callbacks are required");
19
+ if (
20
+ (sessionId !== null && typeof sessionId !== "string") ||
21
+ (policyVersion !== null && typeof policyVersion !== "string")
22
+ )
23
+ throw new TypeError(
24
+ "Docker egress acknowledgement identities must be strings",
25
+ );
26
+ const stopIdentity = Object.freeze({
27
+ receiverId: randomUUID(),
28
+ sessionId,
29
+ policyVersion,
30
+ });
15
31
  let revoked = null;
16
32
  let session = null;
17
33
  let timer = null;
18
34
  let checking = null;
19
35
  let stopped = false;
20
36
  const cleanupTasks = new Set();
37
+ let proxyStopped = false;
38
+ let sessionStopped = false;
21
39
 
22
- function trackCleanup(operation) {
40
+ function trackCleanup(operation, onStopped) {
23
41
  try {
24
- const task = Promise.resolve(operation());
42
+ const task = Promise.resolve(operation()).then(onStopped);
25
43
  cleanupTasks.add(task);
26
44
  void task.catch((error) => {
27
45
  if (revoked && !revoked.cleanupError) revoked.cleanupError = error;
@@ -41,8 +59,18 @@ export function createDockerEgressAuthorityMonitor({
41
59
  if (!revoked.code) revoked.code = "CC_DOCKER_EGRESS_AUTHORITY_CHANGED";
42
60
  clearTimeout(timer);
43
61
  // Latch first, then cut the broker before closing the target containers.
44
- trackCleanup(abortProxy);
45
- if (session) trackCleanup(() => session.close());
62
+ trackCleanup(abortProxy, () => {
63
+ proxyStopped = true;
64
+ });
65
+ if (session) {
66
+ const closing = session;
67
+ trackCleanup(
68
+ () => closing.close(),
69
+ () => {
70
+ if (session === closing) sessionStopped = true;
71
+ },
72
+ );
73
+ }
46
74
  return revoked;
47
75
  }
48
76
 
@@ -51,9 +79,17 @@ export function createDockerEgressAuthorityMonitor({
51
79
  }
52
80
 
53
81
  function attachSession(value) {
82
+ if (session)
83
+ throw new TypeError("Docker egress monitor session is already attached");
54
84
  session = value;
85
+ sessionStopped = false;
55
86
  if (revoked) {
56
- trackCleanup(() => value.close());
87
+ trackCleanup(
88
+ () => value.close(),
89
+ () => {
90
+ if (session === value) sessionStopped = true;
91
+ },
92
+ );
57
93
  throw revoked;
58
94
  }
59
95
  }
@@ -112,6 +148,7 @@ export function createDockerEgressAuthorityMonitor({
112
148
  }
113
149
 
114
150
  return {
151
+ stopIdentity,
115
152
  get revocationError() {
116
153
  return revoked;
117
154
  },
@@ -134,5 +171,25 @@ export function createDockerEgressAuthorityMonitor({
134
171
  async awaitCleanup() {
135
172
  await Promise.allSettled([...cleanupTasks]);
136
173
  },
174
+ getStopAcknowledgement() {
175
+ // A synchronous writer commit is never a stop ACK. Only the receiving
176
+ // runtime, after stopping checks and awaiting both teardown operations,
177
+ // can report this acknowledgement. Cleanup failures never mint one.
178
+ if (
179
+ !stopped ||
180
+ !revoked ||
181
+ revoked.cleanupError ||
182
+ cleanupTasks.size ||
183
+ !proxyStopped ||
184
+ !sessionStopped
185
+ )
186
+ return null;
187
+ return Object.freeze({
188
+ schema: "chainlesschain.egress-stop-ack/v1",
189
+ ...stopIdentity,
190
+ proxyStopped: true,
191
+ sessionStopped: true,
192
+ });
193
+ },
137
194
  };
138
195
  }
@@ -11,6 +11,7 @@ import path from "node:path";
11
11
  import { createHash, randomUUID } from "node:crypto";
12
12
  import projectRoot from "./project-root.cjs";
13
13
  import permissionRules from "./permission-rules.cjs";
14
+ import persistentSettings from "./settings-permission-authority.cjs";
14
15
  import { getMachineSecurityAnchorDir } from "./paths.js";
15
16
  import {
16
17
  readSecurityStore,
@@ -27,6 +28,7 @@ const STORE_LABEL = "scoped permission";
27
28
  const RULE_ID_PATTERN = /^spr_[0-9a-f]{32}$/;
28
29
  const DECISIONS = new Set(["allow", "ask", "deny"]);
29
30
  const DEFAULT_MAX_RECORDS = 1000;
31
+ const persistentBindings = new WeakMap();
30
32
 
31
33
  // Official mutations revoke every provider in this module instance. Path
32
34
  // matching alone misses aliases and files that did not exist at admission.
@@ -216,6 +218,7 @@ export class ScopedPermissionStore {
216
218
  maxRecords = DEFAULT_MAX_RECORDS,
217
219
  lock = undefined,
218
220
  randomId = () => `spr_${randomUUID().replaceAll("-", "")}`,
221
+ settingsAuthority = null,
219
222
  } = {}) {
220
223
  this.workspace = workspaceBinding(cwd);
221
224
  this.filePath = path.resolve(
@@ -242,6 +245,17 @@ export class ScopedPermissionStore {
242
245
  );
243
246
  this._lock = lock;
244
247
  this._randomId = randomId;
248
+ if (settingsAuthority) {
249
+ const bound =
250
+ persistentSettings.settingsPermissionAuthorityOptions(
251
+ settingsAuthority,
252
+ );
253
+ if (path.resolve(cwd) !== bound.cwd || this.filePath !== bound.scopedFile)
254
+ throw new TypeError(
255
+ "persistent scoped permission authority options mismatch",
256
+ );
257
+ persistentBindings.set(this, settingsAuthority);
258
+ }
245
259
  }
246
260
 
247
261
  _capture() {
@@ -253,7 +267,22 @@ export class ScopedPermissionStore {
253
267
  randomId: this._randomId.bind(this),
254
268
  maxRecords: this.maxRecords,
255
269
  lock: this._lock || withFileLock,
270
+ settingsAuthority: persistentBindings.get(this) || null,
256
271
  });
272
+ if (captured.settingsAuthority) {
273
+ const bound = persistentSettings.settingsPermissionAuthorityOptions(
274
+ captured.settingsAuthority,
275
+ );
276
+ const workspace = workspaceBinding(bound.cwd);
277
+ if (
278
+ captured.filePath !== bound.scopedFile ||
279
+ captured.workspace.id !== workspace.id ||
280
+ captured.workspace.root !== workspace.root
281
+ )
282
+ throw new TypeError(
283
+ "persistent scoped permission authority options mismatch",
284
+ );
285
+ }
257
286
  if (getScopedPermissionRevision() !== revision)
258
287
  throw scopedPermissionError(
259
288
  "CC_SCOPED_PERMISSION_AUTHORITY_CHANGED",
@@ -264,13 +293,54 @@ export class ScopedPermissionStore {
264
293
 
265
294
  _read(captured = this._capture()) {
266
295
  getScopedPermissionRevision();
267
- const stored = readSecurityStore(captured.filePath, STORE_LABEL);
296
+ const stored = captured.settingsAuthority
297
+ ? persistentSettings.readSettingsPermissionAuthority(
298
+ captured.settingsAuthority,
299
+ ).scopedObservation.settings || {}
300
+ : readSecurityStore(captured.filePath, STORE_LABEL);
268
301
  if (Object.keys(stored).length === 0) return emptyState(captured.workspace);
269
302
  return validateState(stored, captured.workspace);
270
303
  }
271
304
 
272
305
  _mutate(captured, update) {
273
306
  getScopedPermissionRevision(); // Reject observer reentry before the lock.
307
+ if (captured.settingsAuthority) {
308
+ let started = false;
309
+ try {
310
+ persistentSettings.mutateSettingsPermissionSource(
311
+ captured.settingsAuthority,
312
+ {
313
+ logicalPath: captured.filePath,
314
+ prepare(draft) {
315
+ if (Object.keys(draft).length === 0)
316
+ Object.assign(draft, emptyState(captured.workspace));
317
+ validateState(draft, captured.workspace);
318
+ if (update(draft) === false) return false;
319
+ validateState(draft, captured.workspace);
320
+ },
321
+ revokeLocal() {
322
+ started = true;
323
+ beginMutation();
324
+ },
325
+ replace(file, draft) {
326
+ validateState(draft, captured.workspace);
327
+ writeSecurityStore(file, STORE_LABEL, draft);
328
+ },
329
+ },
330
+ );
331
+ if (started) finishMutation("ready");
332
+ } catch (error) {
333
+ if (started)
334
+ finishMutation(
335
+ error.commitState === "unknown" ||
336
+ permissionRevision.state === "invalid"
337
+ ? "invalid"
338
+ : "ready",
339
+ );
340
+ throw error;
341
+ }
342
+ return;
343
+ }
274
344
  fs.mkdirSync(path.dirname(captured.filePath), {
275
345
  recursive: true,
276
346
  mode: 0o700,
@@ -487,3 +557,36 @@ export class ScopedPermissionStore {
487
557
  return projectRecord(revoked, now, captured.filePath);
488
558
  }
489
559
  }
560
+
561
+ // Only the production provider uses a scoped observation already validated in
562
+ // the same durable read as settings. A store cannot substitute another source.
563
+ export function projectScopedPermissionObservation(
564
+ store,
565
+ binding,
566
+ observation,
567
+ ) {
568
+ if (persistentBindings.get(store) !== binding)
569
+ throw new TypeError(
570
+ "scoped store must use the provider's persistent authority",
571
+ );
572
+ const captured = ScopedPermissionStore.prototype._capture.call(store);
573
+ if (observation.logicalPath !== captured.filePath)
574
+ throw new TypeError("persistent scoped permission source mismatch");
575
+ const stored = observation.settings || {};
576
+ const state =
577
+ Object.keys(stored).length === 0
578
+ ? emptyState(captured.workspace)
579
+ : validateState(stored, captured.workspace);
580
+ const now = captured.now();
581
+ return Object.freeze({
582
+ schema: state.schema,
583
+ schemaVersion: state.schemaVersion,
584
+ generation: state.generation,
585
+ updatedAt: state.updatedAt,
586
+ workspace: structuredClone(state.workspace),
587
+ file: captured.filePath,
588
+ rules: state.rules.map((record) =>
589
+ projectRecord(record, now, captured.filePath),
590
+ ),
591
+ });
592
+ }
@@ -727,12 +727,24 @@ function transitionSettingsAuthority(
727
727
  replace,
728
728
  revokeLocal,
729
729
  transactionId = randomUUID(),
730
+ expectedSnapshot = null,
731
+ localRevision = null,
730
732
  } = {},
731
733
  ) {
732
734
  const state = stateFor(handle);
733
735
  // Snapshot caller data before any callback. These are trusted host APIs, but
734
736
  // mutable intents must not turn an already-validated no-op into a mutation.
735
737
  intent = plainCopy(intent);
738
+ if (expectedSnapshot !== null) {
739
+ expectedSnapshot = plainCopy(expectedSnapshot);
740
+ shape(expectedSnapshot, [
741
+ "domainId",
742
+ "epoch",
743
+ "generation",
744
+ "digest",
745
+ "state",
746
+ ]);
747
+ }
736
748
  requireSynchronous(observeContexts);
737
749
  requireSynchronous(revokeLocal);
738
750
  if (intent.kind === "write") requireSynchronous(replace);
@@ -743,7 +755,15 @@ function transitionSettingsAuthority(
743
755
  try {
744
756
  return locked(state, () => {
745
757
  const perform = () => {
746
- const current = readSettingsAuthority(handle, { observeContexts });
758
+ const current = readSettingsAuthority(handle, {
759
+ observeContexts,
760
+ localRevision,
761
+ });
762
+ if (
763
+ expectedSnapshot !== null &&
764
+ !sameData(current.snapshot, expectedSnapshot)
765
+ )
766
+ refuse("CC_SETTINGS_AUTHORITY_CONFLICT");
747
767
  if (
748
768
  intent.kind === "register" &&
749
769
  current.ledger.contexts.some(
@@ -841,7 +861,15 @@ function transitionSettingsAuthority(
841
861
  };
842
862
  };
843
863
  if (intent.kind !== "write") return perform();
844
- const admission = readSettingsAuthority(handle, { observeContexts });
864
+ const admission = readSettingsAuthority(handle, {
865
+ observeContexts,
866
+ localRevision,
867
+ });
868
+ if (
869
+ expectedSnapshot !== null &&
870
+ !sameData(admission.snapshot, expectedSnapshot)
871
+ )
872
+ refuse("CC_SETTINGS_AUTHORITY_CONFLICT");
845
873
  records.prepareTransition({
846
874
  ledger: admission.ledger,
847
875
  transactionId,
@@ -878,6 +906,12 @@ function transitionSettingsAuthority(
878
906
  }
879
907
  }
880
908
 
909
+ // Official read/modify/write adapters take an initial snapshot under the same
910
+ // authority lock. The later transition MUST still compare expectedSnapshot.
911
+ function readSettingsAuthorityForUpdate(handle, options) {
912
+ return locked(stateFor(handle), () => readSettingsAuthority(handle, options));
913
+ }
914
+
881
915
  function recoverSettingsAuthority(
882
916
  handle,
883
917
  { transactionId, observeContexts, outcome } = {},
@@ -954,6 +988,7 @@ function recoverSettingsAuthority(
954
988
  }
955
989
 
956
990
  module.exports = {
991
+ readSettingsAuthorityForUpdate,
957
992
  DOMAIN_SCHEMA,
958
993
  initializeSettingsAuthorityDomain,
959
994
  pinSettingsAuthorityDomain,
@@ -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);