@intutic/sync-daemon 1.9.0 → 1.10.1

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 (200) hide show
  1. package/README.md +2 -2
  2. package/dist/agentReporter.d.ts +205 -0
  3. package/dist/agentReporter.d.ts.map +1 -0
  4. package/dist/agentReporter.js +394 -0
  5. package/dist/agentReporter.js.map +1 -0
  6. package/dist/configReader.d.ts +39 -2
  7. package/dist/configReader.d.ts.map +1 -1
  8. package/dist/configReader.js +56 -2
  9. package/dist/configReader.js.map +1 -1
  10. package/dist/configWriter.d.ts +51 -1
  11. package/dist/configWriter.d.ts.map +1 -1
  12. package/dist/configWriter.js +490 -63
  13. package/dist/configWriter.js.map +1 -1
  14. package/dist/harness/antigravityHooks.d.ts.map +1 -1
  15. package/dist/harness/antigravityHooks.js +33 -55
  16. package/dist/harness/antigravityHooks.js.map +1 -1
  17. package/dist/harness/claudeCodeHooks.d.ts +47 -13
  18. package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
  19. package/dist/harness/claudeCodeHooks.js +323 -55
  20. package/dist/harness/claudeCodeHooks.js.map +1 -1
  21. package/dist/harness/claudeDesktopHooks.d.ts.map +1 -1
  22. package/dist/harness/claudeDesktopHooks.js +35 -48
  23. package/dist/harness/claudeDesktopHooks.js.map +1 -1
  24. package/dist/harness/clineHooks.d.ts.map +1 -1
  25. package/dist/harness/clineHooks.js +42 -34
  26. package/dist/harness/clineHooks.js.map +1 -1
  27. package/dist/harness/codexHooks.d.ts +36 -0
  28. package/dist/harness/codexHooks.d.ts.map +1 -0
  29. package/dist/harness/codexHooks.js +227 -0
  30. package/dist/harness/codexHooks.js.map +1 -0
  31. package/dist/harness/continueHooks.d.ts +45 -5
  32. package/dist/harness/continueHooks.d.ts.map +1 -1
  33. package/dist/harness/continueHooks.js +225 -5
  34. package/dist/harness/continueHooks.js.map +1 -1
  35. package/dist/harness/cursorHooks.d.ts +31 -0
  36. package/dist/harness/cursorHooks.d.ts.map +1 -1
  37. package/dist/harness/cursorHooks.js +78 -49
  38. package/dist/harness/cursorHooks.js.map +1 -1
  39. package/dist/harness/dshHooks.d.ts +198 -0
  40. package/dist/harness/dshHooks.d.ts.map +1 -0
  41. package/dist/harness/dshHooks.js +569 -0
  42. package/dist/harness/dshHooks.js.map +1 -0
  43. package/dist/harness/gateBody.d.ts +422 -0
  44. package/dist/harness/gateBody.d.ts.map +1 -0
  45. package/dist/harness/gateBody.js +1816 -0
  46. package/dist/harness/gateBody.js.map +1 -0
  47. package/dist/harness/gateKind.d.ts +164 -0
  48. package/dist/harness/gateKind.d.ts.map +1 -0
  49. package/dist/harness/gateKind.js +143 -0
  50. package/dist/harness/gateKind.js.map +1 -0
  51. package/dist/harness/githubCopilotHooks.d.ts +54 -0
  52. package/dist/harness/githubCopilotHooks.d.ts.map +1 -0
  53. package/dist/harness/githubCopilotHooks.js +218 -0
  54. package/dist/harness/githubCopilotHooks.js.map +1 -0
  55. package/dist/harness/gooseHooks.d.ts.map +1 -1
  56. package/dist/harness/gooseHooks.js +67 -37
  57. package/dist/harness/gooseHooks.js.map +1 -1
  58. package/dist/harness/grokHooks.d.ts +77 -0
  59. package/dist/harness/grokHooks.d.ts.map +1 -0
  60. package/dist/harness/grokHooks.js +350 -0
  61. package/dist/harness/grokHooks.js.map +1 -0
  62. package/dist/harness/hermesHooks.d.ts.map +1 -1
  63. package/dist/harness/hermesHooks.js +26 -55
  64. package/dist/harness/hermesHooks.js.map +1 -1
  65. package/dist/harness/holdRedaction.d.ts +69 -0
  66. package/dist/harness/holdRedaction.d.ts.map +1 -0
  67. package/dist/harness/holdRedaction.js +211 -0
  68. package/dist/harness/holdRedaction.js.map +1 -0
  69. package/dist/harness/jetbrainsXmlConfig.d.ts +51 -0
  70. package/dist/harness/jetbrainsXmlConfig.d.ts.map +1 -0
  71. package/dist/harness/jetbrainsXmlConfig.js +151 -0
  72. package/dist/harness/jetbrainsXmlConfig.js.map +1 -0
  73. package/dist/harness/mcpAutoWrite.d.ts +100 -3
  74. package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
  75. package/dist/harness/mcpAutoWrite.js +712 -90
  76. package/dist/harness/mcpAutoWrite.js.map +1 -1
  77. package/dist/harness/museHooks.d.ts +53 -0
  78. package/dist/harness/museHooks.d.ts.map +1 -0
  79. package/dist/harness/museHooks.js +273 -0
  80. package/dist/harness/museHooks.js.map +1 -0
  81. package/dist/harness/n8nHooks.d.ts +25 -9
  82. package/dist/harness/n8nHooks.d.ts.map +1 -1
  83. package/dist/harness/n8nHooks.js +174 -9
  84. package/dist/harness/n8nHooks.js.map +1 -1
  85. package/dist/harness/openCodeHooks.d.ts +93 -0
  86. package/dist/harness/openCodeHooks.d.ts.map +1 -0
  87. package/dist/harness/openCodeHooks.js +248 -0
  88. package/dist/harness/openCodeHooks.js.map +1 -0
  89. package/dist/harness/openWebuiHooks.d.ts.map +1 -1
  90. package/dist/harness/openWebuiHooks.js +56 -11
  91. package/dist/harness/openWebuiHooks.js.map +1 -1
  92. package/dist/harness/openclawHooks.d.ts.map +1 -1
  93. package/dist/harness/openclawHooks.js +30 -54
  94. package/dist/harness/openclawHooks.js.map +1 -1
  95. package/dist/harness/openhandsHooks.d.ts.map +1 -1
  96. package/dist/harness/openhandsHooks.js +35 -41
  97. package/dist/harness/openhandsHooks.js.map +1 -1
  98. package/dist/harness/piHooks.d.ts.map +1 -1
  99. package/dist/harness/piHooks.js +31 -58
  100. package/dist/harness/piHooks.js.map +1 -1
  101. package/dist/harness/protectedPaths.d.ts +353 -0
  102. package/dist/harness/protectedPaths.d.ts.map +1 -0
  103. package/dist/harness/protectedPaths.js +805 -0
  104. package/dist/harness/protectedPaths.js.map +1 -0
  105. package/dist/harness/rooCodeHooks.d.ts.map +1 -1
  106. package/dist/harness/rooCodeHooks.js +34 -48
  107. package/dist/harness/rooCodeHooks.js.map +1 -1
  108. package/dist/harness/windsurfHooks.d.ts +72 -0
  109. package/dist/harness/windsurfHooks.d.ts.map +1 -1
  110. package/dist/harness/windsurfHooks.js +172 -58
  111. package/dist/harness/windsurfHooks.js.map +1 -1
  112. package/dist/harness/windsurfJetBrainsProxy.d.ts +78 -0
  113. package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -0
  114. package/dist/harness/windsurfJetBrainsProxy.js +159 -0
  115. package/dist/harness/windsurfJetBrainsProxy.js.map +1 -0
  116. package/dist/hashReporter.js +1 -1
  117. package/dist/hashReporter.js.map +1 -1
  118. package/dist/index.d.ts +12 -6
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +14 -6
  121. package/dist/index.js.map +1 -1
  122. package/dist/integrityStore.d.ts +0 -8
  123. package/dist/integrityStore.d.ts.map +1 -1
  124. package/dist/integrityStore.js +6 -29
  125. package/dist/integrityStore.js.map +1 -1
  126. package/dist/lib/approvedBypasses.d.ts +76 -0
  127. package/dist/lib/approvedBypasses.d.ts.map +1 -0
  128. package/dist/lib/approvedBypasses.js +140 -0
  129. package/dist/lib/approvedBypasses.js.map +1 -0
  130. package/dist/lib/complianceProbes.d.ts.map +1 -1
  131. package/dist/lib/complianceProbes.js +5 -0
  132. package/dist/lib/complianceProbes.js.map +1 -1
  133. package/dist/lib/decisionsDigest.d.ts +99 -0
  134. package/dist/lib/decisionsDigest.d.ts.map +1 -0
  135. package/dist/lib/decisionsDigest.js +199 -0
  136. package/dist/lib/decisionsDigest.js.map +1 -0
  137. package/dist/lib/egressPolicy.d.ts +69 -0
  138. package/dist/lib/egressPolicy.d.ts.map +1 -0
  139. package/dist/lib/egressPolicy.js +126 -0
  140. package/dist/lib/egressPolicy.js.map +1 -0
  141. package/dist/lib/gitWorktrees.d.ts +140 -0
  142. package/dist/lib/gitWorktrees.d.ts.map +1 -0
  143. package/dist/lib/gitWorktrees.js +191 -0
  144. package/dist/lib/gitWorktrees.js.map +1 -0
  145. package/dist/lib/policySnapshot.d.ts +224 -0
  146. package/dist/lib/policySnapshot.d.ts.map +1 -0
  147. package/dist/lib/policySnapshot.js +637 -0
  148. package/dist/lib/policySnapshot.js.map +1 -0
  149. package/dist/lib/processPoller.d.ts +44 -0
  150. package/dist/lib/processPoller.d.ts.map +1 -1
  151. package/dist/lib/processPoller.js +176 -1
  152. package/dist/lib/processPoller.js.map +1 -1
  153. package/dist/lib/runtimeEnv.d.ts +8 -0
  154. package/dist/lib/runtimeEnv.d.ts.map +1 -1
  155. package/dist/lib/runtimeEnv.js +3 -0
  156. package/dist/lib/runtimeEnv.js.map +1 -1
  157. package/dist/localTrajectoryHeuristics.d.ts +51 -0
  158. package/dist/localTrajectoryHeuristics.d.ts.map +1 -0
  159. package/dist/localTrajectoryHeuristics.js +90 -0
  160. package/dist/localTrajectoryHeuristics.js.map +1 -0
  161. package/dist/sessionReporter.d.ts +70 -0
  162. package/dist/sessionReporter.d.ts.map +1 -0
  163. package/dist/sessionReporter.js +140 -0
  164. package/dist/sessionReporter.js.map +1 -0
  165. package/dist/skillWriter.d.ts +1 -1
  166. package/dist/skillWriter.d.ts.map +1 -1
  167. package/dist/skillWriter.js +6 -3
  168. package/dist/skillWriter.js.map +1 -1
  169. package/dist/syncLoop.d.ts +105 -3
  170. package/dist/syncLoop.d.ts.map +1 -1
  171. package/dist/syncLoop.js +397 -65
  172. package/dist/syncLoop.js.map +1 -1
  173. package/dist/trajectoryMonitor.d.ts +2 -0
  174. package/dist/trajectoryMonitor.d.ts.map +1 -1
  175. package/dist/trajectoryMonitor.js +57 -0
  176. package/dist/trajectoryMonitor.js.map +1 -1
  177. package/dist/watcher/driftWatcher.d.ts.map +1 -1
  178. package/dist/watcher/driftWatcher.js +12 -3
  179. package/dist/watcher/driftWatcher.js.map +1 -1
  180. package/dist/watcher/settingsGuard.d.ts +23 -0
  181. package/dist/watcher/settingsGuard.d.ts.map +1 -1
  182. package/dist/watcher/settingsGuard.js +202 -17
  183. package/dist/watcher/settingsGuard.js.map +1 -1
  184. package/package.json +37 -6
  185. package/dist/brainIndexer.d.ts +0 -51
  186. package/dist/brainIndexer.d.ts.map +0 -1
  187. package/dist/brainIndexer.js +0 -148
  188. package/dist/brainIndexer.js.map +0 -1
  189. package/dist/harness/cursorHooksJson.d.ts +0 -29
  190. package/dist/harness/cursorHooksJson.d.ts.map +0 -1
  191. package/dist/harness/cursorHooksJson.js +0 -130
  192. package/dist/harness/cursorHooksJson.js.map +0 -1
  193. package/dist/harness/gooseHooksWriter.d.ts +0 -27
  194. package/dist/harness/gooseHooksWriter.d.ts.map +0 -1
  195. package/dist/harness/gooseHooksWriter.js +0 -147
  196. package/dist/harness/gooseHooksWriter.js.map +0 -1
  197. package/dist/harness/openhandsHooksWriter.d.ts +0 -23
  198. package/dist/harness/openhandsHooksWriter.d.ts.map +0 -1
  199. package/dist/harness/openhandsHooksWriter.js +0 -146
  200. package/dist/harness/openhandsHooksWriter.js.map +0 -1
