@highflame/policy 2.1.0 → 2.1.2

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.
@@ -1,210 +1,285 @@
1
- // Overwatch (Guardian) Cedar Schema
1
+ // Overwatch Cedar Schema
2
2
  // ===================================
3
- // IDE Security & Policy Enforcement
3
+ // IDE Agent Security & Policy Enforcement
4
4
  //
5
- // Overwatch protects IDE operations (prompts, tool calls, file access) by evaluating
6
- // threats detected by YARA and Javelin scanners against Cedar policies.
5
+ // Overwatch protects IDE agent operations (prompts, tool calls, file access, MCP connections)
6
+ // by evaluating threats detected by the detection engine pipeline against Cedar policies.
7
7
  //
8
8
  // Architecture:
9
- // User/Agent → IDE Hook → YARA/Javelin → Cedar Policy → Allow/Deny
9
+ // User/Agent → IDE Hook → Detection Engine → Cedar Policy → Allow/Deny
10
10
  //
11
11
  // Supported IDEs:
12
12
  // - Cursor (beforeSubmitPrompt, beforeShellExecution, beforeMCPExecution, etc.)
13
13
  // - Claude Code (UserPromptSubmit, PreToolUse)
14
14
  // - GitHub Copilot (userPromptSubmitted, preToolUse)
15
+ //
16
+ // Threat Coverage:
17
+ // - OWASP Top 10 for LLM Applications 2025 (LLM01-LLM10)
18
+ // - OWASP Top 10 for Agentic Applications (ASI01-ASI10)
19
+ // - OWASP MCP Top 10 (MCP01-MCP05)
20
+ // - MITRE ATLAS Agent Techniques (AML.T0051, AML.T0080-T0082)
15
21
 
