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.
- package/package.json +1 -1
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-BZPs-bVl.js → AIOps-D5a4i3N7.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BP1iLSwn.js → ActionButton-DxsRk5n1.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-WxwtPqcd.js → Analytics-D-7EvH4S.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DvcgeNkP.js → AppLayout-DDEWVr-d.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CtYGOtnT.js → Artifacts-CAlJAjRB.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BBzFYWMk.js → Audit-tuaWDOAM.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Dl9wCt0Y.js → BackgroundAgents-CIwRFJ50.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CAL13Eak.js → Backup-CpBepJlH.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-f_lncpU_.js → BaseInput-eKguCzQc.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DLEbkiqI.js → Chat-CFrUfXDV.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-1tCmIvv1.js → ChatBubbleRenderer-DaRtY87l.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-SNvgd2UG.js → Checkbox-o5FEn6a1.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BG0D-INl.js → Codegen-Dih8HogC.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DUBBZz0e.js → Col-D5r-6WZe.js} +1 -1
- package/src/assets/web-panel/assets/{Community-8LfuBDPs.js → Community-jeQ6bGey.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-GWs7Ka6o.js → Compact-CEP7QsWp.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CXRhB-KC.js → Compliance-B8NydOHd.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-OpW8dLKg.js → Cowork-Bmi1zuPy.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-6e47Syc_.js → Cron-BAL9bwSp.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-D-vGH4nC.js → Crosschain-D8WUi2dS.js} +1 -1
- package/src/assets/web-panel/assets/{DID-zEIa2jNx.js → DID-CQCkYcLY.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BfUbZxsl.js → Dashboard-DogXg6kh.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BeCD8gf0.js → Dropdown-BJBJyQJn.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-apOBM2r2.js → EmailListRenderer-CxPWquJx.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-C2dK36po.js → EvolutionSettings-BijH1x8G.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DPMLWPTR.js → FamilyGuardDashboard-DStwf-00.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CivI2pzm.js → Federation-LU9_sNxC.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-B3zrwBDw.js → FormItemContext-DNfWhj7P.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-jGgdcPck.js → GenericCardRenderer-DWYsPjzk.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Cpfkrasd.js → Git-CA0O9ulB.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DG8_7YS3.js → Governance-ECShL7BJ.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-TQgUqPXx.js → Inference-9GA2542e.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-By657fHZ.js → KnowledgeGraph-nC5mTbEH.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CFJ_70-E.js → Logs-B7IqgnDy.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BqlqrJkY.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BWC3xler.js → Marketplace-CEr0suM8.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-B0NkUzeJ.js → McpTools-DhTPQKOE.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-DBnXwjWD.js → Memory-mHRZP1w4.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BNWDrEJ4.js → MobileBridge-BNfX6Rdo.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BaE94B_N.js → MobileProjects-8zG8f5li.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Bp3OAEYb.js → Mtc-CzkV3wsn.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-_1T24zO3.js → MtcAudit-DWYljJik.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-C2i6Yi0B.js → Multisig-kHgtvZY_.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CBq3ujGE.js → NLProgramming-56uQiPiY.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BxRmci_W.js → Notes-DxGOZqRf.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CqBLgOdv.js → NotificationSettings-DabaCpmo.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-cODPlpK7.js → OrderTableRenderer-SzeQNlGT.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DlTnQBlp.js → Organization-D9gnPpK2.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Df2jS2Ep.js → Overflow-Cqp_bXm4.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BwieT0aA.js → P2P-DTeK-r0G.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Btw816xR.js → PdhVaultBrowser-CsjG85GT.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BhVYLcqT.js → Permissions-B4ofZq3j.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CPbNKPCA.js → PersonalDataHub-Cvas5cYd.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-B1BuhoY3.js → Pipeline-Er3wmAew.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BWeikVbU.js → Privacy-DMFYeZox.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BVXcimHE.js → ProjectInit-DaVaYPy0.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CX87x3rT.js → ProjectSettings-dPx5q2Ac.js} +2 -2
- package/src/assets/web-panel/assets/Projects-N0JNeCui.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CGm0Wuer.js → Providers-BPQo_gQa.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DJxv7J64.js → QrScannerModal-CD-sEsWh.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DOXBReRk.js → QuickAsk-LOKRJiN_.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend--MiLwmLM.js → Recommend-BwPXqcWy.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-D6LoXH94.js → RemoteSession-BjotndxF.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-G2jToiqh.js → Reputation-BlZ2gcbU.js} +1 -1
- package/src/assets/web-panel/assets/{Row-B79ZV_lZ.js → Row-D399HM-d.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CHQN68w_.js → RssFeed-C7H-ibnY.js} +2 -2
- package/src/assets/web-panel/assets/{Search-j7v5S2Fa.js → Search-CNHCVmM1.js} +1 -1
- package/src/assets/web-panel/assets/{Security-B2ykFZJH.js → Security-C70eDGYY.js} +3 -3
- package/src/assets/web-panel/assets/{Services-H8BK8uX0.js → Services-6xfq9G-H.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-XEUUrOTK.js → Skeleton-BbY2zeZV.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-B387UJPk.js → Skills-DYtyZ7_6.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CWWBujpe.js → Sla-CkWcl5OH.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-Q2AOv9DV.js → SpeechSettings-CiknYk2P.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-OSNyOUr8.js → SyncSettings-D-8JVip4.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DfUDrno5.js → Tasks-B2KnR9XC.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CSTkzDRS.js → Templates-CipXkOE-.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BR__gCTG.js → Tenant-DbwgIMmx.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-Bv4DM5OI.js → Terminal-OtCriHEE.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DUnZuAtn.js → TimelineRenderer-B9LU5qrl.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Dm0sczRV.js → Tokens-B4iFVxvH.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DsSOp0Ob.js → Trigger-BegM2v8q.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-C0HJtFwV.js → Trust-COkqnnFj.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BbqIZiX-.js → UkeySign-BzqKPChj.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D7dDTt3D.js → VideoEditing-D-F2XL3C.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DJGu0KWq.js → Wallet-22PzRY9T.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-x8OoogZU.js → WebAuthn-eSMq_qtz.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CC3jIUqx.js → WorkflowEditor-BvdAakpq.js} +1 -1
- package/src/assets/web-panel/assets/{chat-Bqgv5oe8.js → chat-C6jhpF7E.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CbAMqLFr.js → colors-BbbW2Pfv.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-B-zIDFg3.js → compact-item-CVFeLTqt.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-D_s1ZLtZ.js → createContext-DS0m7NZc.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-D50gzPS9.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Cj_J9Eb9.js → hasIn-DVouP0t6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtJzqXCQ.js → index--AdzSlH7.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_igPAXH.js → index-54aHB4FL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BivQyKes.js → index-8QbwhE3i.js} +1 -1
- package/src/assets/web-panel/assets/{index-3o8DjHHt.js → index-B5JRIZXi.js} +1 -1
- package/src/assets/web-panel/assets/{index-0je3TLmx.js → index-B8a_c_qZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cz0blpuN.js → index-BE_u-rXr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cu3FtpNc.js → index-BOOsA080.js} +1 -1
- package/src/assets/web-panel/assets/{index-KY7udrs-.js → index-BXw6ivB8.js} +1 -1
- package/src/assets/web-panel/assets/{index-J4JLWUMK.js → index-BaUah6TO.js} +1 -1
- package/src/assets/web-panel/assets/{index-C6bAvixK.js → index-Be12HoCS.js} +3 -3
- package/src/assets/web-panel/assets/{index-Cy3XOp7S.js → index-BhvnNFfd.js} +1 -1
- package/src/assets/web-panel/assets/{index-C6lO24YD.js → index-BrIv7C9a.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPdH-XTd.js → index-BvCgzl0X.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cg0oYTy5.js → index-Bw-ZGQCH.js} +1 -1
- package/src/assets/web-panel/assets/{index-ab6FUgkq.js → index-BwZnPiFd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuqVIsHL.js → index-BwozzG6k.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6qoyCkx.js → index-ByLsH9CL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BjfpW5s3.js → index-BzL3LWFX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFNO6ycz.js → index-C4xmLgLa.js} +1 -1
- package/src/assets/web-panel/assets/{index-dg0JqQDA.js → index-C6q2MkIG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtplHZ5o.js → index-C74emdKD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bi1E5h9V.js → index-CIg4nqyp.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-Z13SCI.js → index-CIk7mg6G.js} +1 -1
- package/src/assets/web-panel/assets/{index-JdSkHz64.js → index-CPS8E-J6.js} +1 -1
- package/src/assets/web-panel/assets/index-CgensV-n.js +1 -0
- package/src/assets/web-panel/assets/{index-DVjFV2mg.js → index-CkdYB0_g.js} +1 -1
- package/src/assets/web-panel/assets/{index-CsHTqPTg.js → index-CpTbnbUI.js} +1 -1
- package/src/assets/web-panel/assets/index-Ctkf06Z_.js +1 -0
- package/src/assets/web-panel/assets/{index-nEWJobH4.js → index-CwYFDMn_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BN5sO3kL.js → index-DGYX17YW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CD_A-N7c.js → index-DR6TDz5H.js} +1 -1
- package/src/assets/web-panel/assets/{index-ggeC-_dR.js → index-DUBkae39.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9CqHOgd.js → index-DotRL126.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSzWODfj.js → index-DpReoAGb.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bw88Mk2t.js → index-Ds4wbrgk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGxI_Qxg.js → index-DuVjTDcW.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLwUCYDf.js → index-JeJqXbUU.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1t2IqNU.js → index-SdFkCtiC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVPI_iIh.js → index-eaXEQFuP.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DDIGXQ08.js → initDefaultProps-CB5cUnQ5.js} +1 -1
- package/src/assets/web-panel/assets/{motion-97SXSYE4.js → motion-bn_fWhQV.js} +1 -1
- package/src/assets/web-panel/assets/{move-CfDQMFRe.js → move-DY4-Odtu.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-qLYCGwY8.js → mtc-parser-B5l5mjoh.js} +1 -1
- package/src/assets/web-panel/assets/{omit-xVqy1mCI.js → omit-rJ-Ci8CW.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DCGlFyfv.js → pickAttrs-BMwGJd6C.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DYgPuMk3.js → placementArrow-CaiQyGXG.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-7zUIdAWH.js → responsiveObserve-DQTO5wDH.js} +1 -1
- package/src/assets/web-panel/assets/{slide-TyaiZlSh.js → slide-Dg6ZDtbz.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BPd13Fae.js → statusUtils-bvjrktKc.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-A44XGgQ0.js → styleChecker-C1geIU_x.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-ZX5EUa8B.js → useFlexGapSupport-BzU3RJRi.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-Sh_fBPqz.js → useFs-B7l6OwLe.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DhR6Izei.js → usePersonalDataHub-BYF1L_8x.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DC8g6oRh.js → vnode-6c2GVkT-.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DRjJmOwj.js → zoom-BYAY6QeD.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +21 -10
- package/src/data/changelog.json +39 -0
- package/src/gateways/ws/session-protocol.js +19 -5
- package/src/gateways/ws/ws-agent-handler.js +6 -0
- package/src/gateways/ws/ws-session-gateway.js +133 -21
- package/src/lib/agent-execution-policy.js +177 -0
- package/src/lib/authority-launch-environment.js +99 -0
- package/src/lib/auto-mode-config.js +129 -45
- package/src/lib/claude-project-auto-memory.js +7 -30
- package/src/lib/context-memory-kernel/task-worklog-port.js +73 -24
- package/src/lib/diagnostic-excerpt.js +91 -10
- package/src/lib/diagnostic-workflow.js +2 -0
- package/src/lib/evolution/volcengine-function-replay-store.js +38 -28
- package/src/lib/git-tool-input.js +7 -0
- package/src/lib/host-tool-policy-authority.js +151 -0
- package/src/lib/permission-authority.js +67 -5
- package/src/lib/plugin-runtime/settings.js +9 -1
- package/src/lib/remote-read-loop-guard.js +59 -14
- package/src/lib/sandbox-egress-authority-monitor.js +3 -2
- package/src/lib/session-manager.js +20 -5
- package/src/lib/settings-loader.cjs +175 -19
- package/src/lib/settings-source-observation.cjs +270 -0
- package/src/lib/sub-agent-context.js +36 -18
- package/src/lib/task-progress-tracker.js +112 -12
- package/src/lib/unattended-action-policy.js +456 -38
- package/src/repl/agent-repl.js +58 -29
- package/src/repl/repl-tool-executor.js +39 -0
- package/src/runtime/agent-core.js +272 -5
- package/src/runtime/agent-runtime.js +15 -3
- package/src/runtime/headless-runner.js +13 -4
- package/src/runtime/headless-stream.js +9 -0
- package/src/runtime/policies/agent-policy.js +5 -4
- package/src/assets/web-panel/assets/MarkdownRenderer-BpRWpN1E.js +0 -1
- package/src/assets/web-panel/assets/Projects-3Qo2Mvxb.js +0 -1
- package/src/assets/web-panel/assets/devWarning-QMSC9xgn.js +0 -1
- package/src/assets/web-panel/assets/index-DVq7UuW0.js +0 -1
- 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
|
-
|
|
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
|
-
|
|
267
|
-
|
|
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 (!
|
|
279
|
-
|
|
368
|
+
if (!["project", "local", "user"].includes(scope)) {
|
|
369
|
+
throw new TypeError("scope must be project | local | user");
|
|
280
370
|
}
|
|
281
|
-
|
|
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
|
-
|
|
286
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
379
|
-
|
|
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
|
-
|
|
667
|
-
|
|
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
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
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;
|