@highflame/policy 2.2.41 → 2.2.42

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.
@@ -1025,7 +1025,7 @@ entity FilePath in [Project];
1025
1025
  // ACTIONS
1026
1026
  // =============================================================================
1027
1027
 
1028
- // Call an MCP tool
1028
+ // Call an MCP tool (tools/call). Enumeration is the \`list\` action.
1029
1029
  // Threat focus: command injection, tool poisoning, rug pull, secrets, PII
1030
1030
  action call_tool appliesTo {
1031
1031
  principal: [User, MCP_Client],
@@ -1225,6 +1225,108 @@ action connect_server appliesTo {
1225
1225
  },
1226
1226
  };
1227
1227
 
1228
+ // Enumerate a server's tools, prompts, or resources
1229
+ // (tools/list, prompts/list, resources/list)
1230
+ //
1231
+ // Threat focus: tool poisoning and rug pull in the DESCRIPTIONS returned by
1232
+ // enumeration — the text a host feeds the model when it assembles the
1233
+ // session's tool schema, and the vector a rug-pull exploits between scans.
1234
+ //
1235
+ // Distinct from the actions it enumerates, and deliberately NOT default-locked
1236
+ // (highflame-shield#515 / INV-AUTHZ-003). Listing is host plumbing: the model
1237
+ // never decides to enumerate, the host does it to build the session. Folding
1238
+ // it into call_tool / process_prompt / read_file made enumeration inherit
1239
+ // their lock, and because a list names no tool, no conditioned grant
1240
+ // (\`context has tool_name && ...\`) can ever match it — so an agent holding a
1241
+ // correct grant for every tool it uses was still denied the enumeration step,
1242
+ // and without tools/list it cannot call anything at all. The only grant that
1243
+ // could match was an unconditioned one, which would have lifted the lock for
1244
+ // every call as well. There is no way to express "may list, may not call"
1245
+ // while a list and a call are the same action. See highflame-firehog#589.
1246
+ //
1247
+ // Authorization over WHAT may be enumerated is expressed through the list
1248
+ // CONTENTS (the grant-derived list filter), not by denying the request.
1249
+ action list appliesTo {
1250
+ principal: [User, MCP_Client],
1251
+ resource: [Server],
1252
+ context: {
1253
+ // Identity (AARM R6 / CAP-IDN-011) — projected from the principal's token; optional.
1254
+ role?: String,
1255
+ privilege_scope?: Set<String>,
1256
+
1257
+ // --- Content ---
1258
+ // The enumerated item's description. Optional: a tool may declare none,
1259
+ // and a policy must still evaluate when it does not.
1260
+ content?: String,
1261
+ mcp_server?: String,
1262
+
1263
+ // --- Enumerated item identity ---
1264
+ // The list is scanned one item at a time, so the item being evaluated
1265
+ // rides here while the resource stays the server being enumerated.
1266
+ tool_name?: String,
1267
+ tool_risk_score?: Long,
1268
+ tool_category?: String, // "safe", "sensitive", "dangerous"
1269
+ tool_is_sensitive?: Bool,
1270
+ tool_is_builtin?: Bool,
1271
+ tool_operation_classes?: Set<String>,
1272
+
1273
+ // --- Threat Detection ---
1274
+ threat_count?: Long,
1275
+ highest_severity?: String,
1276
+ threat_categories?: Set<String>,
1277
+ detected_threats?: Set<String>,
1278
+ max_threat_severity?: Long,
1279
+
1280
+ // --- Agent Security (0-100) — the reason this action scans at all ---
1281
+ tool_poisoning_score?: Long, // Hidden instructions in a tool description
1282
+ tool_poisoning_detected?: Bool,
1283
+ rug_pull_score?: Long, // Description drift after trust establishment
1284
+ rug_pull_detected?: Bool,
1285
+ indirect_injection_score?: Long,
1286
+
1287
+ // --- ML Detector Confidence Scores (0-100) ---
1288
+ injection_score?: Long,
1289
+ jailbreak_score?: Long,
1290
+
1291
+ // --- Secrets ---
1292
+ secrets_detected?: Bool,
1293
+ secret_types?: Set<String>,
1294
+ secret_count?: Long,
1295
+
1296
+ // --- PII Detection ---
1297
+ pii_detected?: Bool,
1298
+ pii_types?: Set<String>,
1299
+ pii_count?: Long,
1300
+ pii_score?: Long,
1301
+
1302
+ // --- MCP Trust & Config Risk ---
1303
+ mcp_server_verified?: Bool,
1304
+ mcp_config_risk?: Bool,
1305
+ mcp_risk_score?: Long,
1306
+
1307
+ // --- Session-Aware Threat Aggregation ---
1308
+ session_threat_turns?: Long,
1309
+ session_cumulative_risk_score?: Long,
1310
+ session_injection_detected?: Bool,
1311
+ session_command_injection?: Bool,
1312
+ session_pii_detected?: Bool,
1313
+ session_pii_types?: Set<String>,
1314
+ session_secrets_detected?: Bool,
1315
+ session_secret_types?: Set<String>,
1316
+ session_max_injection_score?: Long,
1317
+ session_max_jailbreak_score?: Long,
1318
+ session_max_command_injection_score?: Long,
1319
+ session_max_pii_score?: Long,
1320
+ session_max_secret_score?: Long,
1321
+
1322
+ // Rate Limiting — gateway-metered, Shield-decided (ADR 0014)
1323
+ rpm_remaining_pct?: Long,
1324
+ rpm_exceeded?: Bool,
1325
+ tpm_remaining_pct?: Long,
1326
+ tpm_exceeded?: Bool,
1327
+ },
1328
+ };
1329
+
1228
1330
  // Shared content-inspection context.