@@ -0,0 +1,637 @@
1
+ /**
2
+ * policySnapshot.ts — ships workspace policy *to* the machine, so the gate does
3
+ * not have to ship the decision off it.
4
+ *
5
+ * # Why
6
+ *
7
+ * TD-307: `POST /api/v1/hook-gate` was called by two of thirteen gates, so DLP,
8
+ * `BLOCK:` SOPs, SSO policy and promoted findings reached two of thirteen. The
9
+ * obvious repair — route the other eleven through the gate — is the wrong one:
10
+ * those eleven enforce locally and **fail closed**, the two network callers were
11
+ * the only ones that **fail open**, and the gate's allow path is five or six
12
+ * sequential Postgres round-trips. Routing everyone through it would trade
13
+ * fail-closed for fail-open and put a control-plane outage on every `Bash`.
14
+ *
15
+ * Almost none of that decision needs the network. So the daemon resolves policy
16
+ * once per sync cycle and writes it here; every gate reads a local file.
17
+ *
18
+ * # Two tiers, one invariant
19
+ *
20
+ * The **static floor** (`staticFloorPatterns()`) is compiled into each gate and
21
+ * always enforced. The **dynamic tier** is this file, and it is *additive only*:
22
+ *
23
+ * > For every harness and every snapshot state, the set of calls blocked after
24
+ * > this change is a superset of the set blocked before.
25
+ *
26
+ * That is what makes the failure modes tractable. A missing or corrupt snapshot
27
+ * costs only rules that, for eleven of the thirteen gates, did not exist at all
28
+ * before — so degrading returns to yesterday's behaviour rather than opening a
29
+ * new hole. It is also why an absent snapshot does **not** fail closed: that
30
+ * would brick every agent on every machine at install time, to replace
31
+ * protection the floor already provides.
32
+ *
33
+ * # Where it lives, and why that matters
34
+ *
35
+ * `~/.intutic/hooks/policy-snapshot.{json,rules}`. The directory is load-bearing:
36
+ * `.intutic/hooks` is already in `UNIVERSAL_PROTECTED_PATHS`, so all thirteen
37
+ * gates refuse an agent's attempt to write the snapshot on day one, with no new
38
+ * entry and no new code.
39
+ *
40
+ * # The ` WHERE ` clause survives this pipeline now
41
+ *
42
+ * A SOP titled `BLOCK:^shell$ WHERE kubectl\s+apply(?!.*@sha256:):reason`
43
+ * resolves to `{toolPattern, argPattern}` at `GET /api/v1/policy/resolve`
44
+ * (the demo doctor's "argPattern served" check proves that half). This module
45
+ * used to be where the other half died: `ResolvedPolicy` did not declare
46
+ * `argPattern`, `toGuardPattern` did not carry it, and the rule reached all
47
+ * twelve tool-call gates as "block `shell` unconditionally" — simultaneously
48
+ * failing to enforce the argument condition and re-manufacturing the exact
49
+ * over-blocking (`make test` refused) the WHERE grammar was invented to
50
+ * eliminate. It now travels: verbatim in the JSON's `sopRules` and `rules`,
51
+ * base64-encoded in the `.rules` projection's seventh column (see
52
+ * `RULES_COLUMNS` in gateBody.ts for the encoding contract), and every gate
53
+ * family conditions a name-matched rule on it before firing.
54
+ *
55
+ * @module
56
+ */
57
+ import * as fs from 'node:fs/promises';
58
+ import * as path from 'node:path';
59
+ import * as os from 'node:os';
60
+ import { createHash } from 'node:crypto';
61
+ import { createLogger } from '@intutic/logger';
62
+ import { toRulesLine, GATE_VERSION, RULES_COLUMNS } from '../harness/gateBody.js';
63
+ import { DESTRUCTIVE_COMMAND_PATTERNS, SKILL_SURFACE_PATTERNS, assertPortableEre, } from '../harness/protectedPaths.js';
64
+ const log = createLogger('sync-policy-snapshot');
65
+ /** Directory every gate looks in. Inside `.intutic/hooks`, which is already
66
+ * protected — see the module note. */
67
+ export const DEFAULT_SNAPSHOT_DIR = path.join(os.homedir(), '.intutic', 'hooks');
68
+ export const SNAPSHOT_JSON = 'policy-snapshot.json';
69
+ export const SNAPSHOT_RULES = 'policy-snapshot.rules';
70
+ /**
71
+ * Whether the destructive tier ships as `block` or as `warn`.
72
+ *
73
+ * `warn`. These seven patterns qualify for `block` on the merits — every one is
74
+ * unrecoverable without a reinstall or a backup — but they have never run
75
+ * against real developer traffic, and they would land on thirteen harnesses at
76
+ * once. A false positive here does not merely annoy: the fastest workaround
77
+ * available to a blocked developer is `chflags nouchg` on the hook, which is the
78
+ * exact bypass `GOVERNANCE_BYPASS_PATTERNS` exists to stop. Shipping too hard
79
+ * manufactures our own adversary.
80
+ *
81
+ * So they ride the advisory tier first and earn promotion from
82
+ * `tool_flagged` telemetry — the same discipline `sslGateEvaluator.ts` follows
83
+ * for SSL enforcement, and the same one `packages/proxy/src/plugins/anomaly`
84
+ * states as the promotion rule. Flipping this constant is the promotion, and it
85
+ * belongs in a commit alongside the measurement that licenses it.
86
+ *
87
+ * Shipping them *through the snapshot* rather than compiling them into the gate
88
+ * is the other half: the set we are least sure about is the set we can retract
89
+ * in one sync cycle instead of one release.
90
+ */
91
+ export const DESTRUCTIVE_TIER_SEVERITY = 'warn';
92
+ /**
93
+ * Whether the skill-directory-write tier ships as `block` or as `warn`.
94
+ *
95
+ * `block` — deliberately not following `DESTRUCTIVE_TIER_SEVERITY`'s warn-first
96
+ * ramp, because the thing that ramp is buying time for does not exist on this
97
+ * surface. `DESTRUCTIVE_COMMAND_PATTERNS` rides at `warn` because those seven
98
+ * families are unmeasured against real developer traffic and a false positive
99
+ * has a real cost (see that constant's own comment). `SKILL_SURFACE_PATTERNS`
100
+ * is not that kind of rule: it does not run `scanSkillContent` or judge
101
+ * anything about what a skill file says, only WHERE a write's target path
102
+ * points — `.agents/skills/**` or `.claude/skills/**`, a deterministic,
103
+ * zero-ambiguity match. TD-358 (`docs/TECH_DEBT.md`) says this about exactly
104
+ * that distinction: "Path-matching is the one exception, and it is not
105
+ * actually an exception to the measurement requirement — it never needed
106
+ * one." There is no false-positive rate to earn a promotion by measuring,
107
+ * because there is nothing probabilistic here to measure in the first place.
108
+ *
109
+ * The second half of the argument is what `warn` actually buys on this
110
+ * surface, and the answer is nothing. `SECRET_CONTENT_PATTERNS`
111
+ * (`harness/protectedPaths.ts`) skips the warn-first ramp for the same reason
112
+ * and states the principle this constant now follows: "A warn that lets the
113
+ * write proceed IS the incident." A poisoned skill file written under
114
+ * warn-only is not a near-miss sitting in telemetry waiting for a human to
115
+ * notice — it is a file on disk that the very next agent session loads and
116
+ * trusts as instructions, the same way it trusts its own system prompt. Warn
117
+ * logs the write and lets it land anyway, so there is no window in which the
118
+ * advisory tier protected anything. That is what makes this different from
119
+ * `DESTRUCTIVE_COMMAND_PATTERNS`: a destructive *command*'s warn tier buys a
120
+ * chance for a human to notice before the damage is done; a skill file, once
121
+ * written, already is the damage.
122
+ *
123
+ * Shipped *through the snapshot* rather than promoted in place in
124
+ * `staticFloorPatterns()`, for the same operational reason
125
+ * `DESTRUCTIVE_TIER_SEVERITY` is: the rollout, not the mechanism, is the thing
126
+ * still short on field time, and the snapshot channel is what makes it
127
+ * retractable in one sync cycle instead of one release if that turns out
128
+ * wrong. `staticFloorPatterns()` keeps its two `SKILL_SURFACE_PATTERNS`
129
+ * entries at `warn` — unchanged by this constant — as the degraded-mode
130
+ * baseline for a workspace with no snapshot, or an invalid one; this constant
131
+ * is what promotes the steady state to `block` everywhere a valid snapshot has
132
+ * landed. Flipping it back to `'warn'` is the retraction, exactly as it is for
133
+ * `DESTRUCTIVE_TIER_SEVERITY`.
134
+ */
135
+ export const SKILL_SURFACE_TIER_SEVERITY = 'block';
136
+ /**
137
+ * Tool names used to detect a rule that blocks everything.
138
+ *
139
+ * A pattern matching all of these is a catch-all whatever it looks like, so this
140
+ * catches `.*`, `.+`, `[A-Za-z]*` and anything else someone reaches for — rather
141
+ * than a denylist of the three spellings we happened to think of.
142
+ */
143
+ const CANARY_TOOLS = [
144
+ 'Bash', 'Read', 'Write', 'Edit', 'Glob', 'Grep', 'Task', 'WebFetch',
145
+ 'NotebookEdit', 'TodoWrite', 'run_command', 'str_replace_editor',
146
+ // M3: two `mcp__<server>__<tool>`-shaped canaries, so a rule that is
147
+ // scoped to one MCP server (e.g. `mcp__github__.*`) is exercised against
148
+ // this same threshold check as every other tool-name pattern, and a rule
149
+ // that reaches all the way into MCP-tool-shaped names (not just the
150
+ // native tool names above) is still caught as a catch-all.
151
+ 'mcp__github__create_issue', 'mcp__filesystem__read_file',
152
+ ];
153
+ /**
154
+ * Strips a repeated character from one end of a string, in linear time.
155
+ *
156
+ * `s.replace(/\$+$/, '')` is the obvious spelling and it is quadratic. The
157
+ * quantifier is greedy and the anchor is at the *end*, so on a string that does
158
+ * not finish with the character, every start offset consumes the whole run and
159
+ * then backtracks through it: measured on `'$'.repeat(n) + 'a'`, 20 KB took
160
+ * 130 ms and 80 KB took 2 seconds. A string that *does* end with it matches at
161
+ * the first offset and returns instantly, which is why the obvious test for
162
+ * this passes — CodeQL flagged all three call sites as `js/polynomial-redos`.
163
+ *
164
+ * Two of them run over `toolPattern`, which arrives from the control plane and
165
+ * is authored per workspace, on the path that decides whether a policy rule
166
+ * ships to the blocking gate.
167
+ */
168
+ function stripEnd(s, ch) {
169
+ let end = s.length;
170
+ while (end > 0 && s[end - 1] === ch)
171
+ end -= 1;
172
+ return s.slice(0, end);
173
+ }
174
+ /** The same, from the front. Anchored at the start, so this one is linear. */
175
+ function stripStart(s, ch) {
176
+ let i = 0;
177
+ while (i < s.length && s[i] === ch)
178
+ i += 1;
179
+ return s.slice(i);
180
+ }
181
+ /**
182
+ * Decides whether a control-plane rule is safe to put on a blocking path.
183
+ *
184
+ * Returns the reason for rejection, or `null` if the rule is fine. Rejections
185
+ * are logged rather than thrown: one bad SOP must not cost the workspace every
186
+ * other rule.
187
+ */
188
+ export function validateRule(toolPattern, ruleId) {
189
+ const raw = (toolPattern ?? '').trim();
190
+ if (!raw)
191
+ return 'empty toolPattern';
192
+ // Anchors are meaningless once wrapped for token matching, and are the most
193
+ // common thing a SOP author writes out of habit.
194
+ const stripped = stripEnd(stripStart(raw, '^'), '$');
195
+ if (!stripped)
196
+ return 'toolPattern was only anchors';
197
+ const wrapped = ` (${stripped}) `;
198
+ try {
199
+ assertPortableEre(wrapped, ruleId);
200
+ }
201
+ catch (err) {
202
+ return `not portable across grep and JS — ${err instanceof Error ? err.message : String(err)}`;
203
+ }
204
+ let re;
205
+ try {
206
+ re = new RegExp(wrapped);
207
+ }
208
+ catch {
209
+ return 'does not compile';
210
+ }
211
+ // A threshold rather than "matches all of them". `[A-Za-z]*` matches ten of
212
+ // these fourteen — it misses only the four with an underscore — and shipping
213
+ // it to a blocking path would stop essentially every tool call. Requiring a
214
+ // clean sweep would have let it through on a technicality.
215
+ //
216
+ // The ratio is 0.7, not 0.8, and that is a deliberate M3 adjustment, not a
217
+ // drive-by tweak. Before M3, CANARY_TOOLS held 12 entries, 2 of them
218
+ // underscored (`run_command`, `str_replace_editor`), so `[A-Za-z]*` hit 10 of
219
+ // 12 (83%) — above an 0.8 threshold, correctly rejected. M3 added two more
220
+ // *underscored* canaries (`mcp__github__create_issue`,
221
+ // `mcp__filesystem__read_file`, both required by name — see CANARY_TOOLS)
222
+ // without adding any non-underscored ones, so the same `[A-Za-z]*` pattern
223
+ // now hits 10 of 14 (71%): still every alphabetic-only tool name there is,
224
+ // but under an unchanged 0.8 threshold that stops being "a catch-all" and
225
+ // starts being "accepted" — silently weakening a case
226
+ // `harnessProtectedPaths`... `policySnapshot.test.ts` pins by name. Lowering
227
+ // the ratio to 0.7 keeps the absolute hit-count threshold at 10 (`Math.ceil`
228
+ // of both 12*0.8 and 14*0.7), so `[A-Za-z]*` is rejected exactly as before —
229
+ // the ratio moved so the THRESHOLD would not.
230
+ const hits = CANARY_TOOLS.filter((t) => re.test(` ${t} `)).length;
231
+ const limit = Math.ceil(CANARY_TOOLS.length * 0.7);
232
+ if (hits >= limit) {
233
+ return (`matches ${hits} of ${CANARY_TOOLS.length} common tool names — this is a ` +
234
+ `catch-all and would block the workspace`);
235
+ }
236
+ return null;
237
+ }
238
+ /** Converts one resolved SOP rule into a gate pattern, or null if unusable. */
239
+ function toGuardPattern(rule, shadow) {
240
+ // Only `block`. The control plane also emits `{toolPattern: '.*', action:
241
+ // 'warn'}` for every HIGH/CRITICAL-risk SOP (evaluate.ts) — a live landmine
242
+ // for anything that ships rules to a blocking path, and the reason this filter
243
+ // is the first line rather than an afterthought.
244
+ //
245
+ // One exception (LLD #71): a rule the control plane projects from a cited
246
+ // policy guardrail in SHADOW carries `origin: 'guardrail'` and `action:
247
+ // 'warn'`. It ships at severity `warn` — the gate logs `tool_flagged` and
248
+ // allows — so the rule can earn its evidence. That is the documented meaning
249
+ // of `warn` ("has not earned the right to block yet"), and it is still
250
+ // additive: a warn rule blocks nothing. `validateRule` below still applies,
251
+ // so a catch-all guardrail is refused the same way.
252
+ //
253
+ // And `require_approval` (a `REQUIRE_APPROVAL:` SOP title): shipped at
254
+ // severity `hold` since gate body v8. Refuses like block, records the call
255
+ // for review, and an approved bypass lets the exact call through once.
256
+ const guardrail = rule.origin === 'guardrail';
257
+ const hold = rule.action === 'require_approval';
258
+ if (rule.action !== 'block' && !hold && !(guardrail && rule.action === 'warn'))
259
+ return null;
260
+ const why = validateRule(rule.toolPattern, rule.id);
261
+ if (why) {
262
+ log.warn({ action: 'policy_rule_rejected', ruleId: rule.id, toolPattern: rule.toolPattern, reason: why }, `Policy rule rejected and NOT shipped to the gate: ${why}`);
263
+ return null;
264
+ }
265
+ // The ` WHERE ` clause travels with the rule. It is validated only for JS
266
+ // compilability — it is a JS regex matched against serialized tool input,
267
+ // never a grep pattern, so `validateRule`'s portable-ERE discipline does not
268
+ // apply to it. One that does not compile is stripped (the rule ships
269
+ // name-only, today's behaviour) and logged, NOT dropped with its rule: the
270
+ // clause narrows a block, so losing the clause must widen enforcement, and
271
+ // losing the rule would open it.
272
+ let argPattern;
273
+ if (rule.argPattern) {
274
+ try {
275
+ new RegExp(rule.argPattern);
276
+ argPattern = rule.argPattern;
277
+ }
278
+ catch {
279
+ log.warn({ action: 'policy_rule_arg_pattern_dropped', ruleId: rule.id, argPattern: rule.argPattern }, 'argPattern does not compile as a JS RegExp — rule shipped name-only');
280
+ }
281
+ }
282
+ const stripped = stripEnd(stripStart(rule.toolPattern.trim(), '^'), '$');
283
+ return {
284
+ id: `sop.${rule.id}`,
285
+ // Wrapped in spaces so the pattern matches a whole tool token: the gate
286
+ // tests against a space-padded string, so ` (Bash) ` matches the tool `Bash`
287
+ // and not a tool called `BashHistory`.
288
+ source: ` (${stripped}) `,
289
+ subject: 'tool',
290
+ // `shadow`, not `warn`. Both allow the call, but they mean different
291
+ // things: `warn` is "this rule has not earned the right to block yet",
292
+ // `shadow` is "this rule is certain and the workspace asked us not to act".
293
+ // Collapsing them makes a SILENT_LOG rollout unmeasurable, because you
294
+ // cannot tell which flags would have been blocks. A guardrail in SHADOW
295
+ // is the one rule that ships as `warn` on purpose (see above); under
296
+ // SILENT_LOG it demotes to `shadow` with everything else.
297
+ severity: shadow
298
+ ? 'shadow'
299
+ : hold
300
+ ? 'hold'
301
+ : rule.action === 'warn'
302
+ ? 'warn'
303
+ : 'block',
304
+ reason: hold
305
+ ? `Held for human review: ${rule.reason || `SOP ${rule.id}`}`
306
+ : rule.reason || `Blocked by SOP ${rule.id}`,
307
+ rationale: guardrail
308
+ ? 'Projected from a cited policy guardrail by the control plane (LLD #71).'
309
+ : hold
310
+ ? 'Resolved from a REQUIRE_APPROVAL: SOP title by the control plane.'
311
+ : 'Resolved from a BLOCK: SOP title by the control plane.',
312
+ matches: [],
313
+ notMatches: [],
314
+ ...(argPattern ? { argPattern } : {}),
315
+ };
316
+ }
317
+ /** Fetches resolved policy for the workspace. Returns null on any failure —
318
+ * the caller keeps the previous snapshot rather than replacing it with nothing. */
319
+ export async function fetchResolvedPolicy(opts) {
320
+ const url = `${stripEnd(opts.controlPlaneUrl, '/')}/api/v1/policy/resolve` +
321
+ `?workspaceId=${encodeURIComponent(opts.workspaceId)}`;
322
+ try {
323
+ const res = await fetch(url, {
324
+ headers: { Authorization: `Bearer ${opts.apiKey}` },
325
+ signal: AbortSignal.timeout(10_000),
326
+ });
327
+ if (!res.ok) {
328
+ log.warn({ action: 'policy_fetch_failed', status: res.status }, 'Policy resolve returned non-OK');
329
+ return null;
330
+ }
331
+ const body = (await res.json());
332
+ if (typeof body !== 'object' || body === null)
333
+ return null;
334
+ const rec = body;
335
+ const rules = Array.isArray(rec.sopRules) ? rec.sopRules : [];
336
+ return {
337
+ workspaceId: typeof rec.workspaceId === 'string' ? rec.workspaceId : opts.workspaceId,
338
+ interventionMode: typeof rec.interventionMode === 'string' ? rec.interventionMode : 'TRANSPARENT',
339
+ sopRules: rules.filter((r) => {
340
+ if (typeof r !== 'object' || r === null)
341
+ return false;
342
+ const x = r;
343
+ return (typeof x.id === 'string' &&
344
+ typeof x.toolPattern === 'string' &&
345
+ typeof x.action === 'string' &&
346
+ // Optional, but if present it must be a string — a non-string here
347
+ // would flow into `new RegExp` and the base64 encoder downstream.
348
+ (x.argPattern === undefined || typeof x.argPattern === 'string'));
349
+ }),
350
+ // `allowedServers` — the exact field name `evaluate.ts` and
351
+ // `lib/mcpCuration.ts` both use. Absent or wrongly-typed degrades to
352
+ // `[]`, same as `sopRules` above: unrestricted, not a fetch failure.
353
+ mcpAllowedServers: Array.isArray(rec.allowedServers)
354
+ ? rec.allowedServers.filter((s) => typeof s === 'string')
355
+ : [],
356
+ sqlDropStrictBlock: rec.sqlDropStrictBlock === true,
357
+ };
358
+ }
359
+ catch (err) {
360
+ log.warn({ action: 'policy_fetch_failed', err }, 'Policy resolve unreachable');
361
+ return null;
362
+ }
363
+ }
364
+ /**
365
+ * SILENT_LOG means observe, do not intervene (docs/guides/policies.md: the
366
+ * call is permitted to run, the trace is tagged for audit). It demotes the
367
+ * dynamic tier to the advisory severity — it does NOT reach the static floor,
368
+ * which stays enforced. If one settings string could disarm the floor, the
369
+ * floor would not be a floor.
370
+ *
371
+ * This used to compare against 'SHADOW', a value intervention_mode_type
372
+ * (TRANSPARENT|OPAQUE|SILENT_LOG — packages/db/src/enums.ts) can never
373
+ * produce, so the observe-only branch was dead and a SILENT_LOG workspace
374
+ * shipped fully-blocking snapshots.
375
+ *
376
+ * Extracted so `buildSnapshotRules` and `writePolicySnapshot`'s `#mcpservers`
377
+ * header compute "is this workspace observe-only" the same way once, rather
378
+ * than as two copies of the comparison that could drift.
379
+ */
380
+ function isSilentLogMode(policy) {
381
+ return policy.interventionMode.toUpperCase() === 'SILENT_LOG';
382
+ }
383
+ /**
384
+ * Drops MCP server names that would corrupt the comma-joined `#mcpservers`
385
+ * `.rules` header line — a name carrying a comma would be misread as two
386
+ * server names, and a tab or other whitespace would collide with the
387
+ * `.rules` file's own column separator the moment anyone looked at the line
388
+ * next to a rule row. Logged, not silently dropped: a server an operator
389
+ * configured and then watched vanish from enforcement needs to know why,
390
+ * the same discipline `validateRule` follows for a rejected SOP pattern.
391
+ */
392
+ function sanitizeMcpServerNames(names) {
393
+ const out = [];
394
+ // Defensive against a caller that built a ResolvedPolicy by hand without
395
+ // TypeScript actually checking it — this repo's test files are outside
396
+ // `tsconfig.json`'s `include`, so `tsc --noEmit` does not catch a test
397
+ // constructing one with `mcpAllowedServers` omitted, and a JS consumer of
398
+ // this exported function is not checked at all. `Array.isArray` costs
399
+ // nothing on the real path, where `fetchResolvedPolicy` already guarantees
400
+ // an array.
401
+ if (!Array.isArray(names))
402
+ return out;
403
+ for (const raw of names) {
404
+ if (typeof raw !== 'string')
405
+ continue;
406
+ const name = raw.trim();
407
+ if (!name)
408
+ continue;
409
+ if (/[\s,]/.test(name)) {
410
+ log.warn({ action: 'mcp_server_name_rejected', name: raw }, 'MCP server name contains whitespace or a comma — dropped rather than corrupting the .rules #mcpservers header');
411
+ continue;
412
+ }
413
+ out.push(name);
414
+ }
415
+ return out;
416
+ }
417
+ /** Builds the rule set a snapshot would carry, without writing it. Exported so
418
+ * a test can assert the contents rather than re-deriving them. */
419
+ export function buildSnapshotRules(policy, localHoldTokens = []) {
420
+ // `shadow` names the advisory GuardPattern severity these rules are demoted
421
+ // to; the workspace-level trigger is intervention mode SILENT_LOG.
422
+ const shadow = isSilentLogMode(policy);
423
+ const sopRules = policy.sopRules
424
+ .map((r) => toGuardPattern(r, shadow))
425
+ .filter((r) => r !== null);
426
+ const localHolds = [...new Set(localHoldTokens.map((t) => t.trim()).filter(Boolean))]
427
+ .map((t) => localHoldPattern(t, shadow))
428
+ .filter((r) => r !== null);
429
+ const destructive = DESTRUCTIVE_COMMAND_PATTERNS.map((p) => {
430
+ // Wave 7 (audit-remediation): `destructive.sql_drop` is a per-rule
431
+ // override, evaluated BEFORE the tier-wide ramp below — it was never
432
+ // part of `DESTRUCTIVE_TIER_SEVERITY`'s promotion (its own static
433
+ // `severity` is `'warn'`, not `'block'`, so that ternary never reaches
434
+ // it), and this flag is opt-in per workspace, not evidence-gated the way
435
+ // the six-pattern ramp is. Shadow mode still wins over both: SILENT_LOG
436
+ // means observe-only across the whole dynamic tier, no exceptions.
437
+ if (p.id === 'destructive.sql_drop' && !shadow && policy.sqlDropStrictBlock) {
438
+ return { ...p, severity: 'block' };
439
+ }
440
+ return {
441
+ ...p,
442
+ // A `warn` pattern stays `warn` regardless; only the `block` ones are held
443
+ // back by the tier gate.
444
+ severity: shadow
445
+ ? 'shadow'
446
+ : p.severity === 'block'
447
+ ? DESTRUCTIVE_TIER_SEVERITY
448
+ : 'warn',
449
+ };
450
+ });
451
+ // The snapshot-delivered promotion of the skill-directory-write floor rules
452
+ // to `block` — see SKILL_SURFACE_TIER_SEVERITY. `.tier` suffixed onto the
453
+ // floor's own id (`skill_surface.agents_skills_write` ->
454
+ // `skill_surface.agents_skills_write.tier`) so this entry can never collide
455
+ // with `staticFloorPatterns()`'s compiled-in copy of the same pattern: both
456
+ // are meant to be present and matching the same path at once, one warn (the
457
+ // degraded-mode baseline) and one block (the steady state), and
458
+ // `assertGuardTableSane`'s uniqueness check would reject an accidental
459
+ // duplicate id the moment either table tried to load. Mirrors the
460
+ // destructive tier's own shadow handling: SILENT_LOG means observe, don't
461
+ // act, on the dynamic tier only — the floor's warn rule is unaffected
462
+ // either way.
463
+ const skillSurface = SKILL_SURFACE_PATTERNS.map((p) => ({
464
+ ...p,
465
+ id: `${p.id}.tier`,
466
+ severity: shadow ? 'shadow' : SKILL_SURFACE_TIER_SEVERITY,
467
+ }));
468
+ return [...sopRules, ...localHolds, ...destructive, ...skillSurface];
469
+ }
470
+ /**
471
+ * A local `review_before:` token as a hold rule. `action:*` tokens match the
472
+ * gate's action subject (the tokens it classifies a shell command to);
473
+ * anything else is a tool name. Case-insensitive, as the bespoke Claude Code
474
+ * hold was. A token that is not a plain identifier is refused with a log line
475
+ * rather than escaped into a regex nobody can read back.
476
+ */
477
+ function localHoldPattern(token, shadow) {
478
+ if (!/^[A-Za-z0-9_:.-]+$/.test(token)) {
479
+ log.warn({ action: 'local_hold_token_rejected', token }, 'review_before token is not a plain identifier — not shipped to the gate');
480
+ return null;
481
+ }
482
+ // The identifier check above already excludes every metacharacter; the
483
+ // full escape is still applied so the pattern is safe by construction and
484
+ // not by the guard three lines up.
485
+ const source = ` (${token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}) `;
486
+ return {
487
+ id: `sop.local.review_before.${token}`,
488
+ source,
489
+ subject: token.startsWith('action:') ? 'action' : 'tool',
490
+ ignoreCase: true,
491
+ severity: shadow ? 'shadow' : 'hold',
492
+ reason: `Held for human review: ${token} — declared in review_before:`,
493
+ rationale: 'A local SOP or the workspace settings asked to see this action before it runs.',
494
+ matches: [],
495
+ notMatches: [],
496
+ };
497
+ }
498
+ /**
499
+ * Writes the snapshot atomically.
500
+ *
501
+ * Two artifacts from one source: the canonical JSON, and a tab-separated
502
+ * `.rules` projection so the five bash gates need no JSON parser on the decision
503
+ * path. Both carry the same digest, so a test — and the gate — can tell that the
504
+ * projection really came from the JSON.
505
+ */
506
+ export async function writePolicySnapshot(policy, snapshotDir = DEFAULT_SNAPSHOT_DIR, localHoldTokens = []) {
507
+ const rules = buildSnapshotRules(policy, localHoldTokens);
508
+ const lines = rules.map(toRulesLine);
509
+ const digest = createHash('sha256').update(lines.join('\n')).digest('hex').slice(0, 32);
510
+ // One timestamp shared by both artifacts. Two `new Date()` calls would put
511
+ // different values in the JSON and the .rules, so a reader comparing them
512
+ // would see drift that is not there.
513
+ const generatedAt = new Date().toISOString();
514
+ // M3: the per-server MCP allowlist, sanitised once and reused for both
515
+ // artifacts so the JSON and the `.rules` header can never disagree about
516
+ // which names survived. Severity follows SILENT_LOG the same way the
517
+ // dynamic tier's rules do: certain, just not acted on.
518
+ //
519
+ // Deliberately OUTSIDE `lines`/`digest` above — the digest covers RULE lines
520
+ // only, matching the trust model the `#workspace` header already has (parsed
521
+ // and integrity-checked by workspace-id comparison, not by the digest). A
522
+ // `#mcpservers` header is the same kind of metadata line, not a rule, so it
523
+ // must not change what `lines.join('\n')` hashes to.
524
+ const mcpServers = sanitizeMcpServerNames(policy.mcpAllowedServers);
525
+ const mcpSeverity = isSilentLogMode(policy) ? 'shadow' : 'block';
526
+ await fs.mkdir(snapshotDir, { recursive: true });
527
+ const json = JSON.stringify({
528
+ _comment: 'Intutic policy snapshot — auto-generated. DO NOT EDIT.',
529
+ version: 1,
530
+ /**
531
+ * Which emitted evaluator these rules were compiled for.
532
+ *
533
+ * The `.rules` column layout and the normalisation contract are part of
534
+ * the gate body, not of this file, so a snapshot is only meaningful to a
535
+ * gate of the same generation. Recording it here is what lets a support
536
+ * conversation start from "which gate wrote this" instead of guessing —
537
+ * and it is the reason `GATE_VERSION` exists at all rather than being a
538
+ * constant nothing reads.
539
+ */
540
+ gateVersion: GATE_VERSION,
541
+ workspaceId: policy.workspaceId,
542
+ generatedAt,
543
+ interventionMode: policy.interventionMode,
544
+ digest,
545
+ // M3: sanitised, not `policy.mcpAllowedServers` verbatim — a name this
546
+ // module rejected for the `.rules` header must not silently survive in
547
+ // the JSON, or the two artifacts would disagree about what is enforced.
548
+ // Empty means unrestricted, same convention as `allowedServers` at the
549
+ // control plane (`readMcpCurationSettings`).
550
+ mcpAllowedServers: mcpServers,
551
+ /**
552
+ * The resolve response verbatim, alongside the gate projection below.
553
+ *
554
+ * These two are **not** interchangeable and the difference has teeth.
555
+ * `rules` is a *gate artifact*: patterns rewritten into space-padded EREs,
556
+ * `warn` and `require_approval` already discarded, ids prefixed `sop.`.
557
+ * `sopRules` is what the control plane actually said — `{id, toolPattern,
558
+ * action, reason}`, every action.
559
+ *
560
+ * Carrying both exists because the MCP proxy's policy cache consumes
561
+ * `{toolPattern, action}` and its `isSopRule` guard drops anything else.
562
+ * Feeding it `rules` would pass the outer parse and then silently yield
563
+ * *zero* enforceable rules — a cache that reports entries and enforces
564
+ * nothing, which is worse than the cold fetch it replaced. This field is
565
+ * what makes seeding safe; see `seedFromSnapshot` in policyCache.ts.
566
+ */
567
+ sopRules: policy.sopRules,
568
+ rules: rules.map((r) => ({
569
+ id: r.id,
570
+ source: r.source,
571
+ subject: r.subject ?? 'any',
572
+ severity: r.severity,
573
+ ignoreCase: r.ignoreCase === true,
574
+ reason: r.reason,
575
+ // Plain text here; base64 only in the `.rules` projection, where a tab
576
+ // separator forces the encoding. JSON needs no such armour.
577
+ ...(r.argPattern ? { argPattern: r.argPattern } : {}),
578
+ })),
579
+ }, null, 2);
580
+ // `#generated` is not decoration. Without it the five bash gates and the
581
+ // Python one physically cannot compute the snapshot's age from the file they
582
+ // read — only the JSON carries a timestamp, and they never open the JSON. A
583
+ // snapshot from last year enforced identically to one written a second ago,
584
+ // and nothing anywhere could say so.
585
+ // `#mcpservers <severity> <comma-joined-server-names>` — OMITTED entirely
586
+ // when the (sanitised) list is empty. This is the mandatory-mechanism/
587
+ // opt-in-effect split M3 is built on: the header-parsing and per-call
588
+ // allowlist check ship to every gate unconditionally, but they are a no-op
589
+ // until a workspace actually configures `mcpAllowedServers` — and "no
590
+ // header line" is how a v6 gate (and, for forward-compat, a hypothetical
591
+ // v5-reading-a-v6-file) tells "unrestricted" apart from "restricted to
592
+ // zero servers", which would otherwise block every MCP call by omission.
593
+ const mcpHeader = mcpServers.length > 0 ? `#mcpservers ${mcpSeverity} ${mcpServers.join(',')}\n` : '';
594
+ const rulesText = `# Intutic policy snapshot (projection of ${SNAPSHOT_JSON}) — DO NOT EDIT.\n` +
595
+ `# Columns: ${RULES_COLUMNS.join('\t')}\n` +
596
+ `#digest ${digest}\n` +
597
+ `#workspace ${policy.workspaceId}\n` +
598
+ `#generated ${generatedAt}\n` +
599
+ mcpHeader +
600
+ lines.join('\n') +
601
+ '\n';
602
+ await writeAtomic(path.join(snapshotDir, SNAPSHOT_JSON), json);
603
+ await writeAtomic(path.join(snapshotDir, SNAPSHOT_RULES), rulesText);
604
+ log.debug({ action: 'policy_snapshot_written', digest, ruleCount: rules.length, dir: snapshotDir }, 'Policy snapshot refreshed');
605
+ return { digest, ruleCount: rules.length };
606
+ }
607
+ async function writeAtomic(target, content) {
608
+ const tmp = target + '.tmp';
609
+ await fs.writeFile(tmp, content, { encoding: 'utf-8' });
610
+ // 0444: the daemon replaces this by rename, so it never needs write access to
611
+ // the file itself, and a read-only file is one more small obstacle to an agent
612
+ // editing it in place. The real protection is that every gate refuses to touch
613
+ // `.intutic/hooks` at all.
614
+ await fs.chmod(tmp, 0o444);
615
+ await fs.rename(tmp, target);
616
+ }
617
+ /**
618
+ * Fetch and write in one call. Safe to run on every sync cycle.
619
+ *
620
+ * Never throws: policy refresh must not be able to take down the sync loop. A
621
+ * failed fetch leaves the previous snapshot in place, which is the correct
622
+ * degradation — stale rules stay enforced. If they expired into permissiveness,
623
+ * "kill the daemon and wait" would be a supported way to disarm governance.
624
+ */
625
+ export async function refreshPolicySnapshot(opts) {
626
+ const policy = await fetchResolvedPolicy(opts);
627
+ if (!policy)
628
+ return null;
629
+ try {
630
+ return await writePolicySnapshot(policy, opts.snapshotDir ?? DEFAULT_SNAPSHOT_DIR, opts.localHoldTokens ?? []);
631
+ }
632
+ catch (err) {
633
+ log.warn({ action: 'policy_snapshot_write_failed', err }, 'Could not write policy snapshot');
634
+ return null;
635
+ }
636
+ }
637
+ //# sourceMappingURL=policySnapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policySnapshot.js","sourceRoot":"","sources":["../../src/lib/policySnapshot.ts"],"names":[],"mappings":"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"}