chainlesschain 0.166.81 → 0.166.83

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 (189) hide show
  1. package/package.json +1 -1
  2. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  3. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-BZPs-bVl.js → AIOps-D5a4i3N7.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-BP1iLSwn.js → ActionButton-DxsRk5n1.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-WxwtPqcd.js → Analytics-D-7EvH4S.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-DvcgeNkP.js → AppLayout-DDEWVr-d.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-CtYGOtnT.js → Artifacts-CAlJAjRB.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-BBzFYWMk.js → Audit-tuaWDOAM.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-Dl9wCt0Y.js → BackgroundAgents-CIwRFJ50.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-CAL13Eak.js → Backup-CpBepJlH.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-f_lncpU_.js → BaseInput-eKguCzQc.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-DLEbkiqI.js → Chat-CFrUfXDV.js} +4 -4
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-1tCmIvv1.js → ChatBubbleRenderer-DaRtY87l.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-SNvgd2UG.js → Checkbox-o5FEn6a1.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-BG0D-INl.js → Codegen-Dih8HogC.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-DUBBZz0e.js → Col-D5r-6WZe.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-8LfuBDPs.js → Community-jeQ6bGey.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-GWs7Ka6o.js → Compact-CEP7QsWp.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-CXRhB-KC.js → Compliance-B8NydOHd.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-OpW8dLKg.js → Cowork-Bmi1zuPy.js} +2 -2
  22. package/src/assets/web-panel/assets/{Cron-6e47Syc_.js → Cron-BAL9bwSp.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-D-vGH4nC.js → Crosschain-D8WUi2dS.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-zEIa2jNx.js → DID-CQCkYcLY.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-BfUbZxsl.js → Dashboard-DogXg6kh.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-BeCD8gf0.js → Dropdown-BJBJyQJn.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-apOBM2r2.js → EmailListRenderer-CxPWquJx.js} +1 -1
  28. package/src/assets/web-panel/assets/{EvolutionSettings-C2dK36po.js → EvolutionSettings-BijH1x8G.js} +1 -1
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DPMLWPTR.js → FamilyGuardDashboard-DStwf-00.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-CivI2pzm.js → Federation-LU9_sNxC.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-B3zrwBDw.js → FormItemContext-DNfWhj7P.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-jGgdcPck.js → GenericCardRenderer-DWYsPjzk.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-Cpfkrasd.js → Git-CA0O9ulB.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-DG8_7YS3.js → Governance-ECShL7BJ.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-TQgUqPXx.js → Inference-9GA2542e.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-By657fHZ.js → KnowledgeGraph-nC5mTbEH.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-CFJ_70-E.js → Logs-B7IqgnDy.js} +2 -2
  38. package/src/assets/web-panel/assets/MarkdownRenderer-BqlqrJkY.js +1 -0
  39. package/src/assets/web-panel/assets/{Marketplace-BWC3xler.js → Marketplace-CEr0suM8.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-B0NkUzeJ.js → McpTools-DhTPQKOE.js} +3 -3
  41. package/src/assets/web-panel/assets/{Memory-DBnXwjWD.js → Memory-mHRZP1w4.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-BNWDrEJ4.js → MobileBridge-BNfX6Rdo.js} +1 -1
  43. package/src/assets/web-panel/assets/{MobileProjects-BaE94B_N.js → MobileProjects-8zG8f5li.js} +1 -1
  44. package/src/assets/web-panel/assets/{Mtc-Bp3OAEYb.js → Mtc-CzkV3wsn.js} +5 -5
  45. package/src/assets/web-panel/assets/{MtcAudit-_1T24zO3.js → MtcAudit-DWYljJik.js} +4 -4
  46. package/src/assets/web-panel/assets/{Multisig-C2i6Yi0B.js → Multisig-kHgtvZY_.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-CBq3ujGE.js → NLProgramming-56uQiPiY.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-BxRmci_W.js → Notes-DxGOZqRf.js} +3 -3
  49. package/src/assets/web-panel/assets/{NotificationSettings-CqBLgOdv.js → NotificationSettings-DabaCpmo.js} +1 -1
  50. package/src/assets/web-panel/assets/{OrderTableRenderer-cODPlpK7.js → OrderTableRenderer-SzeQNlGT.js} +1 -1
  51. package/src/assets/web-panel/assets/{Organization-DlTnQBlp.js → Organization-D9gnPpK2.js} +4 -4
  52. package/src/assets/web-panel/assets/{Overflow-Df2jS2Ep.js → Overflow-Cqp_bXm4.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-BwieT0aA.js → P2P-DTeK-r0G.js} +2 -2
  54. package/src/assets/web-panel/assets/{PdhVaultBrowser-Btw816xR.js → PdhVaultBrowser-CsjG85GT.js} +3 -3
  55. package/src/assets/web-panel/assets/{Permissions-BhVYLcqT.js → Permissions-B4ofZq3j.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-CPbNKPCA.js → PersonalDataHub-Cvas5cYd.js} +4 -4
  57. package/src/assets/web-panel/assets/{Pipeline-B1BuhoY3.js → Pipeline-Er3wmAew.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-BWeikVbU.js → Privacy-DMFYeZox.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-BVXcimHE.js → ProjectInit-DaVaYPy0.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-CX87x3rT.js → ProjectSettings-dPx5q2Ac.js} +2 -2
  61. package/src/assets/web-panel/assets/Projects-N0JNeCui.js +1 -0
  62. package/src/assets/web-panel/assets/{Providers-CGm0Wuer.js → Providers-BPQo_gQa.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-DJxv7J64.js → QrScannerModal-CD-sEsWh.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-DOXBReRk.js → QuickAsk-LOKRJiN_.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend--MiLwmLM.js → Recommend-BwPXqcWy.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-D6LoXH94.js → RemoteSession-BjotndxF.js} +3 -3
  67. package/src/assets/web-panel/assets/{Reputation-G2jToiqh.js → Reputation-BlZ2gcbU.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-B79ZV_lZ.js → Row-D399HM-d.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-CHQN68w_.js → RssFeed-C7H-ibnY.js} +2 -2
  70. package/src/assets/web-panel/assets/{Search-j7v5S2Fa.js → Search-CNHCVmM1.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-B2ykFZJH.js → Security-C70eDGYY.js} +3 -3
  72. package/src/assets/web-panel/assets/{Services-H8BK8uX0.js → Services-6xfq9G-H.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-XEUUrOTK.js → Skeleton-BbY2zeZV.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-B387UJPk.js → Skills-DYtyZ7_6.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-CWWBujpe.js → Sla-CkWcl5OH.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-Q2AOv9DV.js → SpeechSettings-CiknYk2P.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-OSNyOUr8.js → SyncSettings-D-8JVip4.js} +2 -2
  78. package/src/assets/web-panel/assets/{Tasks-DfUDrno5.js → Tasks-B2KnR9XC.js} +1 -1
  79. package/src/assets/web-panel/assets/{Templates-CSTkzDRS.js → Templates-CipXkOE-.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-BR__gCTG.js → Tenant-DbwgIMmx.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-Bv4DM5OI.js → Terminal-OtCriHEE.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-DUnZuAtn.js → TimelineRenderer-B9LU5qrl.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-Dm0sczRV.js → Tokens-B4iFVxvH.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-DsSOp0Ob.js → Trigger-BegM2v8q.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-C0HJtFwV.js → Trust-COkqnnFj.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-BbqIZiX-.js → UkeySign-BzqKPChj.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-D7dDTt3D.js → VideoEditing-D-F2XL3C.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-DJGu0KWq.js → Wallet-22PzRY9T.js} +2 -2
  89. package/src/assets/web-panel/assets/{WebAuthn-x8OoogZU.js → WebAuthn-eSMq_qtz.js} +4 -4
  90. package/src/assets/web-panel/assets/{WorkflowEditor-CC3jIUqx.js → WorkflowEditor-BvdAakpq.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-Bqgv5oe8.js → chat-C6jhpF7E.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-CbAMqLFr.js → colors-BbbW2Pfv.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-B-zIDFg3.js → compact-item-CVFeLTqt.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-D_s1ZLtZ.js → createContext-DS0m7NZc.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-D50gzPS9.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-Cj_J9Eb9.js → hasIn-DVouP0t6.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BtJzqXCQ.js → index--AdzSlH7.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-D_igPAXH.js → index-54aHB4FL.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BivQyKes.js → index-8QbwhE3i.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-3o8DjHHt.js → index-B5JRIZXi.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-0je3TLmx.js → index-B8a_c_qZ.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cz0blpuN.js → index-BE_u-rXr.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-Cu3FtpNc.js → index-BOOsA080.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-KY7udrs-.js → index-BXw6ivB8.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-J4JLWUMK.js → index-BaUah6TO.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-C6bAvixK.js → index-Be12HoCS.js} +3 -3
  107. package/src/assets/web-panel/assets/{index-Cy3XOp7S.js → index-BhvnNFfd.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-C6lO24YD.js → index-BrIv7C9a.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BPdH-XTd.js → index-BvCgzl0X.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Cg0oYTy5.js → index-Bw-ZGQCH.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-ab6FUgkq.js → index-BwZnPiFd.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DuqVIsHL.js → index-BwozzG6k.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B6qoyCkx.js → index-ByLsH9CL.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BjfpW5s3.js → index-BzL3LWFX.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BFNO6ycz.js → index-C4xmLgLa.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-dg0JqQDA.js → index-C6q2MkIG.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DtplHZ5o.js → index-C74emdKD.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Bi1E5h9V.js → index-CIg4nqyp.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-C-Z13SCI.js → index-CIk7mg6G.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-JdSkHz64.js → index-CPS8E-J6.js} +1 -1
  121. package/src/assets/web-panel/assets/index-CgensV-n.js +1 -0
  122. package/src/assets/web-panel/assets/{index-DVjFV2mg.js → index-CkdYB0_g.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CsHTqPTg.js → index-CpTbnbUI.js} +1 -1
  124. package/src/assets/web-panel/assets/index-Ctkf06Z_.js +1 -0
  125. package/src/assets/web-panel/assets/{index-nEWJobH4.js → index-CwYFDMn_.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BN5sO3kL.js → index-DGYX17YW.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CD_A-N7c.js → index-DR6TDz5H.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-ggeC-_dR.js → index-DUBkae39.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-D9CqHOgd.js → index-DotRL126.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CSzWODfj.js → index-DpReoAGb.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-Bw88Mk2t.js → index-Ds4wbrgk.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BGxI_Qxg.js → index-DuVjTDcW.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BLwUCYDf.js → index-JeJqXbUU.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-D1t2IqNU.js → index-SdFkCtiC.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CVPI_iIh.js → index-eaXEQFuP.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-DDIGXQ08.js → initDefaultProps-CB5cUnQ5.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-97SXSYE4.js → motion-bn_fWhQV.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-CfDQMFRe.js → move-DY4-Odtu.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-qLYCGwY8.js → mtc-parser-B5l5mjoh.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-xVqy1mCI.js → omit-rJ-Ci8CW.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-DCGlFyfv.js → pickAttrs-BMwGJd6C.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-DYgPuMk3.js → placementArrow-CaiQyGXG.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-7zUIdAWH.js → responsiveObserve-DQTO5wDH.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-TyaiZlSh.js → slide-Dg6ZDtbz.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-BPd13Fae.js → statusUtils-bvjrktKc.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-A44XGgQ0.js → styleChecker-C1geIU_x.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-ZX5EUa8B.js → useFlexGapSupport-BzU3RJRi.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-Sh_fBPqz.js → useFs-B7l6OwLe.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-DhR6Izei.js → usePersonalDataHub-BYF1L_8x.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-DC8g6oRh.js → vnode-6c2GVkT-.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-DRjJmOwj.js → zoom-BYAY6QeD.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/commands/agent.js +21 -10
  154. package/src/data/changelog.json +39 -0
  155. package/src/gateways/ws/session-protocol.js +19 -5
  156. package/src/gateways/ws/ws-agent-handler.js +6 -0
  157. package/src/gateways/ws/ws-session-gateway.js +133 -21
  158. package/src/lib/agent-execution-policy.js +177 -0
  159. package/src/lib/authority-launch-environment.js +99 -0
  160. package/src/lib/auto-mode-config.js +129 -45
  161. package/src/lib/claude-project-auto-memory.js +7 -30
  162. package/src/lib/context-memory-kernel/task-worklog-port.js +73 -24
  163. package/src/lib/diagnostic-excerpt.js +91 -10
  164. package/src/lib/diagnostic-workflow.js +2 -0
  165. package/src/lib/evolution/volcengine-function-replay-store.js +38 -28
  166. package/src/lib/git-tool-input.js +7 -0
  167. package/src/lib/host-tool-policy-authority.js +151 -0
  168. package/src/lib/permission-authority.js +67 -5
  169. package/src/lib/plugin-runtime/settings.js +9 -1
  170. package/src/lib/remote-read-loop-guard.js +59 -14
  171. package/src/lib/sandbox-egress-authority-monitor.js +3 -2
  172. package/src/lib/session-manager.js +20 -5
  173. package/src/lib/settings-loader.cjs +175 -19
  174. package/src/lib/settings-source-observation.cjs +270 -0
  175. package/src/lib/sub-agent-context.js +36 -18
  176. package/src/lib/task-progress-tracker.js +112 -12
  177. package/src/lib/unattended-action-policy.js +456 -38
  178. package/src/repl/agent-repl.js +58 -29
  179. package/src/repl/repl-tool-executor.js +39 -0
  180. package/src/runtime/agent-core.js +272 -5
  181. package/src/runtime/agent-runtime.js +15 -3
  182. package/src/runtime/headless-runner.js +13 -4
  183. package/src/runtime/headless-stream.js +9 -0
  184. package/src/runtime/policies/agent-policy.js +5 -4
  185. package/src/assets/web-panel/assets/MarkdownRenderer-BpRWpN1E.js +0 -1
  186. package/src/assets/web-panel/assets/Projects-3Qo2Mvxb.js +0 -1
  187. package/src/assets/web-panel/assets/devWarning-QMSC9xgn.js +0 -1
  188. package/src/assets/web-panel/assets/index-DVq7UuW0.js +0 -1
  189. package/src/assets/web-panel/assets/index-DYVepsIV.js +0 -1
@@ -27,9 +27,83 @@
27
27
  const fsDefault = require("node:fs");
28
28
  const path = require("node:path");
29
29
  const os = require("node:os");
30
+ const { randomUUID } = require("node:crypto");
30
31
  const { projectRootBase } = require("./project-root.cjs");
32
+ const { observeSettingsSources } = require("./settings-source-observation.cjs");
33
+ // Node >=22.12 supports synchronous require(ESM). Load these write-only
34
+ // helpers on first use: permission readers should not trigger Node 22.12's
35
+ // native require(ESM) warning just by discovering settings. Official writes
36
+ // remain synchronous and keep the runtime's warning when applicable.
37
+ let securityStoreHelpers;
38
+ let fileLockHelper;
39
+ function securityStore() {
40
+ return (securityStoreHelpers ??= require("./durable-security-store.js"));
41
+ }
42
+ function lockHelper() {
43
+ return (fileLockHelper ??= require("./with-file-lock.js").withFileLock);
44
+ }
45
+
46
+ const _deps = {
47
+ fs: fsDefault,
48
+ homedir: () => os.homedir(),
49
+ readSecurityStore: (...args) => securityStore().readSecurityStore(...args),
50
+ writeSecurityStore: (...args) => securityStore().writeSecurityStore(...args),
51
+ withFileLock: (...args) => lockHelper()(...args),
52
+ };
53
+
54
+ // A process-wide owner deliberately revokes unrelated projects too. Matching
55
+ // only contributing paths misses absent files, parent discovery and aliases.
56
+ // Only this module can advance it; raw edits and other processes are not events.
57
+ const settingsOwnerId = randomUUID();
58
+ let settingsRevision = Object.freeze({
59
+ ownerId: settingsOwnerId,
60
+ revision: 0,
61
+ state: "ready",
62
+ });
63
+ const settingsListeners = new Set();
64
+
65
+ function getSettingsPermissionRevision() {
66
+ if (settingsRevision.state !== "ready") {
67
+ const error = new Error("settings permission authority is unavailable");
68
+ error.code = "CC_SETTINGS_PERMISSION_AUTHORITY_UNAVAILABLE";
69
+ throw error;
70
+ }
71
+ return settingsRevision;
72
+ }
73
+
74
+ function subscribeSettingsPermissionRevision(listener) {
75
+ if (typeof listener !== "function") {
76
+ throw new TypeError("settings revision listener must be a function");
77
+ }
78
+ settingsListeners.add(listener);
79
+ return () => settingsListeners.delete(listener);
80
+ }
81
+
82
+ function beginSettingsMutation() {
83
+ getSettingsPermissionRevision();
84
+ const next = settingsRevision.revision + 1;
85
+ settingsRevision = Object.freeze({
86
+ ownerId: settingsOwnerId,
87
+ revision: next,
88
+ state: Number.isSafeInteger(next) ? "mutating" : "invalid",
89
+ });
90
+ // Latch before calling observers. Reentrant writers/readers cannot acquire
91
+ // the lock or obtain authority while an official write is in progress.
92
+ for (const listener of [...settingsListeners]) {
93
+ try {
94
+ listener(settingsRevision);
95
+ } catch {
96
+ // An observer cannot prevent another running shell from being revoked.
97
+ }
98
+ }
99
+ if (settingsRevision.state === "invalid") {
100
+ getSettingsPermissionRevision();
101
+ }
102
+ }
31
103
 
32
- const _deps = { fs: fsDefault, homedir: () => os.homedir() };
104
+ function finishSettingsMutation(state) {
105
+ settingsRevision = Object.freeze({ ...settingsRevision, state });
106
+ }
33
107
 
34
108
  const KINDS = Object.freeze(["allow", "ask", "deny"]);
35
109
 
@@ -88,6 +162,27 @@ function settingsPaths(cwd, explicitFile) {
88
162
  return list;
89
163
  }
90
164
 
165
+ /**
166
+ * Strict, read-only inventory for persistent-authority integration. Includes
167
+ * every ordered candidate, even absent files and files with no permission
168
+ * contribution. This does not register an anchor or grant execution authority.
169
+ */
170
+ function inspectSettingsSources(opts = {}) {
171
+ const cwd = path.resolve(opts.cwd || process.cwd());
172
+ const candidates = [
173
+ ...settingsPaths(cwd, opts.settingsFile),
174
+ managedSettingsPath(opts),
175
+ ];
176
+ return Object.freeze({
177
+ schema: "chainlesschain.settings-source-observation/v1",
178
+ cwd,
179
+ sources: observeSettingsSources(candidates, {
180
+ fs: _deps.fs,
181
+ maxBytes: opts.maxBytes,
182
+ }),
183
+ });
184
+ }
185
+
91
186
  /** Organization-controlled settings file. This layer is always highest. */
92
187
  function managedSettingsPath(opts = {}) {
93
188
  if (opts.managedSettingsFile) return path.resolve(opts.managedSettingsFile);
@@ -260,30 +355,88 @@ function scopeFile(cwd, scope) {
260
355
  * @throws if the target file exists but is malformed JSON (refuse to clobber).
261
356
  */
262
357
  function addRule({ cwd = process.cwd(), kind, rule, scope = "project" } = {}) {
358
+ const entryCwd = process.cwd();
359
+ const absoluteCwd = path.resolve(entryCwd, cwd);
360
+ // Check before lock acquisition: notification callbacks may reenter here.
361
+ getSettingsPermissionRevision();
263
362
  if (!KINDS.includes(kind)) {
264
363
  throw new Error(`kind must be allow | ask | deny (got "${kind}")`);
265
364
  }
266
- const file = scopeFile(cwd, scope);
267
- let data = {};
268
- if (_deps.fs.existsSync(file)) {
269
- const text = _deps.fs.readFileSync(file, "utf-8");
270
- try {
271
- data = JSON.parse(text) || {};
272
- } catch (err) {
273
- throw new Error(
274
- `refusing to overwrite malformed ${file} (${err.message})`,
275
- );
276
- }
365
+ if (typeof rule !== "string" || !rule.trim()) {
366
+ throw new TypeError("permission rule must be a non-empty string");
277
367
  }
278
- if (!data.permissions || typeof data.permissions !== "object") {
279
- data.permissions = {};
368
+ if (!["project", "local", "user"].includes(scope)) {
369
+ throw new TypeError("scope must be project | local | user");
280
370
  }
281
- if (!Array.isArray(data.permissions[kind])) data.permissions[kind] = [];
282
- if (data.permissions[kind].includes(rule)) return { file, added: false };
283
- data.permissions[kind].push(rule);
371
+ const file = path.resolve(entryCwd, scopeFile(absoluteCwd, scope));
284
372
  _deps.fs.mkdirSync(path.dirname(file), { recursive: true });
285
- _deps.fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n", "utf-8");
286
- return { file, added: true };
373
+ let began = false;
374
+ let committed = false;
375
+ try {
376
+ const result = _deps.withFileLock(
377
+ file,
378
+ () => {
379
+ // Atomic replacement would otherwise replace the link itself after
380
+ // reading its target. Separate file links also have separate locks.
381
+ // Directory aliases continue to address the same file and lock.
382
+ if (
383
+ _deps.fs.lstatSync(file, { throwIfNoEntry: false })?.isSymbolicLink()
384
+ ) {
385
+ throw new TypeError(
386
+ `refusing to replace a settings file symbolic link: ${file}`,
387
+ );
388
+ }
389
+ 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))
417
+ return { file, added: false };
418
+ data.permissions[kind].push(rule);
419
+ beginSettingsMutation();
420
+ began = true;
421
+ _deps.writeSecurityStore(file, "settings", data);
422
+ committed = true;
423
+ return { file, added: true };
424
+ },
425
+ { failIfUnavailable: true },
426
+ );
427
+ if (began) finishSettingsMutation("ready");
428
+ return result;
429
+ } catch (error) {
430
+ if (!error.commitState) {
431
+ error.commitState = committed ? "committed" : "not-committed";
432
+ }
433
+ if (began) {
434
+ finishSettingsMutation(
435
+ error.commitState === "unknown" ? "invalid" : "ready",
436
+ );
437
+ }
438
+ throw error;
439
+ }
287
440
  }
288
441
 
289
442
  /**
@@ -508,6 +661,7 @@ function readStringArraySetting(key, opts = {}) {
508
661
  }
509
662
 
510
663
  module.exports = {
664
+ inspectSettingsSources,
511
665
  loadSettings,
512
666
  loadSettingsConfig,
513
667
  readSettingsFile,
@@ -523,6 +677,8 @@ module.exports = {
523
677
  mergeSandboxSettings,
524
678
  ruleSource,
525
679
  addRule,
680
+ getSettingsPermissionRevision,
681
+ subscribeSettingsPermissionRevision,
526
682
  scopeFile,
527
683
  KINDS,
528
684
  _deps,
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Read-only settings observations for a future persistent authority protocol.
5
+ * Only Node builtins: CJS readers must not materialize a require(ESM) bridge.
6
+ *
7
+ * These checks detect ordinary replacement and in-place edits. Path-based Node
8
+ * APIs do not prevent an adversarial parent swap-and-restore between checks.
9
+ * No observation is a generation, registration, or permission grant.
10
+ */
11
+ const fsDefault = require("node:fs");
12
+ const path = require("node:path");
13
+ const { createHash } = require("node:crypto");
14
+ const { TextDecoder } = require("node:util");
15
+
16
+ const MAX_SETTINGS_SOURCE_BYTES = 1024 * 1024;
17
+ const MAX_SETTINGS_SOURCES = 64;
18
+ const FILE_FIELDS = [
19
+ "dev",
20
+ "ino",
21
+ "mode",
22
+ "nlink",
23
+ "size",
24
+ "mtimeNs",
25
+ "ctimeNs",
26
+ ];
27
+
28
+ function failure(code, cause = null) {
29
+ const error = new Error(
30
+ "Settings source observation is unavailable",
31
+ cause ? { cause } : undefined,
32
+ );
33
+ error.name = "SettingsSourceObservationError";
34
+ error.code = code;
35
+ return error;
36
+ }
37
+
38
+ function sameFields(left, right, fields) {
39
+ return fields.every(
40
+ (field) =>
41
+ left[field] !== undefined &&
42
+ right[field] !== undefined &&
43
+ left[field] === right[field],
44
+ );
45
+ }
46
+
47
+ function pathIdentity(value) {
48
+ return process.platform === "win32" ? value.toLowerCase() : value;
49
+ }
50
+
51
+ function lstatOrMissing(fs, file) {
52
+ try {
53
+ return fs.lstatSync(file, { bigint: true });
54
+ } catch (cause) {
55
+ if (cause?.code === "ENOENT") return null;
56
+ if (cause?.code === "ENOTDIR")
57
+ throw failure("CC_SETTINGS_SOURCE_UNSAFE", cause);
58
+ throw cause;
59
+ }
60
+ }
61
+
62
+ function assertRegularFile(stat) {
63
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1n) {
64
+ throw failure("CC_SETTINGS_SOURCE_UNSAFE");
65
+ }
66
+ }
67
+
68
+ function freezeJson(value) {
69
+ const pending = [value];
70
+ while (pending.length) {
71
+ const current = pending.pop();
72
+ if (!current || typeof current !== "object") continue;
73
+ for (const child of Object.values(current)) pending.push(child);
74
+ Object.freeze(current);
75
+ }
76
+ return value;
77
+ }
78
+
79
+ function inspect(file, fs, maxBytes) {
80
+ const logicalPath = path.resolve(file);
81
+ const entry = lstatOrMissing(fs, logicalPath);
82
+ if (entry) assertRegularFile(entry);
83
+
84
+ // Preserve absent candidates too, including a missing .claude directory.
85
+ let logicalParent = path.dirname(logicalPath);
86
+ const remaining = [path.basename(logicalPath)];
87
+ while (!lstatOrMissing(fs, logicalParent)) {
88
+ const next = path.dirname(logicalParent);
89
+ if (next === logicalParent) throw failure("CC_SETTINGS_SOURCE_UNAVAILABLE");
90
+ remaining.unshift(path.basename(logicalParent));
91
+ logicalParent = next;
92
+ }
93
+ const canonicalParent = fs.realpathSync(logicalParent);
94
+ const physicalPath = path.join(canonicalParent, ...remaining);
95
+ const flags =
96
+ (fs.constants?.O_RDONLY ?? fsDefault.constants.O_RDONLY) |
97
+ (fs.constants?.O_NOFOLLOW ?? fsDefault.constants.O_NOFOLLOW ?? 0) |
98
+ (fs.constants?.O_NONBLOCK ?? fsDefault.constants.O_NONBLOCK ?? 0);
99
+ const descriptors = [];
100
+ function open(filePath) {
101
+ const fd = fs.openSync(filePath, flags);
102
+ descriptors.push(fd);
103
+ return fd;
104
+ }
105
+ function verifyParent(original) {
106
+ if (
107
+ pathIdentity(fs.realpathSync(logicalParent)) !==
108
+ pathIdentity(canonicalParent)
109
+ ) {
110
+ throw failure("CC_SETTINGS_SOURCE_CHANGED");
111
+ }
112
+ const current = fs.fstatSync(open(canonicalParent), { bigint: true });
113
+ if (
114
+ !current.isDirectory() ||
115
+ !sameFields(original, current, ["dev", "ino", "mode"])
116
+ ) {
117
+ throw failure("CC_SETTINGS_SOURCE_CHANGED");
118
+ }
119
+ }
120
+
121
+ function readOpenedSource() {
122
+ // Comparing opened handles avoids Node 22.12 Windows path-stat's projected
123
+ // zero device; both identities come from the same API and real volume.
124
+ const parent = fs.fstatSync(open(canonicalParent), { bigint: true });
125
+ if (!parent.isDirectory()) throw failure("CC_SETTINGS_SOURCE_UNSAFE");
126
+ const common = {
127
+ logicalPath,
128
+ physicalPath,
129
+ nearestExistingParent: canonicalParent,
130
+ remainingPath: path.join(...remaining),
131
+ parentIdentity: Object.freeze({
132
+ dev: String(parent.dev),
133
+ ino: String(parent.ino),
134
+ }),
135
+ };
136
+ if (!entry) {
137
+ if (lstatOrMissing(fs, logicalPath))
138
+ throw failure("CC_SETTINGS_SOURCE_CHANGED");
139
+ verifyParent(parent);
140
+ if (lstatOrMissing(fs, path.join(logicalParent, remaining[0])))
141
+ throw failure("CC_SETTINGS_SOURCE_CHANGED");
142
+ if (lstatOrMissing(fs, physicalPath))
143
+ throw failure("CC_SETTINGS_SOURCE_CHANGED");
144
+ return Object.freeze({
145
+ ...common,
146
+ exists: false,
147
+ byteLength: 0,
148
+ digest: null,
149
+ settings: null,
150
+ });
151
+ }
152
+
153
+ const fd = open(physicalPath);
154
+ const before = fs.fstatSync(fd, { bigint: true });
155
+ assertRegularFile(before);
156
+ if (before.size > BigInt(maxBytes))
157
+ throw failure("CC_SETTINGS_SOURCE_TOO_LARGE");
158
+ const buffer = Buffer.alloc(maxBytes + 1);
159
+ let length = 0;
160
+ for (;;) {
161
+ const count = fs.readSync(
162
+ fd,
163
+ buffer,
164
+ length,
165
+ buffer.length - length,
166
+ length,
167
+ );
168
+ if (!count) break;
169
+ length += count;
170
+ if (length > maxBytes) throw failure("CC_SETTINGS_SOURCE_TOO_LARGE");
171
+ }
172
+ const after = fs.fstatSync(fd, { bigint: true });
173
+ if (
174
+ !sameFields(before, after, FILE_FIELDS) ||
175
+ BigInt(length) !== after.size
176
+ ) {
177
+ throw failure("CC_SETTINGS_SOURCE_CHANGED");
178
+ }
179
+ const finalEntry = lstatOrMissing(fs, logicalPath);
180
+ if (!finalEntry) throw failure("CC_SETTINGS_SOURCE_CHANGED");
181
+ assertRegularFile(finalEntry);
182
+ const reopened = fs.fstatSync(open(physicalPath), { bigint: true });
183
+ if (!sameFields(before, reopened, FILE_FIELDS))
184
+ throw failure("CC_SETTINGS_SOURCE_CHANGED");
185
+ verifyParent(parent);
186
+ const bytes = buffer.subarray(0, length);
187
+ let settings;
188
+ try {
189
+ const text = new TextDecoder("utf-8", {
190
+ fatal: true,
191
+ ignoreBOM: true,
192
+ }).decode(bytes);
193
+ settings = JSON.parse(text);
194
+ if (!settings || typeof settings !== "object" || Array.isArray(settings))
195
+ throw new TypeError("Settings must be an object");
196
+ } catch (cause) {
197
+ throw failure("CC_SETTINGS_SOURCE_INVALID", cause);
198
+ }
199
+ return Object.freeze({
200
+ ...common,
201
+ exists: true,
202
+ byteLength: length,
203
+ digest: createHash("sha256").update(bytes).digest("hex"),
204
+ settings: freezeJson(settings),
205
+ });
206
+ }
207
+ let result;
208
+ let bodyError = null;
209
+ let bodyThrew = false;
210
+ let closeError = null;
211
+ try {
212
+ result = readOpenedSource();
213
+ } catch (cause) {
214
+ bodyThrew = true;
215
+ bodyError = cause;
216
+ } finally {
217
+ for (const fd of descriptors.reverse()) {
218
+ try {
219
+ fs.closeSync(fd);
220
+ } catch (cause) {
221
+ closeError ??= cause;
222
+ }
223
+ }
224
+ }
225
+ if (bodyThrew) throw bodyError;
226
+ if (closeError) throw closeError;
227
+ return result;
228
+ }
229
+
230
+ function observeSettingsSource(
231
+ file,
232
+ { fs = fsDefault, maxBytes = MAX_SETTINGS_SOURCE_BYTES } = {},
233
+ ) {
234
+ if (typeof file !== "string" || !file || file.includes("\0"))
235
+ throw new TypeError("A settings source path is required");
236
+ if (
237
+ !Number.isSafeInteger(maxBytes) ||
238
+ maxBytes < 1 ||
239
+ maxBytes > MAX_SETTINGS_SOURCE_BYTES
240
+ )
241
+ throw new TypeError("Settings source byte limit is invalid");
242
+ try {
243
+ return inspect(file, fs, maxBytes);
244
+ } catch (cause) {
245
+ if (cause?.name === "SettingsSourceObservationError") throw cause;
246
+ throw failure("CC_SETTINGS_SOURCE_UNAVAILABLE", cause);
247
+ }
248
+ }
249
+
250
+ function observeSettingsSources(files, options = {}) {
251
+ if (!Array.isArray(files) || files.length > MAX_SETTINGS_SOURCES)
252
+ throw new TypeError("Settings source list must be bounded");
253
+ // Resolve every input before the first filesystem operation; later chdir
254
+ // cannot change where a remaining relative candidate is inspected.
255
+ const resolved = files.map((file) => {
256
+ if (typeof file !== "string" || !file || file.includes("\0"))
257
+ throw new TypeError("A settings source path is required");
258
+ return path.resolve(file);
259
+ });
260
+ return Object.freeze(
261
+ resolved.map((file) => observeSettingsSource(file, options)),
262
+ );
263
+ }
264
+
265
+ module.exports = {
266
+ MAX_SETTINGS_SOURCE_BYTES,
267
+ MAX_SETTINGS_SOURCES,
268
+ observeSettingsSource,
269
+ observeSettingsSources,
270
+ };
@@ -9,6 +9,10 @@
9
9
  */
10
10
 
11
11
  import crypto from "crypto";
12
+ import {
13
+ captureAgentExecutionPolicy,
14
+ resolveAgentToolSelection,
15
+ } from "./agent-execution-policy.js";
12
16
  import { CLIContextEngineering } from "./cli-context-engineering.js";
13
17
  import {
14
18
  agentLoop,
@@ -206,7 +210,12 @@ export class SubAgentContext {
206
210
  this.taskProgressTracker = options.taskProgressTracker || null;
207
211
  this.tokenBudget = options.tokenBudget || null;
208
212
  this.inheritedContext = options.inheritedContext || null;
209
- this.allowedTools = options.allowedTools ?? null; // null = all; [] = none
213
+ const executionPolicy = captureAgentExecutionPolicy(options);
214
+ const toolSelection = resolveAgentToolSelection(executionPolicy);
215
+ Object.defineProperty(this, "allowedTools", {
216
+ value: toolSelection.enabledToolNames, // null = all; [] = none
217
+ enumerable: true,
218
+ });
210
219
  this.depth = options.depth || 1; // nesting level (parent main loop = 0)
211
220
  // Shared run-wide TOTAL-sub-agent counter (one object across the whole tree)
212
221
  // so this sub-agent's own spawns draw from the same breadth pool.
@@ -218,7 +227,10 @@ export class SubAgentContext {
218
227
  // Optional session-level Extension Tier admission policy. Keep it as an
219
228
  // explicit inherited capability so child loops cannot silently bypass the
220
229
  // parent's admission decision.
221
- this.toolAdmission = options.toolAdmission || null;
230
+ Object.defineProperty(this, "toolAdmission", {
231
+ value: executionPolicy.toolAdmission,
232
+ enumerable: true,
233
+ });
222
234
  // Skill capability INTERSECT: null = unrestricted; a list (possibly empty)
223
235
  // restricts run_skill/list_skills in this context's loop to those skills.
224
236
  this.skillAllowlist =
@@ -374,14 +386,11 @@ export class SubAgentContext {
374
386
  permissionRules: options.permissionRules || null,
375
387
  permissionRulesProvider: options.permissionRulesProvider || null,
376
388
  hostManagedToolPolicy: options.hostManagedToolPolicy || null,
389
+ hostManagedToolPolicyAuthority:
390
+ options.hostManagedToolPolicyAuthority || null,
377
391
  planManager: options.planManager || null,
378
- sandbox: options.sandbox || null,
379
- additionalDirectories: Array.isArray(options.additionalDirectories)
380
- ? Object.freeze([...options.additionalDirectories])
381
- : null,
382
- shellPolicyOverrides: options.shellPolicyOverrides || null,
383
- classifyAllShell: options.classifyAllShell === true,
384
- unattendedActionPolicy: options.unattendedActionPolicy || null,
392
+ ...executionPolicy,
393
+ disabledTools: toolSelection.disabledTools,
385
394
  });
386
395
 
387
396
  // Build isolated system prompt
@@ -409,6 +418,8 @@ export class SubAgentContext {
409
418
  * @returns {Promise<{ summary: string, artifacts: Array, tokenCount: number, toolsUsed: string[], iterationCount: number }>}
410
419
  */
411
420
  async run(userPrompt, loopOptions = {}) {
421
+ const executionPolicy = captureAgentExecutionPolicy(loopOptions);
422
+ loopOptions = { ...loopOptions, ...executionPolicy };
412
423
  if (this.status !== "active") {
413
424
  throw new Error(
414
425
  `SubAgentContext ${this.id} is not active (status: ${this.status})`,
@@ -663,10 +674,14 @@ export class SubAgentContext {
663
674
  // Re-apply tighten-only capability fields after loopOptions. In particular,
664
675
  // [] must survive as deny-all and cannot be replaced by a worktree/direct
665
676
  // runner override.
666
- if (Array.isArray(this.allowedTools)) {
667
- options.enabledToolNames = [...this.allowedTools];
677
+ const selectionPolicy = captureAgentExecutionPolicy(options);
678
+ const toolSelection = resolveAgentToolSelection(
679
+ selectionPolicy,
680
+ this.allowedTools,
681
+ );
682
+ options.enabledToolNames = toolSelection.enabledToolNames;
683
+ if (Array.isArray(toolSelection.enabledToolNames))
668
684
  options.exactToolNames = true;
669
- }
670
685
  options.subAgentContract = this.subAgentContract;
671
686
  options.sessionBudget = this.sessionBudget;
672
687
  options.hostResourceBudget = this.hostResourceBudget;
@@ -697,14 +712,17 @@ export class SubAgentContext {
697
712
  if (authority.hostManagedToolPolicy) {
698
713
  options.hostManagedToolPolicy = authority.hostManagedToolPolicy;
699
714
  }
715
+ if (authority.hostManagedToolPolicyAuthority) {
716
+ options.hostManagedToolPolicyAuthority =
717
+ authority.hostManagedToolPolicyAuthority;
718
+ }
700
719
  if (authority.planManager) options.planManager = authority.planManager;
701
720
  if (authority.sandbox) options.sandbox = authority.sandbox;
702
- if (authority.additionalDirectories) {
703
- options.additionalDirectories = [...authority.additionalDirectories];
704
- }
705
- if (authority.shellPolicyOverrides) {
706
- options.shellPolicyOverrides = authority.shellPolicyOverrides;
707
- }
721
+ options.additionalDirectories = authority.additionalDirectories || [];
722
+ options.shellPolicyOverrides = authority.shellPolicyOverrides;
723
+ options.disabledTools = Object.freeze([
724
+ ...new Set([...authority.disabledTools, ...toolSelection.disabledTools]),
725
+ ]);
708
726
  if (authority.classifyAllShell) options.classifyAllShell = true;
709
727
  if (authority.unattendedActionPolicy) {
710
728
  options.unattendedActionPolicy = authority.unattendedActionPolicy;