1229
1331
  //
1230
1332
  // Prompts and responses are inspected by the same detector pipeline and so
@@ -1312,7 +1414,8 @@ type ContentInspectionContext = {
1312
1414
  tpm_exceeded?: Bool,
1313
1415
  };
1314
1416
 
1315
- // Process a prompt (MCP prompts/get or LLM chat completions)
1417
+ // Process a prompt (MCP prompts/get or LLM chat completions).
1418
+ // Enumeration (prompts/list) is the \`list\` action.
1316
1419
  // Threat focus: injection, jailbreak, secrets, PII, content safety
1317
1420
  action process_prompt appliesTo {
1318
1421
  principal: [User, MCP_Client],
@@ -1335,7 +1438,8 @@ action process_response appliesTo {
1335
1438
  context: ContentInspectionContext,
1336
1439
  };
1337
1440
 
1338
- // Read an MCP resource (resources/read, resources/list)
1441
+ // Read an MCP resource (resources/read). Enumeration (resources/list)
1442
+ // is the \`list\` action.
1339
1443
  // Threat focus: secrets exposure, PII exposure, sensitive paths
1340
1444
  action read_file appliesTo {
1341
1445
  principal: [User, MCP_Client],
@@ -2327,6 +2431,24 @@ action process_prompt appliesTo {
2327
2431
  // --- Legacy ---
2328
2432
  prompt_text?: String, // Same as content (backward compatibility)
2329
2433
  response_content?: String, // Response content (if available)
2434
+ // --- Session Token Budget --- (OWASP LLM10; CAP-ENF-009, cerberus#147)
2435
+ // Fed by the cumulative session total the Overwatch daemon reports on the
2436
+ // prompt and before-tool events.
2437
+ //
2438
+ // BOTH are optional, so BOTH need a \`has\` guard — a bare
2439
+ // \`context.budget_remaining_pct < 10\` fails cedar validation with
2440
+ // "unable to guarantee safety of access to optional attribute".
2441
+ // Write: \`context has budget_remaining_pct && context.budget_remaining_pct < 10\`.
2442
+ //
2443
+ // The guard is a validation requirement, NOT a safety property. Whenever a
2444
+ // session loads, budget_checker emits both keys — including
2445
+ // budget_exceeded: false and budget_remaining_pct: 100 when no budget rule
2446
+ // is configured for the scope. So a grant conditioned on
2447
+ // \`!context.budget_exceeded\` fires on unbudgeted sessions, and a low
2448
+ // percentage is evidence of spend only when a budget rule exists. Absence
2449
+ // means the detector did not run at all (no session).
2450
+ budget_remaining_pct?: Long, // Remaining session token budget (0-100)
2451
+ budget_exceeded?: Bool, // Session token budget exceeded
2330
2452
  },
2331
2453
  };
2332
2454
 
@@ -2446,6 +2568,24 @@ action call_tool appliesTo {
2446
2568
 
2447
2569
  // --- Legacy ---
2448
2570
  response_content?: String,
2571
+ // --- Session Token Budget --- (OWASP LLM10; CAP-ENF-009, cerberus#147)
2572
+ // Fed by the cumulative session total the Overwatch daemon reports on the
2573
+ // prompt and before-tool events.
2574
+ //
2575
+ // BOTH are optional, so BOTH need a \`has\` guard — a bare
2576
+ // \`context.budget_remaining_pct < 10\` fails cedar validation with
2577
+ // "unable to guarantee safety of access to optional attribute".
2578
+ // Write: \`context has budget_remaining_pct && context.budget_remaining_pct < 10\`.
2579
+ //
2580
+ // The guard is a validation requirement, NOT a safety property. Whenever a
2581
+ // session loads, budget_checker emits both keys — including
2582
+ // budget_exceeded: false and budget_remaining_pct: 100 when no budget rule
2583
+ // is configured for the scope. So a grant conditioned on
2584
+ // \`!context.budget_exceeded\` fires on unbudgeted sessions, and a low
2585
+ // percentage is evidence of spend only when a budget rule exists. Absence
2586
+ // means the detector did not run at all (no session).
2587
+ budget_remaining_pct?: Long, // Remaining session token budget (0-100)
2588
+ budget_exceeded?: Bool, // Session token budget exceeded
2449
2589
  },
2450
2590
  };
