avorelo 0.1.0

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 (258) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/bin/avorelo +9 -0
  4. package/package.json +135 -0
  5. package/scripts/README.md +40 -0
  6. package/scripts/cco-dashboard.js +252 -0
  7. package/scripts/cco-status.js +430 -0
  8. package/scripts/lib/activation/account-state.js +37 -0
  9. package/scripts/lib/activation/activation-runner.js +546 -0
  10. package/scripts/lib/activation/activation-self-healing.js +480 -0
  11. package/scripts/lib/activation/activation-state.js +83 -0
  12. package/scripts/lib/activation/activation-summary.js +191 -0
  13. package/scripts/lib/activation/adapters/claude-code.js +77 -0
  14. package/scripts/lib/activation/adapters/codex-cli.js +52 -0
  15. package/scripts/lib/activation/adapters/cursor.js +37 -0
  16. package/scripts/lib/activation/adapters/github-agent.js +39 -0
  17. package/scripts/lib/activation/adapters/terminal.js +42 -0
  18. package/scripts/lib/activation/adapters/vscode.js +39 -0
  19. package/scripts/lib/activation/adapters/windsurf.js +37 -0
  20. package/scripts/lib/activation/ai-surface-detector.js +151 -0
  21. package/scripts/lib/activation/connect-account.js +145 -0
  22. package/scripts/lib/activation/detect-environment.js +75 -0
  23. package/scripts/lib/activation/detect-hosts.js +62 -0
  24. package/scripts/lib/activation/format-activation-output.js +109 -0
  25. package/scripts/lib/activation/next-action.js +43 -0
  26. package/scripts/lib/activation/repair-engine.js +219 -0
  27. package/scripts/lib/activation-distribution-readiness.js +507 -0
  28. package/scripts/lib/adapter-conformance.js +176 -0
  29. package/scripts/lib/adapter-readiness.js +417 -0
  30. package/scripts/lib/adapter-safety-boundaries.js +335 -0
  31. package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
  32. package/scripts/lib/agent-access-governance.js +455 -0
  33. package/scripts/lib/agent-enforcement.js +765 -0
  34. package/scripts/lib/agent-policy-profile.js +210 -0
  35. package/scripts/lib/agent-security/action-evaluator.js +507 -0
  36. package/scripts/lib/agent-security/adapter-registry.js +98 -0
  37. package/scripts/lib/agent-security/auto-policy.js +139 -0
  38. package/scripts/lib/agent-security/bounded-scan.js +93 -0
  39. package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
  40. package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
  41. package/scripts/lib/agent-security/file-write-adapter.js +183 -0
  42. package/scripts/lib/agent-security/file-write-rules.js +178 -0
  43. package/scripts/lib/agent-security/index.js +3342 -0
  44. package/scripts/lib/agent-security/instruction-risk.js +181 -0
  45. package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
  46. package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
  47. package/scripts/lib/agent-security/package-action-adapter.js +175 -0
  48. package/scripts/lib/agent-security/package-action-rules.js +233 -0
  49. package/scripts/lib/agent-security/performance.js +148 -0
  50. package/scripts/lib/agent-security/permission-minimizer.js +403 -0
  51. package/scripts/lib/agent-security/scan-cache.js +74 -0
  52. package/scripts/lib/agent-security/source-trust.js +146 -0
  53. package/scripts/lib/ai-install-prompt.js +288 -0
  54. package/scripts/lib/ai-workspace-hygiene.js +1499 -0
  55. package/scripts/lib/alpha-activation.js +520 -0
  56. package/scripts/lib/alpha-feedback.js +263 -0
  57. package/scripts/lib/alpha-readiness-gate.js +332 -0
  58. package/scripts/lib/anti-gaming.js +169 -0
  59. package/scripts/lib/artifact-health.js +431 -0
  60. package/scripts/lib/attribution.js +180 -0
  61. package/scripts/lib/audit.js +289 -0
  62. package/scripts/lib/avorelo-skill-registry.js +810 -0
  63. package/scripts/lib/batch-jobs.js +71 -0
  64. package/scripts/lib/brain-pack.js +578 -0
  65. package/scripts/lib/brand-boundary.js +424 -0
  66. package/scripts/lib/brand.js +74 -0
  67. package/scripts/lib/browser-capability.js +1048 -0
  68. package/scripts/lib/browser-proof-preflight.js +321 -0
  69. package/scripts/lib/cache-readiness.js +187 -0
  70. package/scripts/lib/canonical-reentry.js +162 -0
  71. package/scripts/lib/capability-packs.js +314 -0
  72. package/scripts/lib/capability-recommender.js +512 -0
  73. package/scripts/lib/capability-registry.js +1059 -0
  74. package/scripts/lib/carry-forward-surfacing.js +194 -0
  75. package/scripts/lib/ccusage-adapter.js +188 -0
  76. package/scripts/lib/company-loop.js +1149 -0
  77. package/scripts/lib/config.js +637 -0
  78. package/scripts/lib/context-acquisition-plan.js +287 -0
  79. package/scripts/lib/context-budget-guard.js +170 -0
  80. package/scripts/lib/context-budget-scanner.js +257 -0
  81. package/scripts/lib/context-optimizer.js +715 -0
  82. package/scripts/lib/context-reduction-plan.js +178 -0
  83. package/scripts/lib/context-safety.js +88 -0
  84. package/scripts/lib/context-savings-engine.js +158 -0
  85. package/scripts/lib/cost-evidence.js +254 -0
  86. package/scripts/lib/cross-host-install-plan.js +308 -0
  87. package/scripts/lib/cross-host-install-readiness.js +237 -0
  88. package/scripts/lib/cross-host-value-flow.js +268 -0
  89. package/scripts/lib/dashboard.js +900 -0
  90. package/scripts/lib/design-partner-feedback.js +346 -0
  91. package/scripts/lib/entitlements.js +100 -0
  92. package/scripts/lib/execution-packet.js +559 -0
  93. package/scripts/lib/experimentation-events.js +547 -0
  94. package/scripts/lib/external-capability-compliance.js +107 -0
  95. package/scripts/lib/external-user-simulation.js +166 -0
  96. package/scripts/lib/failure-recovery-readiness.js +81 -0
  97. package/scripts/lib/failure-recovery.js +419 -0
  98. package/scripts/lib/feedback-intelligence.js +537 -0
  99. package/scripts/lib/feedback-signals.js +205 -0
  100. package/scripts/lib/file-integrity.js +68 -0
  101. package/scripts/lib/fsx.js +127 -0
  102. package/scripts/lib/full-readiness-gate.js +451 -0
  103. package/scripts/lib/guidance-builder.js +174 -0
  104. package/scripts/lib/hook-apply.js +1019 -0
  105. package/scripts/lib/hook-baseline.js +310 -0
  106. package/scripts/lib/hook-config-preview.js +275 -0
  107. package/scripts/lib/hook-contracts.js +290 -0
  108. package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
  109. package/scripts/lib/host-capability-matrix.js +351 -0
  110. package/scripts/lib/host-support-context.js +254 -0
  111. package/scripts/lib/http-hook-action.js +538 -0
  112. package/scripts/lib/install-ai-readiness.js +84 -0
  113. package/scripts/lib/install-intake-risk.js +1037 -0
  114. package/scripts/lib/install-journey-intelligence.js +329 -0
  115. package/scripts/lib/intervention-guidance.js +57 -0
  116. package/scripts/lib/known-limitations.js +115 -0
  117. package/scripts/lib/l8-path-truth.js +146 -0
  118. package/scripts/lib/launch-hardening-gate.js +436 -0
  119. package/scripts/lib/launch-readiness.js +628 -0
  120. package/scripts/lib/learning-memory.js +686 -0
  121. package/scripts/lib/lifecycle-hooks.js +802 -0
  122. package/scripts/lib/local-package-smoke.js +423 -0
  123. package/scripts/lib/local-pricing.js +299 -0
  124. package/scripts/lib/mcp-enforcement.js +311 -0
  125. package/scripts/lib/mcp-least-privilege-policy.js +303 -0
  126. package/scripts/lib/mcp-tool-inventory.js +388 -0
  127. package/scripts/lib/mcp-tool-risk.js +0 -0
  128. package/scripts/lib/memory.js +335 -0
  129. package/scripts/lib/metrics.js +699 -0
  130. package/scripts/lib/micro-proof.js +133 -0
  131. package/scripts/lib/next-run-context.js +436 -0
  132. package/scripts/lib/operating-value.js +1648 -0
  133. package/scripts/lib/optimization-v3.js +122 -0
  134. package/scripts/lib/orchestration/adapters/_shared.js +49 -0
  135. package/scripts/lib/orchestration/adapters/aider.js +18 -0
  136. package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
  137. package/scripts/lib/orchestration/adapters/codex.js +35 -0
  138. package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
  139. package/scripts/lib/orchestration/adapters/git.js +25 -0
  140. package/scripts/lib/orchestration/adapters/index.js +31 -0
  141. package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
  142. package/scripts/lib/orchestration/adapters/ollama.js +18 -0
  143. package/scripts/lib/orchestration/adapters/opencode.js +18 -0
  144. package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
  145. package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
  146. package/scripts/lib/orchestration/cli.js +438 -0
  147. package/scripts/lib/orchestration/execution-manager.js +279 -0
  148. package/scripts/lib/orchestration/handoff.js +314 -0
  149. package/scripts/lib/orchestration/index.js +456 -0
  150. package/scripts/lib/orchestration/inventory.js +47 -0
  151. package/scripts/lib/orchestration/model-discovery.js +498 -0
  152. package/scripts/lib/orchestration/model-profiler.js +170 -0
  153. package/scripts/lib/orchestration/model-profiles.js +252 -0
  154. package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
  155. package/scripts/lib/orchestration/proof-writer.js +349 -0
  156. package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
  157. package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
  158. package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
  159. package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
  160. package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
  161. package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
  162. package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
  163. package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
  164. package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
  165. package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
  166. package/scripts/lib/orchestration/risk-classifier.js +130 -0
  167. package/scripts/lib/orchestration/routing-policy.js +486 -0
  168. package/scripts/lib/orchestration/settings.js +112 -0
  169. package/scripts/lib/orchestration/state.js +165 -0
  170. package/scripts/lib/orchestration/verification-manager.js +138 -0
  171. package/scripts/lib/output-profiles.js +146 -0
  172. package/scripts/lib/package-content-audit.js +368 -0
  173. package/scripts/lib/package-runtime.js +278 -0
  174. package/scripts/lib/plan-surface.js +53 -0
  175. package/scripts/lib/plans.js +2318 -0
  176. package/scripts/lib/policy-provider.js +27 -0
  177. package/scripts/lib/prelaunch-activation-readiness.js +409 -0
  178. package/scripts/lib/prelaunch-evidence-store.js +816 -0
  179. package/scripts/lib/prelaunch-intelligence.js +869 -0
  180. package/scripts/lib/pricing-experiment.js +118 -0
  181. package/scripts/lib/pro-moment-events.js +77 -0
  182. package/scripts/lib/pro-moment-state.js +227 -0
  183. package/scripts/lib/pro-moments.js +1216 -0
  184. package/scripts/lib/product-learning-events.js +629 -0
  185. package/scripts/lib/project-profile.js +555 -0
  186. package/scripts/lib/prompt-compiler.js +280 -0
  187. package/scripts/lib/prompt-lint.js +32 -0
  188. package/scripts/lib/prompt-suggestions.js +52 -0
  189. package/scripts/lib/proof-canonical.js +398 -0
  190. package/scripts/lib/proof-drilldown.js +383 -0
  191. package/scripts/lib/proof-events.js +342 -0
  192. package/scripts/lib/proof-history.js +243 -0
  193. package/scripts/lib/proof-metrics.js +296 -0
  194. package/scripts/lib/proof-outcome-evidence.js +134 -0
  195. package/scripts/lib/proof-receipt.js +335 -0
  196. package/scripts/lib/proof-record.js +461 -0
  197. package/scripts/lib/public-activation-distribution-gate.js +258 -0
  198. package/scripts/lib/public-cli.js +3891 -0
  199. package/scripts/lib/public-distribution-truth.js +211 -0
  200. package/scripts/lib/public-install-claim-checker.js +294 -0
  201. package/scripts/lib/publish-provenance-readiness.js +283 -0
  202. package/scripts/lib/readiness-delta.js +218 -0
  203. package/scripts/lib/readiness-evidence-closure.js +196 -0
  204. package/scripts/lib/reentry-memory-capture.js +241 -0
  205. package/scripts/lib/reentry-memory-retrieval.js +302 -0
  206. package/scripts/lib/reentry-memory-status.js +146 -0
  207. package/scripts/lib/reentry-memory-store.js +178 -0
  208. package/scripts/lib/reentry-state.js +66 -0
  209. package/scripts/lib/release-candidate-bundle.js +166 -0
  210. package/scripts/lib/remediation.js +81 -0
  211. package/scripts/lib/repo-map.js +391 -0
  212. package/scripts/lib/run-improvements-lifecycle.js +330 -0
  213. package/scripts/lib/run-improvements.js +789 -0
  214. package/scripts/lib/runtime-decision-policy.js +387 -0
  215. package/scripts/lib/safe-path-engine.js +705 -0
  216. package/scripts/lib/safe-run-controller.js +887 -0
  217. package/scripts/lib/score.js +262 -0
  218. package/scripts/lib/seamless-enforcement.js +329 -0
  219. package/scripts/lib/seamless-outcome.js +689 -0
  220. package/scripts/lib/seamless-reality-gate.js +5043 -0
  221. package/scripts/lib/security-risk-classifier.js +511 -0
  222. package/scripts/lib/security-scan.js +384 -0
  223. package/scripts/lib/session-context-optimizer.js +1211 -0
  224. package/scripts/lib/session-timing.js +315 -0
  225. package/scripts/lib/skill-hygiene.js +805 -0
  226. package/scripts/lib/skill-packs.js +161 -0
  227. package/scripts/lib/skills-operating-layer.js +580 -0
  228. package/scripts/lib/smart-work-routing.js +768 -0
  229. package/scripts/lib/source-catalog.js +700 -0
  230. package/scripts/lib/status-value-summary.js +32 -0
  231. package/scripts/lib/support-bundle.js +578 -0
  232. package/scripts/lib/task-continuation.js +440 -0
  233. package/scripts/lib/test-helpers.js +15 -0
  234. package/scripts/lib/tier.js +38 -0
  235. package/scripts/lib/token-context-quality-gate.js +370 -0
  236. package/scripts/lib/token-cost-capture.js +187 -0
  237. package/scripts/lib/token-cost-intelligence.js +358 -0
  238. package/scripts/lib/token-efficiency-evidence.js +213 -0
  239. package/scripts/lib/token-evidence.js +699 -0
  240. package/scripts/lib/tokenish.js +17 -0
  241. package/scripts/lib/tool-output-sandbox.js +304 -0
  242. package/scripts/lib/trust-audit.js +136 -0
  243. package/scripts/lib/unified-events.js +396 -0
  244. package/scripts/lib/upgrade-interruption-recovery.js +407 -0
  245. package/scripts/lib/usage-ledger.js +201 -0
  246. package/scripts/lib/value-ledger.js +130 -0
  247. package/scripts/lib/value-proof-calibration.js +531 -0
  248. package/scripts/lib/visual-qa.js +231 -0
  249. package/scripts/lib/voice-alpha.js +29 -0
  250. package/scripts/lib/work-aware-orchestration.js +976 -0
  251. package/scripts/lib/work-control-receipts.js +577 -0
  252. package/scripts/lib/work-ledger.js +1123 -0
  253. package/scripts/lib/work-panel-preview.js +352 -0
  254. package/scripts/lib/workflow-discipline.js +280 -0
  255. package/scripts/lib/workflow-signals.js +419 -0
  256. package/scripts/lib/workspace-map.js +281 -0
  257. package/scripts/lib/workspace-registry.js +1367 -0
  258. package/scripts/lib/workspace-resolver.js +480 -0
