delegation.rego
Hard caps on what delegated sub-agents can do. The principle: a child agent is more constrained than its parent, both in delegation chain depth and in transactional authority.
Like security.rego, every denial here becomes a SECURITY_CRITICAL decision in main.rego — no escalation path, no scope override.
Package: kitelogik.delegation · Source: kitelogik/policies/delegation.rego
Rules
| Trigger | Match |
|---|---|
| Delegation chain too deep | context.delegation_depth > 2 |
| Depth-1 refund > $50 | action == "approve_refund" and delegation_depth == 1 and args.amount > 50 |
| Depth-1 refund with malformed amount | action == "approve_refund" and delegation_depth == 1 and args.amount < 0 (catches null, false, true, negatives) |
| Depth-2+ refund (any amount) | action == "approve_refund" and delegation_depth >= 2 |
The < 0 rule is a deliberate guard against OPA's structural ordering — in OPA, null < bool < number, so null > 50 evaluates false (not an error) and would silently bypass the cap. The < 0 check catches all three malformed-amount cases.
Source
package kitelogik.delegation
import future.keywords.if
import future.keywords.in
default allow := false
default deny := false
# Hard cap: no delegation chain deeper than 2
deny if { input.context.delegation_depth > 2 }
# Depth-1 delegates: refund cap is $50
deny if {
input.action == "approve_refund"
input.context.delegation_depth == 1
input.args.amount > 50
}
# Depth-1 delegates: malformed amounts (null/bool/negative) are blocked
deny if {
input.action == "approve_refund"
input.context.delegation_depth == 1
input.args.amount < 0
}
# Depth-2+ delegates: no refunds at all
deny if {
input.action == "approve_refund"
input.context.delegation_depth >= 2
}How main.rego uses this module
Same pattern as security — checked first, overrides allows, gets SECURITY_CRITICAL risk tier, no HITL escalation:
deny if { delegation.deny }
risk_tier := "SECURITY_CRITICAL" if { delegation.deny }
requires_hitl if {
not allow
not security.deny
not delegation.deny # ← excluded from HITL
}Where this fits with agent_lifecycle.rego
agent_lifecycle.rego governs the spawn and delegate events themselves (can this agent create a child?). delegation.rego governs what the resulting child agent is allowed to do (the constraints on its action surface).
So a complete delegation flow runs through both:
- Parent agent fires
agent.delegateevent →agent_lifecycle.regogates - Child session is created with
delegation_depth = parent.depth + 1 - Child fires a
tool_callevent →delegation.regogates the tool's authority based on the new depth
Extending in your own project
Same package, new file:
package kitelogik.delegation
import future.keywords.if
# Restrict child agents from sending notifications
deny if {
input.action == "send_notification"
input.context.delegation_depth > 0
}TIP
The hard depth cap of > 2 is the OSS default. To raise (or lower) it, add a deny rule in your own package with the desired threshold — since denies merge across files, the stricter rule wins.