2451
2591
 
@@ -2568,6 +2708,24 @@ action connect_server appliesTo {
2568
2708
  session_max_pii_score?: Long,
2569
2709
  session_max_secret_score?: Long,
2570
2710
  session_cumulative_risk_score?: Long,
2711
+ // --- Session Token Budget --- (OWASP LLM10; CAP-ENF-009, cerberus#147)
2712
+ // Fed by the cumulative session total the Overwatch daemon reports on the
2713
+ // prompt and before-tool events.
2714
+ //
2715
+ // BOTH are optional, so BOTH need a \`has\` guard — a bare
2716
+ // \`context.budget_remaining_pct < 10\` fails cedar validation with
2717
+ // "unable to guarantee safety of access to optional attribute".
2718
+ // Write: \`context has budget_remaining_pct && context.budget_remaining_pct < 10\`.
2719
+ //
2720
+ // The guard is a validation requirement, NOT a safety property. Whenever a
2721
+ // session loads, budget_checker emits both keys — including
2722
+ // budget_exceeded: false and budget_remaining_pct: 100 when no budget rule
2723
+ // is configured for the scope. So a grant conditioned on
2724
+ // \`!context.budget_exceeded\` fires on unbudgeted sessions, and a low
2725
+ // percentage is evidence of spend only when a budget rule exists. Absence
2726
+ // means the detector did not run at all (no session).
2727
+ budget_remaining_pct?: Long, // Remaining session token budget (0-100)
2728
+ budget_exceeded?: Bool, // Session token budget exceeded
2571
2729
  },
2572
2730
  };
2573
2731
 
@@ -2622,6 +2780,24 @@ action read_file appliesTo {
2622
2780
 
2623
2781
  // --- Agent Security ---
2624
2782
  indirect_injection_score?: Long, // Indirect injection via file content
2783
+ // --- Session Token Budget --- (OWASP LLM10; CAP-ENF-009, cerberus#147)
2784
+ // Fed by the cumulative session total the Overwatch daemon reports on the
2785
+ // prompt and before-tool events.
2786
+ //
2787
+ // BOTH are optional, so BOTH need a \`has\` guard — a bare
2788
+ // \`context.budget_remaining_pct < 10\` fails cedar validation with
2789
+ // "unable to guarantee safety of access to optional attribute".
2790
+ // Write: \`context has budget_remaining_pct && context.budget_remaining_pct < 10\`.
2791
+ //
2792
+ // The guard is a validation requirement, NOT a safety property. Whenever a
2793
+ // session loads, budget_checker emits both keys — including
2794
+ // budget_exceeded: false and budget_remaining_pct: 100 when no budget rule
2795
+ // is configured for the scope. So a grant conditioned on
2796
+ // \`!context.budget_exceeded\` fires on unbudgeted sessions, and a low
2797
+ // percentage is evidence of spend only when a budget rule exists. Absence
2798
+ // means the detector did not run at all (no session).
2799
+ budget_remaining_pct?: Long, // Remaining session token budget (0-100)
2800
+ budget_exceeded?: Bool, // Session token budget exceeded
2625
2801
  },