@@ -0,0 +1,1129 @@
1
+ "use strict";
2
+
3
+ const crypto = require("crypto");
4
+ const { safeReadJson, safeWriteJson } = require("../fsx");
5
+ const {
6
+ ENFORCEMENT_CAPABILITIES_REL_PATH,
7
+ resolveAgentSecurityAdapter,
8
+ writeEnforcementCapabilityMatrix,
9
+ } = require("./adapter-registry");
10
+ const { normalizeAgentSecurityAction } = require("./action-evaluator");
11
+ const {
12
+ contentHashFromAction,
13
+ normalizeTargetPath,
14
+ targetHashFromAction,
15
+ } = require("./file-write-rules");
16
+ const { argsHashFromAction, targetHashFromMcpAction } = require("./mcp-action-rules");
17
+
18
+ const ENFORCEMENT_SUMMARY_REL_PATH = ".claude/cco/security/agent-enforcement-summary.json";
19
+ const JIT_GRANTS_REL_PATH = ".claude/cco/security/agent-jit-grants.json";
20
+ const ONE_TIME_GRANTS_REL_PATH = ".claude/cco/security/agent-one-time-grants.json";
21
+ const ENFORCEMENT_AUDIT_LOG_REL_PATH = ".claude/cco/audit/agent-security-enforcement.jsonl";
22
+ const DEFAULT_GRANT_TTL_SECONDS = 1800;
23
+
24
+ function sha256(value) {
25
+ return crypto.createHash("sha256").update(String(value || "")).digest("hex");
26
+ }
27
+
28
+ function shortHash(value) {
29
+ return sha256(value).slice(0, 12);
30
+ }
31
+
32
+ function appendJsonl(cwd, relPath, entry) {
33
+ const fs = require("fs");
34
+ const path = require("path");
35
+ const absPath = path.join(cwd, relPath);
36
+ fs.mkdirSync(path.dirname(absPath), { recursive: true });
37
+ fs.appendFileSync(absPath, `${JSON.stringify(entry)}\n`, "utf8");
38
+ }
39
+
40
+ function toIso(dateLike) {
41
+ return new Date(dateLike).toISOString();
42
+ }
43
+
44
+ function addSeconds(timestamp, seconds) {
45
+ return toIso(Date.parse(timestamp) + (Number(seconds) * 1000));
46
+ }
47
+
48
+ function commandHash(command) {
49
+ return sha256(String(command || ""));
50
+ }
51
+
52
+ function ttlSecondsFromRecommendation(value) {
53
+ const normalized = String(value || "").toLowerCase();
54
+ if (normalized.includes("30 minute")) return 1800;
55
+ if (normalized.includes("15 minute")) return 900;
56
+ if (normalized.includes("this session")) return 14400;
57
+ if (normalized.includes("once")) return DEFAULT_GRANT_TTL_SECONDS;
58
+ return DEFAULT_GRANT_TTL_SECONDS;
59
+ }
60
+
61
+ function normalizeGrant(grant) {
62
+ if (!grant || typeof grant !== "object") return null;
63
+ const ttlSeconds = Number(grant.ttlSeconds);
64
+ return {
65
+ grantId: String(grant.grantId || `grant-${shortHash(JSON.stringify(grant))}`),
66
+ grantType: "jit_permission",
67
+ decision: String(grant.decision || "approve_once"),
68
+ actionId: grant.actionId || null,
69
+ componentId: grant.componentId || null,
70
+ componentType: grant.componentType || null,
71
+ actionType: grant.actionType || null,
72
+ adapterId: grant.adapterId || null,
73
+ commandHash: grant.commandHash || null,
74
+ targetHash: grant.targetHash || null,
75
+ contentHash: grant.contentHash || null,
76
+ argsHash: grant.argsHash || null,
77
+ scope: {
78
+ cwd: String(grant.scope?.cwd || ""),
79
+ paths: Array.isArray(grant.scope?.paths) ? grant.scope.paths : [],
80
+ repo: String(grant.scope?.repo || "current"),
81
+ branchPrefix: String(grant.scope?.branchPrefix || "wuz/"),
82
+ allowedCommands: Array.isArray(grant.scope?.allowedCommands) ? grant.scope.allowedCommands : [],
83
+ allowedTargets: Array.isArray(grant.scope?.allowedTargets) ? grant.scope.allowedTargets : [],
84
+ packageManager: grant.scope?.packageManager || null,
85
+ scriptName: grant.scope?.scriptName || null,
86
+ packageName: grant.scope?.packageName || null,
87
+ mcpServer: grant.scope?.mcpServer || null,
88
+ toolName: grant.scope?.toolName || null,
89
+ },
90
+ allowedActions: Array.isArray(grant.allowedActions) ? grant.allowedActions : [],
91
+ deniedActions: Array.isArray(grant.deniedActions) ? grant.deniedActions : [],
92
+ ttlSeconds: Number.isFinite(ttlSeconds) && ttlSeconds > 0 ? ttlSeconds : DEFAULT_GRANT_TTL_SECONDS,
93
+ createdAt: grant.createdAt || null,
94
+ expiresAt: grant.expiresAt || null,
95
+ singleUse: grant.singleUse !== false,
96
+ consumedAt: grant.consumedAt || null,
97
+ reason: String(grant.reason || ""),
98
+ enforcementAvailable: grant.enforcementAvailable === true,
99
+ };
100
+ }
101
+
102
+ function loadJitGrantStore(cwd) {
103
+ const preferred = safeReadJson(cwd, JIT_GRANTS_REL_PATH, null);
104
+ const legacy = preferred ? null : safeReadJson(cwd, ONE_TIME_GRANTS_REL_PATH, null);
105
+ const source = preferred || legacy;
106
+ if (!source || typeof source !== "object") {
107
+ return {
108
+ grantsVersion: 2,
109
+ what: "Agent Security JIT Permission Grants.",
110
+ generatedAt: null,
111
+ grants: [],
112
+ };
113
+ }
114
+
115
+ return {
116
+ grantsVersion: 2,
117
+ what: "Agent Security JIT Permission Grants.",
118
+ generatedAt: source.generatedAt || null,
119
+ grants: (Array.isArray(source.grants) ? source.grants : [])
120
+ .map((grant) => normalizeGrant(grant))
121
+ .filter(Boolean),
122
+ };
123
+ }
124
+
125
+ function saveJitGrantStore(cwd, store) {
126
+ const normalized = {
127
+ grantsVersion: 2,
128
+ what: "Agent Security JIT Permission Grants.",
129
+ generatedAt: store.generatedAt || null,
130
+ grants: (Array.isArray(store.grants) ? store.grants : []).map((grant) => normalizeGrant(grant)).filter(Boolean),
131
+ };
132
+ safeWriteJson(cwd, JIT_GRANTS_REL_PATH, normalized);
133
+ safeWriteJson(cwd, ONE_TIME_GRANTS_REL_PATH, normalized);
134
+ return normalized;
135
+ }
136
+
137
+ function isGrantExpired(grant, timestamp) {
138
+ const expiresAt = Date.parse(grant?.expiresAt || "");
139
+ const comparedAt = Date.parse(timestamp || "");
140
+ if (!Number.isFinite(expiresAt) || !Number.isFinite(comparedAt)) return true;
141
+ return expiresAt <= comparedAt;
142
+ }
143
+
144
+ function countActiveJitGrants(store, timestamp) {
145
+ return (store?.grants || []).filter((grant) => grant && !grant.consumedAt && !isGrantExpired(grant, timestamp)).length;
146
+ }
147
+
148
+ function matchesGrantAction(grant, action, cwd) {
149
+ if (!grant || !action) return false;
150
+ if (grant.actionType !== action.actionType) return false;
151
+ if ((grant.componentId || null) !== (action.componentId || null)) return false;
152
+ if (String(grant.scope?.cwd || "") !== String(cwd || "")) return false;
153
+ if (grant.actionType === "filesystem.write") {
154
+ const targetHash = targetHashFromAction(action);
155
+ const contentHash = contentHashFromAction(action);
156
+ if (!grant.targetHash || !targetHash || grant.targetHash !== targetHash) return false;
157
+ if (grant.contentHash && contentHash !== grant.contentHash) return false;
158
+ return true;
159
+ }
160
+ if (grant.actionType === "package.install" || grant.actionType === "package.run" || grant.actionType === "package.script") {
161
+ if (!grant.commandHash || grant.commandHash !== commandHash(action.command || "")) return false;
162
+ if (grant.scope?.packageManager && grant.scope.packageManager !== (action.packageManager || null)) return false;
163
+ if (grant.scope?.scriptName && grant.scope.scriptName !== (action.scriptName || null)) return false;
164
+ if (grant.scope?.packageName && grant.scope.packageName !== (action.packageName || null)) return false;
165
+ return true;
166
+ }
167
+ if (
168
+ grant.actionType === "mcp.read" ||
169
+ grant.actionType === "mcp.write" ||
170
+ grant.actionType === "mcp.delete" ||
171
+ grant.actionType === "mcp.admin" ||
172
+ grant.actionType === "mcp.externalMutation" ||
173
+ grant.actionType === "mcp.unknown"
174
+ ) {
175
+ if (grant.scope?.mcpServer && grant.scope.mcpServer !== (action.mcpServer || null)) return false;
176
+ if (grant.scope?.toolName && grant.scope.toolName !== (action.toolName || null)) return false;
177
+ if (grant.targetHash && grant.targetHash !== targetHashFromMcpAction(action)) return false;
178
+ if (grant.argsHash && grant.argsHash !== argsHashFromAction(action)) return false;
179
+ return true;
180
+ }
181
+ if (grant.actionId && action.actionId && grant.actionId === action.actionId) return true;
182
+ if (grant.commandHash && grant.commandHash === commandHash(action.command || "")) return true;
183
+ return false;
184
+ }
185
+
186
+ function buildGrantScope(action, limitPlan, cwd) {
187
+ const scope = limitPlan?.scope || {};
188
+ const normalizedTarget = normalizeTargetPath(action?.target || "");
189
+ return {
190
+ cwd: String(cwd || ""),
191
+ paths: Array.isArray(scope.allowedPaths)
192
+ ? scope.allowedPaths.slice(0, 10)
193
+ : normalizedTarget
194
+ ? [normalizedTarget]
195
+ : [],
196
+ repo: "current",
197
+ branchPrefix: "wuz/",
198
+ allowedCommands: Array.isArray(scope.allowedCommands) ? scope.allowedCommands.slice(0, 5) : [],
199
+ allowedTargets: Array.isArray(scope.allowedTargets) ? scope.allowedTargets.slice(0, 10) : [],
200
+ packageManager: scope.packageManager || action?.packageManager || null,
201
+ scriptName: scope.scriptName || action?.scriptName || null,
202
+ packageName: scope.packageName || action?.packageName || null,
203
+ mcpServer: scope.mcpServer || action?.mcpServer || null,
204
+ toolName: scope.toolName || action?.toolName || null,
205
+ };
206
+ }
207
+
208
+ function buildJitGrant(action, decisionRecord, limitPlan, enforcementContext = {}) {
209
+ const decision = String(decisionRecord?.decision || "");
210
+ const effectiveBehavior = String(enforcementContext.effectiveBehavior || "");
211
+ const enforcementAvailable = enforcementContext.enforcementAvailable === true;
212
+ const timestamp = enforcementContext.timestamp;
213
+
214
+ if (!decision || decision === "deny") {
215
+ return {
216
+ grantCreated: false,
217
+ grant: null,
218
+ effectiveBehavior: enforcementAvailable ? "blocked" : "recommended_only",
219
+ reason: decision === "deny"
220
+ ? (enforcementAvailable ? "User denied this action." : "User denial was recorded, but no enforcement adapter is available.")
221
+ : "No JIT grant was requested.",
222
+ };
223
+ }
224
+
225
+ if (!enforcementAvailable) {
226
+ return {
227
+ grantCreated: false,
228
+ grant: null,
229
+ effectiveBehavior: decision === "let_wuz_limit" ? "recommended_only" : "recommended_only",
230
+ reason: "No enforcement adapter is available for this action type.",
231
+ };
232
+ }
233
+
234
+ if (effectiveBehavior === "blocked" || effectiveBehavior === "recommended_only") {
235
+ return {
236
+ grantCreated: false,
237
+ grant: null,
238
+ effectiveBehavior,
239
+ reason: enforcementContext.reason || "This action was not allowed, so no JIT grant was created.",
240
+ };
241
+ }
242
+
243
+ if (decision === "let_wuz_limit" && limitPlan?.recommendedMode !== "limited") {
244
+ return {
245
+ grantCreated: false,
246
+ grant: null,
247
+ effectiveBehavior: limitPlan?.recommendedMode === "deny" ? "blocked" : "limit_plan_only",
248
+ reason: limitPlan?.reason || "No safe limited version found. Recommended: deny.",
249
+ };
250
+ }
251
+
252
+ const ttlSeconds = decision === "let_wuz_limit"
253
+ ? ttlSecondsFromRecommendation(limitPlan?.ttlRecommendation || "30 minutes")
254
+ : Number.isFinite(Number(decisionRecord?.ttlSeconds)) && Number(decisionRecord?.ttlSeconds) > 0
255
+ ? Number(decisionRecord.ttlSeconds)
256
+ : DEFAULT_GRANT_TTL_SECONDS;
257
+
258
+ const grant = normalizeGrant({
259
+ grantId: `grant-${shortHash(`${timestamp}:${action?.actionId}:${decision}:${action?.command || action?.target || ""}`)}`,
260
+ grantType: "jit_permission",
261
+ decision,
262
+ actionId: action?.actionId || null,
263
+ componentId: action?.componentId || null,
264
+ componentType: action?.componentType || null,
265
+ actionType: action?.actionType || null,
266
+ adapterId: enforcementContext.adapterId || null,
267
+ commandHash: action?.command ? commandHash(action.command) : null,
268
+ targetHash: action?.actionType === "filesystem.write"
269
+ ? targetHashFromAction(action)
270
+ : (String(action?.actionType || "").startsWith("mcp.") ? targetHashFromMcpAction(action) : null),
271
+ contentHash: action?.actionType === "filesystem.write" ? contentHashFromAction(action) : null,
272
+ argsHash: String(action?.actionType || "").startsWith("mcp.") ? argsHashFromAction(action) : null,
273
+ scope: buildGrantScope(action, limitPlan, enforcementContext.cwd),
274
+ allowedActions: decision === "let_wuz_limit"
275
+ ? (Array.isArray(limitPlan?.wouldAllow) ? limitPlan.wouldAllow.slice(0, 10) : [])
276
+ : [
277
+ action?.actionType === "filesystem.write"
278
+ ? "write exact file once"
279
+ : action?.actionType === "package.install" || action?.actionType === "package.run" || action?.actionType === "package.script"
280
+ ? "run exact package command once"
281
+ : String(action?.actionType || "").startsWith("mcp.")
282
+ ? "run exact MCP action once"
283
+ : (action?.actionType || "requested-action"),
284
+ ],
285
+ deniedActions: decision === "let_wuz_limit"
286
+ ? (Array.isArray(limitPlan?.wouldDeny) ? limitPlan.wouldDeny.slice(0, 10) : [])
287
+ : (
288
+ action?.actionType === "filesystem.write"
289
+ ? ["sensitive file access", "workflow modification", "path traversal", "outside project root"]
290
+ : action?.actionType === "package.install" || action?.actionType === "package.run" || action?.actionType === "package.script"
291
+ ? ["remote script execution", "global install", "lifecycle script execution", "sensitive file access", "outside project root"]
292
+ : String(action?.actionType || "").startsWith("mcp.")
293
+ ? ["delete actions", "push to main", "workflow modification", "secrets modification", "admin actions"]
294
+ : []
295
+ ),
296
+ ttlSeconds,
297
+ createdAt: timestamp,
298
+ expiresAt: addSeconds(timestamp, ttlSeconds),
299
+ singleUse: true,
300
+ consumedAt: null,
301
+ reason: decision === "let_wuz_limit"
302
+ ? (limitPlan?.reason || "Wuz limited this action to the current task.")
303
+ : "Approved once for this exact action.",
304
+ enforcementAvailable: true,
305
+ });
306
+
307
+ return {
308
+ grantCreated: true,
309
+ grant,
310
+ effectiveBehavior,
311
+ reason: grant.reason,
312
+ };
313
+ }
314
+
315
+ function recordGrant(cwd, grant, timestamp) {
316
+ const store = loadJitGrantStore(cwd);
317
+ store.generatedAt = timestamp;
318
+ store.grants = [normalizeGrant(grant), ...(store.grants || [])].slice(0, 100);
319
+ saveJitGrantStore(cwd, store);
320
+ return normalizeGrant(grant);
321
+ }
322
+
323
+ function consumeGrant(cwd, grantId, timestamp) {
324
+ const store = loadJitGrantStore(cwd);
325
+ let consumedGrant = null;
326
+ store.grants = (store.grants || []).map((grant) => {
327
+ if (grant?.grantId !== grantId) return grant;
328
+ consumedGrant = normalizeGrant({
329
+ ...grant,
330
+ consumedAt: timestamp,
331
+ });
332
+ return consumedGrant;
333
+ });
334
+ if (consumedGrant) {
335
+ store.generatedAt = timestamp;
336
+ saveJitGrantStore(cwd, store);
337
+ }
338
+ return consumedGrant;
339
+ }
340
+
341
+ function loadEnforcementSummary(cwd) {
342
+ return safeReadJson(cwd, ENFORCEMENT_SUMMARY_REL_PATH, null);
343
+ }
344
+
345
+ function saveEnforcementSummary(cwd, summary) {
346
+ safeWriteJson(cwd, ENFORCEMENT_SUMMARY_REL_PATH, summary);
347
+ return summary;
348
+ }
349
+
350
+ function baseEnforcementCounts(previousCounts, payload) {
351
+ return {
352
+ attempted: Number(previousCounts?.attempted || 0) + 1,
353
+ blocked: Number(previousCounts?.blocked || 0),
354
+ approvedOnce: Number(previousCounts?.approvedOnce || 0),
355
+ limited: Number(previousCounts?.limited || 0),
356
+ unavailable: Number(previousCounts?.unavailable || 0),
357
+ fileWritesBlocked: Number(previousCounts?.fileWritesBlocked || 0),
358
+ fileWritesLimited: Number(previousCounts?.fileWritesLimited || 0),
359
+ fileWritesApprovedOnce: Number(previousCounts?.fileWritesApprovedOnce || 0),
360
+ packageActionsBlocked: Number(previousCounts?.packageActionsBlocked || 0),
361
+ packageActionsLimited: Number(previousCounts?.packageActionsLimited || 0),
362
+ packageActionsApprovedOnce: Number(previousCounts?.packageActionsApprovedOnce || 0),
363
+ mcpActionsBlocked: Number(previousCounts?.mcpActionsBlocked || 0),
364
+ mcpActionsLimited: Number(previousCounts?.mcpActionsLimited || 0),
365
+ mcpActionsApprovedOnce: Number(previousCounts?.mcpActionsApprovedOnce || 0),
366
+ jitGrantsCreated: Number(previousCounts?.jitGrantsCreated || 0),
367
+ jitGrantsConsumed: Number(previousCounts?.jitGrantsConsumed || 0),
368
+ jitGrantsExpired: Number(previousCounts?.jitGrantsExpired || 0),
369
+ activeJitGrants: Number.isFinite(Number(payload.activeJitGrants))
370
+ ? Number(payload.activeJitGrants)
371
+ : Number(previousCounts?.activeJitGrants || 0),
372
+ autoLimited: Number(previousCounts?.autoLimited || 0),
373
+ autoDenied: Number(previousCounts?.autoDenied || 0),
374
+ autoRequiresApproval: Number(previousCounts?.autoRequiresApproval || 0),
375
+ autoRecommendedOnly: Number(previousCounts?.autoRecommendedOnly || 0),
376
+ autoLimitPlanOnly: Number(previousCounts?.autoLimitPlanOnly || 0),
377
+ };
378
+ }
379
+
380
+ function updateEnforcementSummary(cwd, payload) {
381
+ const mode = String(payload.mode || "warn");
382
+ const previous = loadEnforcementSummary(cwd) || {
383
+ enforcementSummaryVersion: 2,
384
+ what: "Agent Security enforcement summary.",
385
+ generatedAt: null,
386
+ mode,
387
+ counts: {
388
+ attempted: 0,
389
+ blocked: 0,
390
+ approvedOnce: 0,
391
+ limited: 0,
392
+ unavailable: 0,
393
+ fileWritesBlocked: 0,
394
+ fileWritesLimited: 0,
395
+ fileWritesApprovedOnce: 0,
396
+ packageActionsBlocked: 0,
397
+ packageActionsLimited: 0,
398
+ packageActionsApprovedOnce: 0,
399
+ mcpActionsBlocked: 0,
400
+ mcpActionsLimited: 0,
401
+ mcpActionsApprovedOnce: 0,
402
+ jitGrantsCreated: 0,
403
+ jitGrantsConsumed: 0,
404
+ jitGrantsExpired: 0,
405
+ activeJitGrants: 0,
406
+ autoLimited: 0,
407
+ autoDenied: 0,
408
+ autoRequiresApproval: 0,
409
+ autoRecommendedOnly: 0,
410
+ autoLimitPlanOnly: 0,
411
+ },
412
+ latestEnforcement: null,
413
+ recentEnforcements: [],
414
+ };
415
+
416
+ const counts = baseEnforcementCounts(previous.counts, payload);
417
+
418
+ if (payload.effectiveBehavior === "blocked") counts.blocked += 1;
419
+ if (payload.effectiveBehavior === "limited") counts.limited += 1;
420
+ if (payload.decision === "approve_once" && (payload.effectiveBehavior === "allowed" || payload.effectiveBehavior === "dry_run_only")) {
421
+ counts.approvedOnce += 1;
422
+ }
423
+ if (payload.actionType === "filesystem.write" && payload.effectiveBehavior === "blocked") counts.fileWritesBlocked += 1;
424
+ if (payload.actionType === "filesystem.write" && payload.effectiveBehavior === "limited") counts.fileWritesLimited += 1;
425
+ if (
426
+ payload.actionType === "filesystem.write" &&
427
+ payload.decision === "approve_once" &&
428
+ (payload.effectiveBehavior === "allowed" || payload.effectiveBehavior === "dry_run_only")
429
+ ) {
430
+ counts.fileWritesApprovedOnce += 1;
431
+ }
432
+ if (
433
+ (payload.actionType === "package.install" || payload.actionType === "package.run" || payload.actionType === "package.script") &&
434
+ payload.effectiveBehavior === "blocked"
435
+ ) {
436
+ counts.packageActionsBlocked += 1;
437
+ }
438
+ if (
439
+ (payload.actionType === "package.install" || payload.actionType === "package.run" || payload.actionType === "package.script") &&
440
+ payload.effectiveBehavior === "limited"
441
+ ) {
442
+ counts.packageActionsLimited += 1;
443
+ }
444
+ if (
445
+ (payload.actionType === "package.install" || payload.actionType === "package.run" || payload.actionType === "package.script") &&
446
+ payload.decision === "approve_once" &&
447
+ (payload.effectiveBehavior === "allowed" || payload.effectiveBehavior === "dry_run_only")
448
+ ) {
449
+ counts.packageActionsApprovedOnce += 1;
450
+ }
451
+ if (String(payload.actionType || "").startsWith("mcp.") && payload.effectiveBehavior === "blocked") counts.mcpActionsBlocked += 1;
452
+ if (String(payload.actionType || "").startsWith("mcp.") && payload.effectiveBehavior === "limited") counts.mcpActionsLimited += 1;
453
+ if (
454
+ String(payload.actionType || "").startsWith("mcp.") &&
455
+ payload.decision === "approve_once" &&
456
+ (payload.effectiveBehavior === "allowed" || payload.effectiveBehavior === "dry_run_only")
457
+ ) {
458
+ counts.mcpActionsApprovedOnce += 1;
459
+ }
460
+ if (payload.enforcementAvailable === false || payload.effectiveBehavior === "recommended_only") {
461
+ counts.unavailable += 1;
462
+ }
463
+ if (mode === "auto") {
464
+ if (payload.autoDecision === "auto_allow_limited") counts.autoLimited += 1;
465
+ if (payload.autoDecision === "auto_deny") counts.autoDenied += 1;
466
+ if (payload.autoDecision === "require_approval") counts.autoRequiresApproval += 1;
467
+ if (payload.autoDecision === "recommended_only") counts.autoRecommendedOnly += 1;
468
+ if (payload.autoDecision === "limit_plan_only") counts.autoLimitPlanOnly += 1;
469
+ }
470
+ if (payload.jitGrantCreated) counts.jitGrantsCreated += 1;
471
+ if (payload.jitGrantConsumed) counts.jitGrantsConsumed += 1;
472
+ if (payload.jitGrantExpired) counts.jitGrantsExpired += 1;
473
+
474
+ const latestEnforcement = {
475
+ actionId: payload.actionId,
476
+ componentId: payload.componentId || null,
477
+ componentType: payload.componentType || null,
478
+ actionType: payload.actionType,
479
+ target: payload.target || null,
480
+ commandHash: payload.command ? commandHash(payload.command) : null,
481
+ packageManager: payload.packageManager || null,
482
+ scriptName: payload.scriptName || null,
483
+ packageName: payload.packageName || null,
484
+ mcpServer: payload.mcpServer || null,
485
+ toolName: payload.toolName || null,
486
+ targetHash: String(payload.actionType || "").startsWith("mcp.") ? targetHashFromMcpAction(payload) : null,
487
+ argsHash: String(payload.actionType || "").startsWith("mcp.") ? argsHashFromAction(payload) : null,
488
+ decision: payload.decision || null,
489
+ autoDecision: payload.autoDecision || null,
490
+ adapterId: payload.adapterId || null,
491
+ enforcementAvailable: payload.enforcementAvailable === true,
492
+ effectiveBehavior: payload.effectiveBehavior,
493
+ executed: payload.executed === true,
494
+ reason: payload.reason || "",
495
+ timestamp: payload.timestamp,
496
+ };
497
+
498
+ const recentEnforcements = [latestEnforcement, ...(Array.isArray(previous.recentEnforcements) ? previous.recentEnforcements : [])]
499
+ .slice(0, 5);
500
+
501
+ return saveEnforcementSummary(cwd, {
502
+ enforcementSummaryVersion: 2,
503
+ what: "Agent Security enforcement summary.",
504
+ generatedAt: payload.timestamp,
505
+ mode,
506
+ counts,
507
+ latestEnforcement,
508
+ recentEnforcements,
509
+ });
510
+ }
511
+
512
+ function writeGrantAuditEvent(cwd, eventType, payload, grant) {
513
+ const mode = String(payload.mode || "warn");
514
+ appendJsonl(cwd, ENFORCEMENT_AUDIT_LOG_REL_PATH, {
515
+ eventType,
516
+ mode,
517
+ grantId: grant?.grantId || null,
518
+ actionId: payload.actionId,
519
+ componentId: payload.componentId || null,
520
+ componentType: payload.componentType || null,
521
+ actionType: payload.actionType,
522
+ target: payload.target || null,
523
+ commandHash: payload.command ? commandHash(payload.command) : null,
524
+ packageManager: payload.packageManager || null,
525
+ scriptName: payload.scriptName || null,
526
+ packageName: payload.packageName || null,
527
+ mcpServer: payload.mcpServer || null,
528
+ toolName: payload.toolName || null,
529
+ targetHash: String(payload.actionType || "").startsWith("mcp.") ? targetHashFromMcpAction(payload) : null,
530
+ argsHash: String(payload.actionType || "").startsWith("mcp.") ? argsHashFromAction(payload) : null,
531
+ decision: payload.decision || null,
532
+ autoDecision: payload.autoDecision || null,
533
+ confidence: payload.confidence || null,
534
+ adapterId: payload.adapterId || null,
535
+ effectiveBehavior: payload.effectiveBehavior,
536
+ enforcementAvailable: payload.enforcementAvailable === true,
537
+ executed: payload.executed === true,
538
+ ttlSeconds: grant?.ttlSeconds || null,
539
+ reason: payload.reason || "",
540
+ timestamp: payload.timestamp,
541
+ });
542
+ }
543
+
544
+ function writeEnforcementAuditEvents(cwd, payload) {
545
+ const mode = String(payload.mode || "warn");
546
+ appendJsonl(cwd, ENFORCEMENT_AUDIT_LOG_REL_PATH, {
547
+ eventType: "agent_security_enforcement_attempt",
548
+ mode,
549
+ grantId: payload.jitGrant?.grantId || payload.jitGrantConsumed?.grantId || payload.jitGrantExpired?.grantId || null,
550
+ actionId: payload.actionId,
551
+ componentId: payload.componentId || null,
552
+ componentType: payload.componentType || null,
553
+ actionType: payload.actionType,
554
+ target: payload.target || null,
555
+ commandHash: payload.command ? commandHash(payload.command) : null,
556
+ packageManager: payload.packageManager || null,
557
+ scriptName: payload.scriptName || null,
558
+ packageName: payload.packageName || null,
559
+ mcpServer: payload.mcpServer || null,
560
+ toolName: payload.toolName || null,
561
+ targetHash: String(payload.actionType || "").startsWith("mcp.") ? targetHashFromMcpAction(payload) : null,
562
+ argsHash: String(payload.actionType || "").startsWith("mcp.") ? argsHashFromAction(payload) : null,
563
+ decision: payload.decision || null,
564
+ autoDecision: payload.autoDecision || null,
565
+ confidence: payload.confidence || null,
566
+ adapterId: payload.adapterId || null,
567
+ effectiveBehavior: payload.effectiveBehavior,
568
+ enforcementAvailable: payload.enforcementAvailable === true,
569
+ executed: payload.executed === true,
570
+ ttlSeconds: payload.jitGrant?.ttlSeconds || null,
571
+ reason: payload.reason || "",
572
+ timestamp: payload.timestamp,
573
+ });
574
+
575
+ if (payload.jitGrant) {
576
+ writeGrantAuditEvent(cwd, "agent_security_jit_grant_created", payload, payload.jitGrant);
577
+ }
578
+ if (payload.jitGrantConsumed) {
579
+ writeGrantAuditEvent(cwd, "agent_security_jit_grant_consumed", payload, payload.jitGrantConsumed);
580
+ }
581
+ if (payload.jitGrantExpired) {
582
+ writeGrantAuditEvent(cwd, "agent_security_jit_grant_expired", payload, payload.jitGrantExpired);
583
+ }
584
+
585
+ if (payload.enforcementAvailable === false || payload.effectiveBehavior === "recommended_only") {
586
+ appendJsonl(cwd, ENFORCEMENT_AUDIT_LOG_REL_PATH, {
587
+ eventType: mode === "auto" ? "agent_security_auto_recommended_only" : "agent_security_enforcement_unavailable",
588
+ mode,
589
+ grantId: null,
590
+ actionId: payload.actionId,
591
+ componentId: payload.componentId || null,
592
+ componentType: payload.componentType || null,
593
+ actionType: payload.actionType,
594
+ target: payload.target || null,
595
+ commandHash: payload.command ? commandHash(payload.command) : null,
596
+ packageManager: payload.packageManager || null,
597
+ scriptName: payload.scriptName || null,
598
+ packageName: payload.packageName || null,
599
+ mcpServer: payload.mcpServer || null,
600
+ toolName: payload.toolName || null,
601
+ targetHash: String(payload.actionType || "").startsWith("mcp.") ? targetHashFromMcpAction(payload) : null,
602
+ argsHash: String(payload.actionType || "").startsWith("mcp.") ? argsHashFromAction(payload) : null,
603
+ decision: payload.decision || null,
604
+ autoDecision: payload.autoDecision || null,
605
+ confidence: payload.confidence || null,
606
+ adapterId: payload.adapterId || null,
607
+ effectiveBehavior: payload.effectiveBehavior,
608
+ enforcementAvailable: false,
609
+ executed: false,
610
+ ttlSeconds: null,
611
+ reason: payload.reason || "",
612
+ timestamp: payload.timestamp,
613
+ });
614
+ return;
615
+ }
616
+
617
+ if (mode === "auto" && payload.autoDecision === "require_approval") {
618
+ appendJsonl(cwd, ENFORCEMENT_AUDIT_LOG_REL_PATH, {
619
+ eventType: "agent_security_auto_requires_approval",
620
+ mode,
621
+ grantId: null,
622
+ actionId: payload.actionId,
623
+ componentId: payload.componentId || null,
624
+ componentType: payload.componentType || null,
625
+ actionType: payload.actionType,
626
+ target: payload.target || null,
627
+ commandHash: payload.command ? commandHash(payload.command) : null,
628
+ packageManager: payload.packageManager || null,
629
+ scriptName: payload.scriptName || null,
630
+ packageName: payload.packageName || null,
631
+ mcpServer: payload.mcpServer || null,
632
+ toolName: payload.toolName || null,
633
+ targetHash: String(payload.actionType || "").startsWith("mcp.") ? targetHashFromMcpAction(payload) : null,
634
+ argsHash: String(payload.actionType || "").startsWith("mcp.") ? argsHashFromAction(payload) : null,
635
+ decision: payload.decision || null,
636
+ autoDecision: payload.autoDecision || null,
637
+ confidence: payload.confidence || null,
638
+ adapterId: payload.adapterId || null,
639
+ effectiveBehavior: payload.effectiveBehavior,
640
+ enforcementAvailable: payload.enforcementAvailable === true,
641
+ executed: false,
642
+ ttlSeconds: null,
643
+ reason: payload.reason || "",
644
+ timestamp: payload.timestamp,
645
+ });
646
+ return;
647
+ }
648
+
649
+ if (mode === "auto" && payload.autoDecision === "limit_plan_only") {
650
+ appendJsonl(cwd, ENFORCEMENT_AUDIT_LOG_REL_PATH, {
651
+ eventType: "agent_security_auto_limit_plan_only",
652
+ mode,
653
+ grantId: null,
654
+ actionId: payload.actionId,
655
+ componentId: payload.componentId || null,
656
+ componentType: payload.componentType || null,
657
+ actionType: payload.actionType,
658
+ target: payload.target || null,
659
+ commandHash: payload.command ? commandHash(payload.command) : null,
660
+ packageManager: payload.packageManager || null,
661
+ scriptName: payload.scriptName || null,
662
+ packageName: payload.packageName || null,
663
+ mcpServer: payload.mcpServer || null,
664
+ toolName: payload.toolName || null,
665
+ targetHash: String(payload.actionType || "").startsWith("mcp.") ? targetHashFromMcpAction(payload) : null,
666
+ argsHash: String(payload.actionType || "").startsWith("mcp.") ? argsHashFromAction(payload) : null,
667
+ decision: payload.decision || null,
668
+ autoDecision: payload.autoDecision || null,
669
+ confidence: payload.confidence || null,
670
+ adapterId: payload.adapterId || null,
671
+ effectiveBehavior: payload.effectiveBehavior,
672
+ enforcementAvailable: payload.enforcementAvailable === true,
673
+ executed: false,
674
+ ttlSeconds: null,
675
+ reason: payload.reason || "",
676
+ timestamp: payload.timestamp,
677
+ });
678
+ return;
679
+ }
680
+
681
+ if (payload.effectiveBehavior === "blocked") {
682
+ const eventType = mode === "auto"
683
+ ? "agent_security_auto_denied"
684
+ : payload.actionType === "filesystem.write"
685
+ ? "agent_security_file_write_blocked"
686
+ : (
687
+ payload.actionType === "package.install" || payload.actionType === "package.run" || payload.actionType === "package.script"
688
+ ? "agent_security_package_action_blocked"
689
+ : String(payload.actionType || "").startsWith("mcp.")
690
+ ? "agent_security_mcp_action_blocked"
691
+ : "agent_security_action_blocked"
692
+ );
693
+ appendJsonl(cwd, ENFORCEMENT_AUDIT_LOG_REL_PATH, {
694
+ eventType,
695
+ mode,
696
+ grantId: payload.jitGrant?.grantId || payload.jitGrantConsumed?.grantId || null,
697
+ actionId: payload.actionId,
698
+ componentId: payload.componentId || null,
699
+ componentType: payload.componentType || null,
700
+ actionType: payload.actionType,
701
+ target: payload.target || null,
702
+ commandHash: payload.command ? commandHash(payload.command) : null,
703
+ packageManager: payload.packageManager || null,
704
+ scriptName: payload.scriptName || null,
705
+ packageName: payload.packageName || null,
706
+ mcpServer: payload.mcpServer || null,
707
+ toolName: payload.toolName || null,
708
+ targetHash: String(payload.actionType || "").startsWith("mcp.") ? targetHashFromMcpAction(payload) : null,
709
+ argsHash: String(payload.actionType || "").startsWith("mcp.") ? argsHashFromAction(payload) : null,
710
+ decision: payload.decision || null,
711
+ autoDecision: payload.autoDecision || null,
712
+ confidence: payload.confidence || null,
713
+ adapterId: payload.adapterId || null,
714
+ effectiveBehavior: payload.effectiveBehavior,
715
+ enforcementAvailable: true,
716
+ executed: payload.executed === true,
717
+ ttlSeconds: payload.jitGrant?.ttlSeconds || payload.jitGrantConsumed?.ttlSeconds || null,
718
+ reason: payload.reason || "",
719
+ timestamp: payload.timestamp,
720
+ });
721
+ return;
722
+ }
723
+
724
+ if (payload.effectiveBehavior === "limited") {
725
+ const eventType = mode === "auto"
726
+ ? "agent_security_auto_limited"
727
+ : payload.actionType === "filesystem.write"
728
+ ? "agent_security_file_write_limited"
729
+ : (
730
+ payload.actionType === "package.install" || payload.actionType === "package.run" || payload.actionType === "package.script"
731
+ ? "agent_security_package_action_limited"
732
+ : String(payload.actionType || "").startsWith("mcp.")
733
+ ? "agent_security_mcp_action_limited"
734
+ : "agent_security_action_limited"
735
+ );
736
+ appendJsonl(cwd, ENFORCEMENT_AUDIT_LOG_REL_PATH, {
737
+ eventType,
738
+ mode,
739
+ grantId: payload.jitGrant?.grantId || payload.jitGrantConsumed?.grantId || null,
740
+ actionId: payload.actionId,
741
+ componentId: payload.componentId || null,
742
+ componentType: payload.componentType || null,
743
+ actionType: payload.actionType,
744
+ target: payload.target || null,
745
+ commandHash: payload.command ? commandHash(payload.command) : null,
746
+ packageManager: payload.packageManager || null,
747
+ scriptName: payload.scriptName || null,
748
+ packageName: payload.packageName || null,
749
+ mcpServer: payload.mcpServer || null,
750
+ toolName: payload.toolName || null,
751
+ targetHash: String(payload.actionType || "").startsWith("mcp.") ? targetHashFromMcpAction(payload) : null,
752
+ argsHash: String(payload.actionType || "").startsWith("mcp.") ? argsHashFromAction(payload) : null,
753
+ decision: payload.decision || null,
754
+ autoDecision: payload.autoDecision || null,
755
+ confidence: payload.confidence || null,
756
+ adapterId: payload.adapterId || null,
757
+ effectiveBehavior: payload.effectiveBehavior,
758
+ enforcementAvailable: true,
759
+ executed: payload.executed === true,
760
+ ttlSeconds: payload.jitGrant?.ttlSeconds || payload.jitGrantConsumed?.ttlSeconds || null,
761
+ reason: payload.reason || "",
762
+ timestamp: payload.timestamp,
763
+ });
764
+ return;
765
+ }
766
+
767
+ if (payload.actionType === "filesystem.write" && payload.decision === "approve_once" && payload.effectiveBehavior === "allowed") {
768
+ appendJsonl(cwd, ENFORCEMENT_AUDIT_LOG_REL_PATH, {
769
+ eventType: "agent_security_file_write_allowed_once",
770
+ mode,
771
+ grantId: payload.jitGrantConsumed?.grantId || payload.jitGrant?.grantId || null,
772
+ actionId: payload.actionId,
773
+ componentId: payload.componentId || null,
774
+ componentType: payload.componentType || null,
775
+ actionType: payload.actionType,
776
+ target: payload.target || null,
777
+ commandHash: payload.command ? commandHash(payload.command) : null,
778
+ packageManager: payload.packageManager || null,
779
+ scriptName: payload.scriptName || null,
780
+ packageName: payload.packageName || null,
781
+ decision: payload.decision || null,
782
+ autoDecision: payload.autoDecision || null,
783
+ confidence: payload.confidence || null,
784
+ adapterId: payload.adapterId || null,
785
+ effectiveBehavior: payload.effectiveBehavior,
786
+ enforcementAvailable: true,
787
+ executed: payload.executed === true,
788
+ ttlSeconds: payload.jitGrantConsumed?.ttlSeconds || payload.jitGrant?.ttlSeconds || null,
789
+ reason: payload.reason || "",
790
+ timestamp: payload.timestamp,
791
+ });
792
+ return;
793
+ }
794
+
795
+ if (
796
+ (payload.actionType === "package.install" || payload.actionType === "package.run" || payload.actionType === "package.script") &&
797
+ payload.decision === "approve_once" &&
798
+ payload.effectiveBehavior === "allowed"
799
+ ) {
800
+ appendJsonl(cwd, ENFORCEMENT_AUDIT_LOG_REL_PATH, {
801
+ eventType: "agent_security_package_action_allowed_once",
802
+ mode,
803
+ grantId: payload.jitGrantConsumed?.grantId || payload.jitGrant?.grantId || null,
804
+ actionId: payload.actionId,
805
+ componentId: payload.componentId || null,
806
+ componentType: payload.componentType || null,
807
+ actionType: payload.actionType,
808
+ target: payload.target || null,
809
+ commandHash: payload.command ? commandHash(payload.command) : null,
810
+ packageManager: payload.packageManager || null,
811
+ scriptName: payload.scriptName || null,
812
+ packageName: payload.packageName || null,
813
+ mcpServer: payload.mcpServer || null,
814
+ toolName: payload.toolName || null,
815
+ targetHash: String(payload.actionType || "").startsWith("mcp.") ? targetHashFromMcpAction(payload) : null,
816
+ argsHash: String(payload.actionType || "").startsWith("mcp.") ? argsHashFromAction(payload) : null,
817
+ decision: payload.decision || null,
818
+ autoDecision: payload.autoDecision || null,
819
+ confidence: payload.confidence || null,
820
+ adapterId: payload.adapterId || null,
821
+ effectiveBehavior: payload.effectiveBehavior,
822
+ enforcementAvailable: true,
823
+ executed: payload.executed === true,
824
+ ttlSeconds: payload.jitGrantConsumed?.ttlSeconds || payload.jitGrant?.ttlSeconds || null,
825
+ reason: payload.reason || "",
826
+ timestamp: payload.timestamp,
827
+ });
828
+ return;
829
+ }
830
+
831
+ if (String(payload.actionType || "").startsWith("mcp.") && payload.decision === "approve_once" && payload.effectiveBehavior === "allowed") {
832
+ appendJsonl(cwd, ENFORCEMENT_AUDIT_LOG_REL_PATH, {
833
+ eventType: "agent_security_mcp_action_allowed_once",
834
+ mode,
835
+ grantId: payload.jitGrantConsumed?.grantId || payload.jitGrant?.grantId || null,
836
+ actionId: payload.actionId,
837
+ componentId: payload.componentId || null,
838
+ componentType: payload.componentType || null,
839
+ actionType: payload.actionType,
840
+ target: payload.target || null,
841
+ commandHash: payload.command ? commandHash(payload.command) : null,
842
+ packageManager: payload.packageManager || null,
843
+ scriptName: payload.scriptName || null,
844
+ packageName: payload.packageName || null,
845
+ mcpServer: payload.mcpServer || null,
846
+ toolName: payload.toolName || null,
847
+ targetHash: targetHashFromMcpAction(payload),
848
+ argsHash: argsHashFromAction(payload),
849
+ decision: payload.decision || null,
850
+ autoDecision: payload.autoDecision || null,
851
+ confidence: payload.confidence || null,
852
+ adapterId: payload.adapterId || null,
853
+ effectiveBehavior: payload.effectiveBehavior,
854
+ enforcementAvailable: true,
855
+ executed: payload.executed === true,
856
+ ttlSeconds: payload.jitGrantConsumed?.ttlSeconds || payload.jitGrant?.ttlSeconds || null,
857
+ reason: payload.reason || "",
858
+ timestamp: payload.timestamp,
859
+ });
860
+ }
861
+ }
862
+
863
+ function normalizeResultPreview(value) {
864
+ const text = String(value || "");
865
+ return text.length > 400 ? `${text.slice(0, 400)}...` : text;
866
+ }
867
+
868
+ function normalizeEnforcementResult(action, adapterId, timestamp, result) {
869
+ const normalizedAction = normalizeAgentSecurityAction(action);
870
+ return {
871
+ actionId: normalizedAction.actionId,
872
+ componentId: normalizedAction.componentId || null,
873
+ componentType: normalizedAction.componentType || null,
874
+ actionType: normalizedAction.actionType || null,
875
+ target: normalizedAction.target || null,
876
+ command: normalizedAction.command || "",
877
+ packageManager: normalizedAction.packageManager || null,
878
+ scriptName: normalizedAction.scriptName || null,
879
+ packageName: normalizedAction.packageName || null,
880
+ mcpServer: normalizedAction.mcpServer || null,
881
+ toolName: normalizedAction.toolName || null,
882
+ commandHash: normalizedAction.commandHash || null,
883
+ targetHash: normalizedAction.targetHash || null,
884
+ contentHash: normalizedAction.contentHash || null,
885
+ argsHash: normalizedAction.argsHash || null,
886
+ scopeKey: normalizedAction.scopeKey || null,
887
+ decision: result?.decision || null,
888
+ adapterId: adapterId || null,
889
+ enforcementAvailable: result?.enforcementAvailable === true,
890
+ effectiveBehavior: String(result?.effectiveBehavior || "recommended_only"),
891
+ executed: result?.executed === true,
892
+ exitCode: typeof result?.exitCode === "number" ? result.exitCode : null,
893
+ stdoutPreview: normalizeResultPreview(result?.stdoutPreview || ""),
894
+ stderrPreview: normalizeResultPreview(result?.stderrPreview || ""),
895
+ reason: String(result?.reason || ""),
896
+ timestamp: result?.timestamp || timestamp,
897
+ };
898
+ }
899
+
900
+ function buildUnavailableResult(action, timestamp, reason, decision) {
901
+ return normalizeEnforcementResult(action, null, timestamp, {
902
+ decision: decision || null,
903
+ adapterId: null,
904
+ enforcementAvailable: false,
905
+ effectiveBehavior: "recommended_only",
906
+ executed: false,
907
+ exitCode: null,
908
+ stdoutPreview: "",
909
+ stderrPreview: "",
910
+ reason,
911
+ timestamp,
912
+ });
913
+ }
914
+
915
+ function buildMatchingGrantDecisionRecord(grant, action, timestamp) {
916
+ return {
917
+ decisionId: `grant-${grant.grantId}`,
918
+ actionId: action?.actionId || null,
919
+ componentId: action?.componentId || null,
920
+ decision: grant.decision || "approve_once",
921
+ mode: "warn",
922
+ effectiveBehavior: grant.decision === "let_wuz_limit" ? "limited" : "allowed",
923
+ ttlSeconds: grant.ttlSeconds || DEFAULT_GRANT_TTL_SECONDS,
924
+ scope: {
925
+ cwd: grant.scope?.cwd || "",
926
+ actionType: action?.actionType || null,
927
+ commandHash: grant.commandHash || null,
928
+ targetHash: grant.targetHash || null,
929
+ contentHash: grant.contentHash || null,
930
+ allowedCommands: grant.scope?.allowedCommands || [],
931
+ allowedTargets: grant.scope?.allowedTargets || [],
932
+ allowedPaths: grant.scope?.paths || [],
933
+ packageManager: grant.scope?.packageManager || null,
934
+ scriptName: grant.scope?.scriptName || null,
935
+ packageName: grant.scope?.packageName || null,
936
+ mcpServer: grant.scope?.mcpServer || null,
937
+ toolName: grant.scope?.toolName || null,
938
+ argsHash: grant.argsHash || null,
939
+ },
940
+ denied: Array.isArray(grant.deniedActions) ? grant.deniedActions : [],
941
+ reason: grant.reason || "Matched an active JIT grant for this exact action.",
942
+ timestamp,
943
+ };
944
+ }
945
+
946
+ function maybeExpireGrant(cwd, grant, action, timestamp, options = {}) {
947
+ if (!grant || !isGrantExpired(grant, timestamp) || grant.consumedAt || options.writeEvidence === false) return null;
948
+ return normalizeGrant(grant);
949
+ }
950
+
951
+ function recordEnforcementOutcome(cwd, payload, options = {}) {
952
+ const mode = String(options.mode || payload.mode || "warn");
953
+ const unsupportedActionTypes = Array.isArray(options.unsupportedActionTypes) ? options.unsupportedActionTypes : [];
954
+ const capabilityMatrix = options.writeEvidence === false
955
+ ? null
956
+ : writeEnforcementCapabilityMatrix(cwd, payload.timestamp, mode, unsupportedActionTypes);
957
+ const activeJitGrants = countActiveJitGrants(loadJitGrantStore(cwd), payload.timestamp);
958
+ const normalizedPayload = {
959
+ ...payload,
960
+ mode,
961
+ activeJitGrants,
962
+ jitGrantCreated: Boolean(payload.jitGrant),
963
+ jitGrantConsumed: Boolean(payload.jitGrantConsumed),
964
+ jitGrantExpired: Boolean(payload.jitGrantExpired),
965
+ };
966
+ const summary = options.writeEvidence === false ? null : updateEnforcementSummary(cwd, normalizedPayload);
967
+ if (options.writeEvidence !== false) {
968
+ writeEnforcementAuditEvents(cwd, normalizedPayload);
969
+ }
970
+ return {
971
+ ...payload,
972
+ mode,
973
+ enforcementSummary: summary,
974
+ capabilityMatrix,
975
+ };
976
+ }
977
+
978
+ function enforceAgentSecurityAction(cwd, input, options = {}) {
979
+ const timestamp = options.timestamp;
980
+ const mode = String(options.mode || input?.decisionRecord?.mode || "warn");
981
+ const action = normalizeAgentSecurityAction(input?.action || {});
982
+ const evaluation = input?.evaluation || {};
983
+ const limitPlan = input?.limitPlan || null;
984
+ const decisionRecord = input?.decisionRecord || null;
985
+ const adapter = resolveAgentSecurityAdapter(action?.actionType);
986
+ let jitGrant = null;
987
+ let jitGrantConsumed = null;
988
+ let jitGrantExpired = null;
989
+
990
+ if (!adapter) {
991
+ const unavailable = buildUnavailableResult(action, timestamp, "No enforcement adapter is available for this action type.", decisionRecord?.decision || null);
992
+ return recordEnforcementOutcome(cwd, {
993
+ ...unavailable,
994
+ mode,
995
+ jitGrant: null,
996
+ jitGrantConsumed: null,
997
+ jitGrantExpired: null,
998
+ autoDecision: options.autoDecision || null,
999
+ confidence: options.confidence || null,
1000
+ }, {
1001
+ mode,
1002
+ writeEvidence: options.writeEvidence,
1003
+ unsupportedActionTypes: [action?.actionType],
1004
+ });
1005
+ }
1006
+
1007
+ const store = loadJitGrantStore(cwd);
1008
+ let matchingGrant = null;
1009
+ for (const grant of store.grants || []) {
1010
+ if (!matchesGrantAction(grant, action, cwd)) continue;
1011
+ if (grant.consumedAt) continue;
1012
+ if (isGrantExpired(grant, timestamp)) {
1013
+ jitGrantExpired = maybeExpireGrant(cwd, grant, action, timestamp, options);
1014
+ continue;
1015
+ }
1016
+ matchingGrant = grant;
1017
+ break;
1018
+ }
1019
+
1020
+ const effectiveDecision = decisionRecord?.decision || matchingGrant?.decision || null;
1021
+
1022
+ if (!effectiveDecision && evaluation.decisionRequired) {
1023
+ return recordEnforcementOutcome(cwd, {
1024
+ actionId: action?.actionId || null,
1025
+ componentId: action?.componentId || null,
1026
+ componentType: action?.componentType || null,
1027
+ actionType: action?.actionType || null,
1028
+ target: action?.target || null,
1029
+ command: action?.command || "",
1030
+ packageManager: action?.packageManager || null,
1031
+ scriptName: action?.scriptName || null,
1032
+ packageName: action?.packageName || null,
1033
+ mcpServer: action?.mcpServer || null,
1034
+ toolName: action?.toolName || null,
1035
+ decision: null,
1036
+ adapterId: adapter.adapterId,
1037
+ enforcementAvailable: true,
1038
+ effectiveBehavior: "blocked",
1039
+ executed: false,
1040
+ exitCode: null,
1041
+ stdoutPreview: "",
1042
+ stderrPreview: "",
1043
+ reason: "Decision required before executing this action.",
1044
+ timestamp,
1045
+ mode,
1046
+ autoDecision: options.autoDecision || null,
1047
+ confidence: options.confidence || null,
1048
+ jitGrantExpired,
1049
+ jitGrant: null,
1050
+ jitGrantConsumed: null,
1051
+ }, {
1052
+ mode,
1053
+ writeEvidence: options.writeEvidence,
1054
+ });
1055
+ }
1056
+
1057
+ const adapterDecisionRecord = decisionRecord || (matchingGrant ? buildMatchingGrantDecisionRecord(matchingGrant, action, timestamp) : null);
1058
+ const rawResult = adapter.enforce(action, adapterDecisionRecord, limitPlan, {
1059
+ cwd,
1060
+ execute: options.execute === true,
1061
+ timestamp,
1062
+ executor: options.executor,
1063
+ });
1064
+ const result = normalizeEnforcementResult(action, adapter.adapterId, timestamp, rawResult);
1065
+
1066
+ if (decisionRecord && !matchingGrant) {
1067
+ const grantBuild = buildJitGrant(action, decisionRecord, limitPlan, {
1068
+ cwd,
1069
+ timestamp,
1070
+ adapterId: adapter.adapterId,
1071
+ enforcementAvailable: result.enforcementAvailable === true,
1072
+ effectiveBehavior: result.effectiveBehavior,
1073
+ reason: result.reason,
1074
+ });
1075
+ if (grantBuild.grantCreated && grantBuild.grant) {
1076
+ jitGrant = recordGrant(cwd, grantBuild.grant, timestamp);
1077
+ matchingGrant = jitGrant;
1078
+ }
1079
+ }
1080
+
1081
+ if (
1082
+ options.execute === true &&
1083
+ matchingGrant &&
1084
+ matchingGrant.singleUse === true &&
1085
+ (result.effectiveBehavior === "allowed" || result.effectiveBehavior === "limited") &&
1086
+ result.executed === true &&
1087
+ Number(result.exitCode) === 0
1088
+ ) {
1089
+ jitGrantConsumed = consumeGrant(cwd, matchingGrant.grantId, timestamp);
1090
+ }
1091
+
1092
+ return recordEnforcementOutcome(cwd, {
1093
+ ...result,
1094
+ mode,
1095
+ autoDecision: options.autoDecision || null,
1096
+ confidence: options.confidence || null,
1097
+ target: action?.target || null,
1098
+ command: action?.command || "",
1099
+ packageManager: action?.packageManager || null,
1100
+ scriptName: action?.scriptName || null,
1101
+ packageName: action?.packageName || null,
1102
+ mcpServer: action?.mcpServer || null,
1103
+ toolName: action?.toolName || null,
1104
+ jitGrant,
1105
+ jitGrantConsumed,
1106
+ jitGrantExpired,
1107
+ }, {
1108
+ mode,
1109
+ writeEvidence: options.writeEvidence,
1110
+ });
1111
+ }
1112
+
1113
+ module.exports = {
1114
+ ENFORCEMENT_CAPABILITIES_REL_PATH,
1115
+ ENFORCEMENT_SUMMARY_REL_PATH,
1116
+ JIT_GRANTS_REL_PATH,
1117
+ ONE_TIME_GRANTS_REL_PATH,
1118
+ ENFORCEMENT_AUDIT_LOG_REL_PATH,
1119
+ DEFAULT_GRANT_TTL_SECONDS,
1120
+ commandHash,
1121
+ ttlSecondsFromRecommendation,
1122
+ loadJitGrantStore,
1123
+ loadEnforcementSummary,
1124
+ normalizeGrant,
1125
+ normalizeEnforcementResult,
1126
+ buildJitGrant,
1127
+ recordEnforcementOutcome,
1128
+ enforceAgentSecurityAction,
1129
+ };