16
22
  namespace Overwatch {
17
23
 
18
24
  // =============================================================================
19
- // ENTITIES - Organization Hierarchy (ReBAC)
25
+ // ENTITIES - Tenant Hierarchy (ReBAC)
20
26
  // =============================================================================
27
+ // Aligned with Guardrails entity hierarchy (Account → Project).
28
+ // Overwatch does not have app-specific policies, so App is omitted.
29
+ //
30
+ // Entity hierarchy enables Cedar's `in` operator for policy scoping:
31
+ // Account (org root)
32
+ // └── Project in [Account]
33
+ // └── Tool/Server/FilePath/LlmPrompt in [Project]
34
+ //
35
+ // Policy scoping examples:
36
+ // resource == Overwatch::Tool::"shell" → specific tool
37
+ // resource in Overwatch::Project::"<uuid>" → project-wide
38
+ // resource in Overwatch::Account::"<uuid>" → org-wide
21
39
 
22
- // Top-level organization for multi-tenant policy enforcement
23
- // Enables policies like: principal in Overwatch::Organization::"acme-corp"
24
- entity Organization {
25
- name: String, // "Acme Corp", "Highflame"
26
- };
40
+ /// Account represents an organization (top-level tenant)
41
+ entity Account;
27
42
 
28
- // Team within an organization
29
- // Enables policies like: principal in Overwatch::Team::"security-team"
30
- entity Team in [Organization] {
31
- name: String, // "security", "engineering", "devops"
32
- };
43
+ /// Project represents a project within an account
44
+ entity Project in [Account];
33
45
 
34
46
  // =============================================================================
35
47
  // ENTITIES - Principals
36
48
  // =============================================================================
37
49
 
38
- // Human user or service account making requests to the IDE
39
- entity User in [Team] {
40
- user_type: String, // "external" or "internal"
41
- email: String, // User email (optional)
42
- };
50
+ /// Human user or service account making requests to the IDE
51
+ entity User;
43
52
 
44
- // AI agent (Claude, GitHub Copilot, etc.)
45
- entity Agent in [Team] {
46
- agent_type: String, // "claude", "copilot", etc.
47
- };
53
+ /// AI agent (Claude, GitHub Copilot, etc.)
54
+ entity Agent;
48
55
 
49
- // LLM prompt or session
50
- entity LlmPrompt {
51
- prompt_type: String, // "user_prompt", "session"
52
- };
56
+ // =============================================================================
57
+ // ENTITIES - Resources (scoped under Project)
58
+ // =============================================================================
53
59
 
54
- // MCP tool or native IDE tool
55
- entity Tool {
56
- tool_name: String, // "shell", "read_file", "playwright", etc.
57
- risk_level: String, // "low", "medium", "high"
58
- };
60
+ /// LLM prompt or session resource for process_prompt action
61
+ entity LlmPrompt in [Project];
59
62
 
60
- // MCP server
61
- entity Server {
62
- server_name: String, // "filesystem", "playwright", etc.
63
- };
63
+ /// MCP tool or native IDE tool — resource for call_tool action
64
+ entity Tool in [Project];
64
65
 
65
- // File system path
66
- entity FilePath {
67
- path: String,
68
- is_within_workspace: Bool,
69
- };
66
+ /// MCP server — resource for connect_server action
67
+ entity Server in [Project];
68
+
69
+ /// File system path — resource for read_file/write_file/call_tool actions
70
+ entity FilePath in [Project];
70
71
 
71
72
  // =============================================================================
72
73
  // ACTIONS
73
74
  // =============================================================================
74
75
 
75
76
  // User submits a prompt or receives AI response
77
+ // Threat focus: injection, jailbreak, secrets, PII, content safety, invisible chars
76
78
  action process_prompt appliesTo {
77
79
  principal: [User, Agent],
78
80
  resource: [LlmPrompt],
79
81
  context: {
80
- // Event & Source
81
- content: String, // Raw content being scanned
82
- source: String, // IDE source: "cursor", "claudecode", "github_copilot"
83
- event: String, // Hook event name
84
- user_email: String, // User identifier
85
-
86
- // Workspace
87
- cwd?: String, // Current working directory
88
- workspace_root?: String, // Workspace/repository root
89
-
90
- // Threat Detection
91
- threat_count: Long, // Total threats detected
92
- highest_severity: String, // "critical", "high", "medium", "low"
93
- threat_categories: Set<String>, // Threat category names
94
- yara_threats: Set<String>, // YARA rule names
95
- max_threat_severity: Long, // Numeric severity (0-4)
96
- contains_secrets: Bool, // Whether secrets detected
97
- prompt_text?: String, // Same as content (legacy)
98
- response_content?: String, // Response content (if available)
99
-
100
- // Trust/Safety Scores (0-100, from Javelin/Lakera/LlamaGuard classifiers)
101
- // Required: content safety classifiers always run for prompt processing
102
- violence_score: Long, // Violence content detection score
103
- weapons_score: Long, // Weapons content detection score
104
- hate_speech_score: Long, // Hate speech detection score
105
- crime_score: Long, // Criminal content detection score
106
- sexual_score: Long, // Sexual content detection score
107
- profanity_score: Long, // Profanity detection score
108
-
109
- // Detector Confidence Scores (0-100, ML classifier confidence)
110
- // Required: ML classifiers always run for prompt processing
111
- pii_confidence: Long, // PII detection confidence
112
- injection_confidence: Long, // Prompt injection confidence
113
- jailbreak_confidence: Long, // Jailbreak detection confidence
114
-
115
- // Agent Security (0-100)
116
- // Required: agent security scanners always run for prompt processing
117
- indirect_injection_score: Long, // Indirect prompt injection risk
82
+ // --- Event & Source ---
83
+ content: String, // Raw content being scanned
84
+ source: String, // IDE source: "cursor", "claudecode", "github_copilot"
85
+ event: String, // Hook event name
86
+ user_email: String, // User identifier
87
+
88
+ // --- Workspace ---
89
+ cwd?: String, // Current working directory
90
+ workspace_root?: String, // Workspace/repository root
91
+
92
+ // --- Threat Detection (from detection engine pipeline) ---
93
+ threat_count: Long, // Total threats detected
94
+ highest_severity: String, // "critical", "high", "medium", "low", "none"
95
+ threat_categories: Set<String>, // Threat category names
96
+ detected_threats: Set<String>, // Detection rule names that matched
97
+ max_threat_severity: Long, // Numeric severity (0=none, 1=low, 2=medium, 3=high, 4=critical)
98
+ contains_secrets: Bool, // Whether secrets/credentials detected
99
+
100
+ // --- Secrets (granular) ---
101
+ secret_types?: Set<String>, // Types: "aws_access_key", "github_token", "ssh_private_key", etc.
102
+ secret_count?: Long, // Number of distinct secrets found
103
+
104
+ // --- PII Detection ---
105
+ pii_detected?: Bool, // Whether any PII patterns matched
106
+ pii_types?: Set<String>, // Types: "ssn", "credit_card", "email", "phone", etc.
107
+ pii_count?: Long, // Number of PII matches
108
+
109
+ // --- Encoding & Unicode Attacks ---
110
+ contains_invisible_chars?: Bool, // Zero-width chars, bidi overrides, tag chars detected
111
+ invisible_chars_score?: Long, // Unicode attack severity (0-100)
112
+
113
+ // --- Content Safety Scores (0-100, from ML classifiers) ---
114
+ violence_score: Long,
115
+ weapons_score: Long,
116
+ hate_speech_score: Long,
117
+ crime_score: Long,
118
+ sexual_score: Long,
119
+ profanity_score: Long,
120
+
121
+ // --- ML Detector Confidence Scores (0-100) ---
122
+ pii_confidence: Long, // PII detection classifier confidence
123
+ injection_confidence: Long, // Prompt injection classifier confidence
124
+ jailbreak_confidence: Long, // Jailbreak detection classifier confidence
125
+
126
+ // --- Agent Security (0-100) ---
127
+ indirect_injection_score: Long, // Indirect prompt injection risk (OWASP LLM01, ASI01)
128
+
129
+ // --- Session Detection History (cross-turn sticky flags) ---
130
+ session_pii_detected?: Bool,
131
+ session_pii_types?: Set<String>,
132
+ session_secrets_detected?: Bool,
133
+ session_secret_types?: Set<String>,
134
+ session_injection_detected?: Bool,
135
+ session_command_injection?: Bool,
136
+ session_threat_turns?: Long,
137
+
138
+ // --- Legacy ---
139
+ prompt_text?: String, // Same as content (backward compatibility)
140
+ response_content?: String, // Response content (if available)
118
141
  },
119
142
  };
120
143
 
121
144
  // User calls a tool (native IDE tool or MCP tool)
145
+ // Threat focus: command injection, tool poisoning, rug pull, data exfiltration, loops
122
146
  action call_tool appliesTo {
123
147
  principal: [User, Agent],
124
148
  resource: [Tool, FilePath],
125
149
  context: {
126
- // Event & Source
127
- content: String, // Raw content being scanned (e.g., shell command)
128
- source: String, // IDE source
129
- event: String, // Hook event name
130
- user_email: String, // User identifier
150
+ // --- Event & Source ---
151
+ content: String, // Raw content being scanned (e.g., shell command, tool args)
152
+ source: String, // IDE source
153
+ event: String, // Hook event name
154
+ user_email: String, // User identifier
131
155
 
132
- // Tool & MCP
133
- tool_name?: String, // Normalized tool name ("shell", "read_file", etc.)
134
- mcp_server?: String, // MCP server name
135
- mcp_tool?: String, // MCP tool name
156
+ // --- Tool & MCP ---
157
+ tool_name?: String, // Normalized tool name ("shell", "read_file", etc.)
158
+ mcp_server?: String, // MCP server name
159
+ mcp_tool?: String, // MCP tool name
136
160
 
137
- // File & Path
138
- path?: String, // File path (if file operation)
161
+ // --- File & Path ---
162
+ path?: String, // File path (if file operation)
139
163
 
140
- // Workspace
164
+ // --- Workspace ---
141
165
  cwd?: String,
142
166
  workspace_root?: String,
143
167
 
144
- // Threat Detection (optional: scanning may not have run before tool call)
168
+ // --- Threat Detection ---
145
169
  threat_count?: Long,
146
170
  highest_severity?: String,
147
171
  threat_categories?: Set<String>,
148
- yara_threats?: Set<String>,
172
+ detected_threats?: Set<String>,
149
173
  max_threat_severity?: Long,
150
174
  contains_secrets?: Bool,
151
- response_content?: String,
152
175
 
153
- // Trust/Safety Scores (0-100, from Javelin/Lakera/LlamaGuard classifiers)
154
- // Optional: only present when trust/safety classifiers have run
155
- violence_score?: Long, // Violence content detection score
156
- weapons_score?: Long, // Weapons content detection score
157
- hate_speech_score?: Long, // Hate speech detection score
158
- crime_score?: Long, // Criminal content detection score
159
- sexual_score?: Long, // Sexual content detection score
160
- profanity_score?: Long, // Profanity detection score
161
-
162
- // Detector Confidence Scores (0-100, ML classifier confidence)
163
- // Optional: only present when ML classifiers have run
164
- pii_confidence?: Long, // PII detection confidence
165
- injection_confidence?: Long, // Prompt injection confidence
166
- jailbreak_confidence?: Long, // Jailbreak detection confidence
167
-
168
- // Agent Security (0-100)
169
- // Optional: only present when agent security scanners have run
170
- tool_poisoning_score?: Long, // Tool description manipulation risk
171
- rug_pull_score?: Long, // Tool behavior mismatch risk
172
- indirect_injection_score?: Long, // Indirect prompt injection risk
173
-
174
- // MCP Trust
175
- // Optional: only present when MCP server verification has run
176
- mcp_server_verified?: Bool, // Whether server is from verified registry
176
+ // --- Secrets (granular) ---
177
+ secret_types?: Set<String>,
178
+ secret_count?: Long,
179
+
180
+ // --- PII Detection ---
181
+ pii_detected?: Bool,
182
+ pii_types?: Set<String>,
183
+ pii_count?: Long,
184
+
185
+ // --- Encoding & Unicode Attacks ---
186
+ contains_invisible_chars?: Bool,
187
+ invisible_chars_score?: Long,
188
+
189
+ // --- Content Safety Scores (0-100) ---
190
+ violence_score?: Long,
191
+ weapons_score?: Long,
192
+ hate_speech_score?: Long,
193
+ crime_score?: Long,
194
+ sexual_score?: Long,
195
+ profanity_score?: Long,
196
+
197
+ // --- ML Detector Confidence Scores (0-100) ---
198
+ pii_confidence?: Long,
199
+ injection_confidence?: Long,
200
+ jailbreak_confidence?: Long,
201
+
202
+ // --- Agent Security (0-100) --- (OWASP ASI01, ASI02, ASI04; MITRE AML.T0051)
203
+ tool_poisoning_score?: Long, // Hidden instructions in tool description/args
204
+ tool_poisoning_detected?: Bool, // Boolean flag for tool poisoning
205
+ rug_pull_score?: Long, // Tool behavior drift after trust establishment
206
+ rug_pull_detected?: Bool, // Boolean flag for rug pull
207
+ indirect_injection_score?: Long, // Indirect injection via tool output
208
+
209
+ // --- Tool Risk Assessment ---
210
+ tool_risk_score?: Long, // Computed tool risk (0-100)
211
+ tool_category?: String, // "safe", "sensitive", "dangerous"
212
+ tool_is_sensitive?: Bool, // Sensitivity classification
213
+ tool_is_builtin?: Bool, // Built-in IDE tool vs MCP tool
214
+
215
+ // --- Behavioral Analysis --- (OWASP LLM10, ASI02, ASI08)
216
+ loop_detected?: Bool, // Consecutive same-tool call loop
217
+ loop_count?: Long, // Number of consecutive repeat calls
218
+ loop_tool?: String, // Tool name in loop
219
+ suspicious_pattern?: Bool, // Data exfiltration or attack sequence detected
220
+ pattern_type?: String, // "data_exfiltration", "secret_exfiltration", "credential_theft", "destructive_sequence"
221
+ sequence_risk?: Long, // Sequence risk score (0-100)
222
+
223
+ // --- MCP Trust ---
224
+ mcp_server_verified?: Bool, // Whether server is from verified registry
225
+
226
+ // --- Session Detection History (cross-turn sticky flags) ---
227
+ session_pii_detected?: Bool,
228
+ session_pii_types?: Set<String>,
229
+ session_secrets_detected?: Bool,
230
+ session_secret_types?: Set<String>,
231
+ session_injection_detected?: Bool,
232
+ session_command_injection?: Bool,
233
+ session_threat_turns?: Long,
234
+
235
+ // --- Legacy ---
236
+ response_content?: String,
177
237
  },
178
238
  };
179
239
 
180
240
  // Connect to an MCP server
241
+ // Threat focus: supply chain, tool poisoning, rug pull, config risk
181
242
  action connect_server appliesTo {
182
243
  principal: [User, Agent],
183
244
  resource: [Server],
184
245
  context: {
185
- content?: String, // No content to scan when connecting
246
+ content?: String, // Server config content (if available)
186
247
  source: String,
187
248
  event: String,
188
249
  user_email: String,
189
250
  mcp_server?: String,
190
- threat_count?: Long, // Threat scanning may not run for connections
251
+
252
+ // --- Threat Detection ---
253
+ threat_count?: Long,
191
254
  highest_severity?: String,
192
255
  threat_categories?: Set<String>,
193
256
  max_threat_severity?: Long,
194
257
 
195
- // Agent Security (0-100)
196
- // Optional: only present when agent security scanners have run
197
- tool_poisoning_score?: Long, // Tool description manipulation risk
198
- rug_pull_score?: Long, // Tool behavior mismatch risk
199
- indirect_injection_score?: Long, // Indirect prompt injection risk
200
-
201
- // MCP Trust
202
- // Optional: only present when MCP server verification has run
203
- mcp_server_verified?: Bool, // Whether server is from verified registry
258
+ // --- Agent Security (0-100) --- (OWASP ASI04, MCP01-MCP05)
259
+ tool_poisoning_score?: Long, // Poisoned tool descriptions in server
260
+ tool_poisoning_detected?: Bool,
261
+ rug_pull_score?: Long, // Server behavior change after approval
262
+ rug_pull_detected?: Bool,
263
+ indirect_injection_score?: Long, // Injection payloads in server responses
264
+
265
+ // --- MCP Trust & Config Risk ---
266
+ mcp_server_verified?: Bool, // Verified registry status
267
+ mcp_config_risk?: Bool, // Risky server config detected (inline code exec, etc.)
268
+ mcp_risk_score?: Long, // Config risk severity (0-100)
269
+
270
+ // --- Session Detection History (cross-turn sticky flags) ---
271
+ session_pii_detected?: Bool,
272
+ session_pii_types?: Set<String>,
273
+ session_secrets_detected?: Bool,
274
+ session_secret_types?: Set<String>,
275
+ session_injection_detected?: Bool,
276
+ session_command_injection?: Bool,
277
+ session_threat_turns?: Long,
204
278
  },
205
279
  };
206
280
 
207
281
  // Read a file from disk
282
+ // Threat focus: secrets exposure, PII exposure, path traversal, sensitive paths
208
283
  action read_file appliesTo {
209
284
  principal: [User, Agent],
210
285
  resource: [FilePath],
@@ -216,15 +291,37 @@ action read_file appliesTo {
216
291
  path?: String,
217
292
  cwd?: String,
218
293
  workspace_root?: String,
219
- threat_count?: Long, // Threat scanning may not have run
294
+
295
+ // --- Threat Detection ---
296
+ threat_count?: Long,
220
297
  highest_severity?: String,
221
298
  threat_categories?: Set<String>,
299
+ detected_threats?: Set<String>,
222
300
  max_threat_severity?: Long,
223
301
  contains_secrets?: Bool,
302
+
303
+ // --- Secrets (granular) ---
304
+ secret_types?: Set<String>,
305
+ secret_count?: Long,
306
+
307
+ // --- PII Detection ---
308
+ pii_detected?: Bool,
309
+ pii_types?: Set<String>,
310
+ pii_count?: Long,
311
+
312
+ // --- Session Detection History (cross-turn sticky flags) ---
313
+ session_pii_detected?: Bool,
314
+ session_pii_types?: Set<String>,
315
+ session_secrets_detected?: Bool,
316
+ session_secret_types?: Set<String>,
317
+ session_injection_detected?: Bool,
318
+ session_command_injection?: Bool,
319
+ session_threat_turns?: Long,
224
320
  },
225
321
  };
226
322
 
227
323
  // Write a file to disk
324
+ // Threat focus: secrets in output, PII in output, sensitive paths, malicious code
228
325
  action write_file appliesTo {
229
326
  principal: [User, Agent],
230
327
  resource: [FilePath],
@@ -236,11 +333,32 @@ action write_file appliesTo {
236
333
  path?: String,
237
334
  cwd?: String,
238
335
  workspace_root?: String,
239
- threat_count?: Long, // Threat scanning may not have run
336
+
337
+ // --- Threat Detection ---
338
+ threat_count?: Long,
240
339
  highest_severity?: String,
241
340
  threat_categories?: Set<String>,
341
+ detected_threats?: Set<String>,
242
342
  max_threat_severity?: Long,
243
343
  contains_secrets?: Bool,
344
+
345
+ // --- Secrets (granular) ---
346
+ secret_types?: Set<String>,
347
+ secret_count?: Long,
348
+
349
+ // --- PII Detection ---
350
+ pii_detected?: Bool,
351
+ pii_types?: Set<String>,
352
+ pii_count?: Long,
353
+
354
+ // --- Session Detection History (cross-turn sticky flags) ---
355
+ session_pii_detected?: Bool,
356
+ session_pii_types?: Set<String>,
357
+ session_secrets_detected?: Bool,
358
+ session_secret_types?: Set<String>,
359
+ session_injection_detected?: Bool,
360
+ session_command_injection?: Bool,
361
+ session_threat_turns?: Long,
244
362
  },
245
363
  };
246
364
 
package/dist/explain.d.ts CHANGED
@@ -46,6 +46,18 @@ export interface PolicyExplanation {
46
46
  /** Raw Cedar condition text if the policy uses rawCondition instead of structured conditions */
47
47
  raw_condition?: string;
48
48
  }
49
+ /**
50
+ * Identifies the detector that produced a context value.
51
+ * Used for provenance tracking — linking policy conditions back to their source detector.
52
+ */
53
+ export interface EvidenceSource {
54
+ /** Detector name (e.g., "injection", "secrets", "tool_validator") */
55
+ detector: string;
56
+ /** Detector execution latency in milliseconds */
57
+ latency_ms?: number;
58
+ /** Producer-specific metadata (e.g., "tier", "phase", "priority"). Treated as opaque key-value pairs. */
59
+ labels?: Record<string, string>;
60
+ }
49
61
  /**
50
62
  * Result of evaluating a single condition against the request context.
51
63
  */
@@ -60,6 +72,15 @@ export interface ConditionResult {
60
72
  actual?: unknown;
61
73
  /** Whether this condition matched */
62
74
  matched: boolean;
75
+ /** Source detector that produced this context value (when provenance is available) */
76
+ source?: EvidenceSource;
77
+ }
78
+ /**
79
+ * Options for explainDecision.
80
+ */
81
+ export interface ExplainOptions {
82
+ /** Maps context field names to the detector that produced them */
83
+ provenance?: Record<string, EvidenceSource>;
63
84
  }
64
85
  /** Evaluated comparison: context.field <op> value */
65
86
  export interface EvaluatedComparison {
@@ -69,6 +90,8 @@ export interface EvaluatedComparison {
69
90
  expected: string | number | boolean | string[];
70
91
  actual: unknown;
71
92
  matched: boolean;
93
+ /** Source detector that produced this context value (when provenance is available) */
94
+ source?: EvidenceSource;
72
95
  }
73
96
  /** Evaluated contains: context.field.contains(value) */
74
97
  export interface EvaluatedContains {
@@ -77,6 +100,8 @@ export interface EvaluatedContains {
77
100
  expected: string | number | boolean;
78
101
  actual: unknown;
79
102
  matched: boolean;
103
+ /** Source detector that produced this context value (when provenance is available) */
104
+ source?: EvidenceSource;
80
105
  }
81
106
  /** Evaluated like: context.field like "pattern" */
82
107
  export interface EvaluatedLike {
@@ -85,6 +110,8 @@ export interface EvaluatedLike {
85
110
  pattern: string;
86
111
  actual: unknown;
87
112
  matched: boolean;
113
+ /** Source detector that produced this context value (when provenance is available) */
114
+ source?: EvidenceSource;
88
115
  }
89
116
  /** Evaluated has: context has field */
90
117
  export interface EvaluatedHas {
@@ -142,9 +169,10 @@ export type EvaluatedExpression = EvaluatedComparison | EvaluatedContains | Eval
142
169
  * }
143
170
  * ```
144
171
  */
145
- export declare function explainDecision(decision: DecisionInput, rules: PolicyRule[], context: Record<string, unknown>): ExplainedDecision;
172
+ export declare function explainDecision(decision: DecisionInput, rules: PolicyRule[], context: Record<string, unknown>, options?: ExplainOptions): ExplainedDecision;
146
173
  /**
147
174
  * Recursively evaluate a ConditionExpression tree against a context map.
148
175
  * Returns an EvaluatedExpression tree with `matched` booleans and `actual` values.
176
+ * The optional provenance map links context field names to their source detectors.
149
177
  */
150
- export declare function evaluateExpression(expr: ConditionExpression, context: Record<string, unknown>): EvaluatedExpression;
178
+ export declare function evaluateExpression(expr: ConditionExpression, context: Record<string, unknown>, provenance?: Record<string, EvidenceSource>): EvaluatedExpression;