2626
2802
  };
2627
2803
 
@@ -2676,6 +2852,24 @@ action write_file appliesTo {
2676
2852
 
2677
2853
  // --- Agent Security ---
2678
2854
  indirect_injection_score?: Long, // Indirect injection via file content
2855
+ // --- Session Token Budget --- (OWASP LLM10; CAP-ENF-009, cerberus#147)
2856
+ // Fed by the cumulative session total the Overwatch daemon reports on the
2857
+ // prompt and before-tool events.
2858
+ //
2859
+ // BOTH are optional, so BOTH need a \`has\` guard — a bare
2860
+ // \`context.budget_remaining_pct < 10\` fails cedar validation with
2861
+ // "unable to guarantee safety of access to optional attribute".
2862
+ // Write: \`context has budget_remaining_pct && context.budget_remaining_pct < 10\`.
2863
+ //
2864
+ // The guard is a validation requirement, NOT a safety property. Whenever a
2865
+ // session loads, budget_checker emits both keys — including
2866
+ // budget_exceeded: false and budget_remaining_pct: 100 when no budget rule
2867
+ // is configured for the scope. So a grant conditioned on
2868
+ // \`!context.budget_exceeded\` fires on unbudgeted sessions, and a low
2869
+ // percentage is evidence of spend only when a budget rule exists. Absence
2870
+ // means the detector did not run at all (no session).
2871
+ budget_remaining_pct?: Long, // Remaining session token budget (0-100)
2872
+ budget_exceeded?: Bool, // Session token budget exceeded
2679
2873
  },
2680
2874
  };
2681
2875
 
@@ -4035,6 +4229,61 @@ export const AI_GATEWAY_CONTEXT = {
4035
4229
  { "key": "package_check_status", "type": "string", "required": false, "description": "Set to \'degraded\' when the osv.dev lookup failed (fail-open)" }
4036
4230
  ]
4037
4231
  },
4232
+ {
4233
+ "name": "list",
4234
+ "description": "Enumerate a server\'s tools, prompts, or resources — threat focus: tool poisoning and rug pull in the returned descriptions",
4235
+ "context_attributes": [
4236
+ { "key": "role", "type": "string", "required": false, "description": "Caller\'s RBAC role projected from the principal\'s token (AARM R6 / CAP-IDN-011)." },
4237
+ { "key": "privilege_scope", "type": "array", "required": false, "description": "Privilege-scope strings granted to the caller, projected from the token (AARM R6 / CAP-IDN-011)." },
4238
+ { "key": "content", "type": "string", "required": false, "description": "The enumerated item\'s description — the text scanned for poisoning. Absent when the item declares none." },
4239
+ { "key": "mcp_server", "type": "string", "required": false, "description": "MCP server being enumerated" },
4240
+ { "key": "tool_name", "type": "string", "required": false, "description": "Name of the enumerated item being evaluated" },
4241
+ { "key": "tool_risk_score", "type": "number", "required": false, "description": "Tool risk score" },
4242
+ { "key": "tool_category", "type": "string", "required": false, "description": "\"safe\", \"sensitive\", \"dangerous\"" },
4243
+ { "key": "tool_is_sensitive", "type": "boolean", "required": false, "description": "Tool is sensitive" },
4244
+ { "key": "tool_is_builtin", "type": "boolean", "required": false, "description": "Tool is builtin" },
4245
+ { "key": "tool_operation_classes", "type": "array", "required": false, "description": "Tool operation classes" },
4246
+ { "key": "threat_count", "type": "number", "required": false, "description": "Total threats detected" },
4247
+ { "key": "highest_severity", "type": "string", "required": false, "description": "Highest threat severity" },
4248
+ { "key": "threat_categories", "type": "array", "required": false, "description": "Threat category names" },
4249
+ { "key": "detected_threats", "type": "array", "required": false, "description": "Detection rule names that matched" },
4250
+ { "key": "max_threat_severity", "type": "number", "required": false, "description": "Numeric severity (0=none, 4=critical)" },
4251
+ { "key": "tool_poisoning_score", "type": "number", "required": false, "description": "Hidden instructions in a tool description" },
4252
+ { "key": "tool_poisoning_detected", "type": "boolean", "required": false, "description": "Hidden instructions found in the description" },
4253
+ { "key": "rug_pull_score", "type": "number", "required": false, "description": "Description drift after trust establishment" },
4254
+ { "key": "rug_pull_detected", "type": "boolean", "required": false, "description": "Description drifted after trust was established" },
4255
+ { "key": "indirect_injection_score", "type": "number", "required": false, "description": "Indirect injection score" },
4256
+ { "key": "injection_score", "type": "number", "required": false, "description": "Prompt injection classifier confidence (0-100)" },
4257
+ { "key": "jailbreak_score", "type": "number", "required": false, "description": "Jailbreak classifier confidence (0-100)" },
4258
+ { "key": "secrets_detected", "type": "boolean", "required": false, "description": "Whether secrets were detected in the description" },
4259
+ { "key": "secret_types", "type": "array", "required": false, "description": "Secret types detected" },
4260
+ { "key": "secret_count", "type": "number", "required": false, "description": "Number of secrets detected" },
4261
+ { "key": "pii_detected", "type": "boolean", "required": false, "description": "Whether PII was detected" },
4262
+ { "key": "pii_types", "type": "array", "required": false, "description": "PII types detected" },
4263
+ { "key": "pii_count", "type": "number", "required": false, "description": "Number of PII entities" },
4264
+ { "key": "pii_score", "type": "number", "required": false, "description": "PII confidence (0-100)" },
4265
+ { "key": "mcp_server_verified", "type": "boolean", "required": false, "description": "Whether the server is from a verified registry" },
4266
+ { "key": "mcp_config_risk", "type": "boolean", "required": false, "description": "Server config flagged risky" },
4267
+ { "key": "mcp_risk_score", "type": "number", "required": false, "description": "Server risk score (0-100)" },
4268
+ { "key": "session_threat_turns", "type": "number", "required": false, "description": "Session-aggregated threat turns across prior turns" },
4269
+ { "key": "session_cumulative_risk_score", "type": "number", "required": false, "description": "Session-aggregated cumulative risk score across prior turns" },
4270
+ { "key": "session_injection_detected", "type": "boolean", "required": false, "description": "Session-aggregated injection detected across prior turns" },
4271
+ { "key": "session_command_injection", "type": "boolean", "required": false, "description": "Session-aggregated command injection across prior turns" },
4272
+ { "key": "session_pii_detected", "type": "boolean", "required": false, "description": "Session-aggregated pii detected across prior turns" },
4273
+ { "key": "session_pii_types", "type": "array", "required": false, "description": "Session-aggregated pii types across prior turns" },
4274
+ { "key": "session_secrets_detected", "type": "boolean", "required": false, "description": "Session-aggregated secrets detected across prior turns" },
4275
+ { "key": "session_secret_types", "type": "array", "required": false, "description": "Session-aggregated secret types across prior turns" },
4276
+ { "key": "session_max_injection_score", "type": "number", "required": false, "description": "Session-aggregated max injection score across prior turns" },
4277
+ { "key": "session_max_jailbreak_score", "type": "number", "required": false, "description": "Session-aggregated max jailbreak score across prior turns" },
4278
+ { "key": "session_max_command_injection_score", "type": "number", "required": false, "description": "Session-aggregated max command injection score across prior turns" },
4279
+ { "key": "session_max_pii_score", "type": "number", "required": false, "description": "Session-aggregated max pii score across prior turns" },
4280
+ { "key": "session_max_secret_score", "type": "number", "required": false, "description": "Session-aggregated max secret score across prior turns" },
4281
+ { "key": "rpm_remaining_pct", "type": "number", "required": false, "description": "Requests-per-minute headroom remaining" },
4282
+ { "key": "rpm_exceeded", "type": "boolean", "required": false, "description": "RPM limit exceeded" },
4283
+ { "key": "tpm_remaining_pct", "type": "number", "required": false, "description": "Tokens-per-minute headroom remaining" },
4284
+ { "key": "tpm_exceeded", "type": "boolean", "required": false, "description": "TPM limit exceeded" }
4285
+ ]
4286
+ },
4038
4287
  {
4039
4288
  "name": "process_prompt",
4040
4289
  "description": "Process a prompt (MCP or LLM chat completion) — threat focus: injection, jailbreak, secrets, PII, content safety",
@@ -4774,7 +5023,9 @@ export const OVERWATCH_CONTEXT = {
4774
5023
  { "key": "session_max_command_injection_score", "type": "number", "required": false, "description": "Highest command injection score seen in any turn of the session (0-100)" },
4775
5024
  { "key": "session_max_pii_score", "type": "number", "required": false, "description": "Highest PII risk score seen in any turn of the session (0-100)" },
4776
5025
  { "key": "session_max_secret_score", "type": "number", "required": false, "description": "Highest secret detection score seen in any turn of the session (0-100)" },
4777
- { "key": "session_cumulative_risk_score", "type": "number", "required": false, "description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant" }
5026
+ { "key": "session_cumulative_risk_score", "type": "number", "required": false, "description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant" },
5027
+ { "key": "budget_remaining_pct", "type": "number", "required": false, "description": "Remaining session token budget as a percentage (0-100). Optional, so a policy must guard it: `context has budget_remaining_pct && context.budget_remaining_pct < 10` — a bare comparison fails Cedar validation. Runtime default-fills it to 100 whenever the detector runs, so a high value is not evidence that a budget rule exists." },
5028
+ { "key": "budget_exceeded", "type": "boolean", "required": false, "description": "Whether the session token budget has been exceeded. Optional, so a policy must guard it with `context has budget_exceeded`. The guard is a validation requirement, not a fail-closed property: the detector emits false whenever a session loads, including when no budget rule is configured, so a grant conditioned on `!context.budget_exceeded` will fire on unbudgeted sessions." }
4778
5029
  ]
4779
5030
  },
4780
5031
  {
@@ -4864,7 +5115,9 @@ export const OVERWATCH_CONTEXT = {
4864
5115
  { "key": "session_max_command_injection_score", "type": "number", "required": false, "description": "Highest command injection score seen in any turn of the session (0-100)" },
4865
5116
  { "key": "session_max_pii_score", "type": "number", "required": false, "description": "Highest PII risk score seen in any turn of the session (0-100)" },
4866
5117
  { "key": "session_max_secret_score", "type": "number", "required": false, "description": "Highest secret detection score seen in any turn of the session (0-100)" },
4867
- { "key": "session_cumulative_risk_score", "type": "number", "required": false, "description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant" }
5118
+ { "key": "session_cumulative_risk_score", "type": "number", "required": false, "description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant" },
5119
+ { "key": "budget_remaining_pct", "type": "number", "required": false, "description": "Remaining session token budget as a percentage (0-100). Optional, so a policy must guard it: `context has budget_remaining_pct && context.budget_remaining_pct < 10` — a bare comparison fails Cedar validation. Runtime default-fills it to 100 whenever the detector runs, so a high value is not evidence that a budget rule exists." },
5120
+ { "key": "budget_exceeded", "type": "boolean", "required": false, "description": "Whether the session token budget has been exceeded. Optional, so a policy must guard it with `context has budget_exceeded`. The guard is a validation requirement, not a fail-closed property: the detector emits false whenever a session loads, including when no budget rule is configured, so a grant conditioned on `!context.budget_exceeded` will fire on unbudgeted sessions." }
4868
5121
  ]
4869
5122
  },
4870
5123
  {
@@ -4945,7 +5198,9 @@ export const OVERWATCH_CONTEXT = {
4945
5198
  { "key": "session_max_command_injection_score", "type": "number", "required": false, "description": "Highest command injection score seen in any turn of the session (0-100)" },
4946
5199
  { "key": "session_max_pii_score", "type": "number", "required": false, "description": "Highest PII risk score seen in any turn of the session (0-100)" },
4947
5200
  { "key": "session_max_secret_score", "type": "number", "required": false, "description": "Highest secret detection score seen in any turn of the session (0-100)" },
4948
- { "key": "session_cumulative_risk_score", "type": "number", "required": false, "description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant" }
5201
+ { "key": "session_cumulative_risk_score", "type": "number", "required": false, "description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant" },
5202
+ { "key": "budget_remaining_pct", "type": "number", "required": false, "description": "Remaining session token budget as a percentage (0-100). Optional, so a policy must guard it: `context has budget_remaining_pct && context.budget_remaining_pct < 10` — a bare comparison fails Cedar validation. Runtime default-fills it to 100 whenever the detector runs, so a high value is not evidence that a budget rule exists." },
5203
+ { "key": "budget_exceeded", "type": "boolean", "required": false, "description": "Whether the session token budget has been exceeded. Optional, so a policy must guard it with `context has budget_exceeded`. The guard is a validation requirement, not a fail-closed property: the detector emits false whenever a session loads, including when no budget rule is configured, so a grant conditioned on `!context.budget_exceeded` will fire on unbudgeted sessions." }
4949
5204
  ]
4950
5205
  },
4951
5206
  {
@@ -4984,7 +5239,9 @@ export const OVERWATCH_CONTEXT = {
4984
5239
  { "key": "session_max_command_injection_score", "type": "number", "required": false, "description": "Highest command injection score seen in any turn of the session (0-100)" },
4985
5240
  { "key": "session_max_pii_score", "type": "number", "required": false, "description": "Highest PII risk score seen in any turn of the session (0-100)" },
4986
5241
  { "key": "session_max_secret_score", "type": "number", "required": false, "description": "Highest secret detection score seen in any turn of the session (0-100)" },
4987
- { "key": "session_cumulative_risk_score", "type": "number", "required": false, "description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant" }
5242
+ { "key": "session_cumulative_risk_score", "type": "number", "required": false, "description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant" },
5243
+ { "key": "budget_remaining_pct", "type": "number", "required": false, "description": "Remaining session token budget as a percentage (0-100). Optional, so a policy must guard it: `context has budget_remaining_pct && context.budget_remaining_pct < 10` — a bare comparison fails Cedar validation. Runtime default-fills it to 100 whenever the detector runs, so a high value is not evidence that a budget rule exists." },
5244
+ { "key": "budget_exceeded", "type": "boolean", "required": false, "description": "Whether the session token budget has been exceeded. Optional, so a policy must guard it with `context has budget_exceeded`. The guard is a validation requirement, not a fail-closed property: the detector emits false whenever a session loads, including when no budget rule is configured, so a grant conditioned on `!context.budget_exceeded` will fire on unbudgeted sessions." }
4988
5245
  ]
4989
5246
  },
4990
5247
  {
@@ -5024,7 +5281,9 @@ export const OVERWATCH_CONTEXT = {
5024
5281
  { "key": "session_max_command_injection_score", "type": "number", "required": false, "description": "Highest command injection score seen in any turn of the session (0-100)" },
5025
5282
  { "key": "session_max_pii_score", "type": "number", "required": false, "description": "Highest PII risk score seen in any turn of the session (0-100)" },
5026
5283
  { "key": "session_max_secret_score", "type": "number", "required": false, "description": "Highest secret detection score seen in any turn of the session (0-100)" },
5027
- { "key": "session_cumulative_risk_score", "type": "number", "required": false, "description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant" }
5284
+ { "key": "session_cumulative_risk_score", "type": "number", "required": false, "description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant" },
5285
+ { "key": "budget_remaining_pct", "type": "number", "required": false, "description": "Remaining session token budget as a percentage (0-100). Optional, so a policy must guard it: `context has budget_remaining_pct && context.budget_remaining_pct < 10` — a bare comparison fails Cedar validation. Runtime default-fills it to 100 whenever the detector runs, so a high value is not evidence that a budget rule exists." },
5286
+ { "key": "budget_exceeded", "type": "boolean", "required": false, "description": "Whether the session token budget has been exceeded. Optional, so a policy must guard it with `context has budget_exceeded`. The guard is a validation requirement, not a fail-closed property: the detector emits false whenever a session loads, including when no budget rule is configured, so a grant conditioned on `!context.budget_exceeded` will fire on unbudgeted sessions." }
5028
5287
  ]
5029
5288
  }
5030
5289
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highflame/policy",
3
- "version": "2.2.41",
3
+ "version": "2.2.42",
4
4
  "engines": {
5
5
  "node": ">=18"
6
6
  },