Skip to content

Commit f01b1bb

Browse files
feat(dotnet): add Tier 1 features — ExecutionRings, SagaOrchestrator, CircuitBreaker, SloEngine, PromptInjectionDetector
- Hypervisor/ExecutionRings: Ring 0-3 privilege model with trust-based assignment, resource limits, demotion - Hypervisor/SagaOrchestrator: Multi-step transactions with forward execution, reverse compensation, escalation - Sre/CircuitBreaker: Three-state (Closed/Open/HalfOpen) with configurable thresholds and ExecuteAsync - Sre/SloEngine: SLO specs, SLI evaluation, error budget tracking, burn rate alerts - Security/PromptInjectionDetector: 17 regex patterns for 7 attack types, blocklist/allowlist, canary tokens, base64 detection - 67 new tests (188 total), all passing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f18f037 commit f01b1bb

File tree

9 files changed

+1921
-0
lines changed

9 files changed

+1921
-0
lines changed
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
2+
3+
namespace AgentGovernance.Hypervisor;
4+
5+
/// <summary>
6+
/// Execution privilege levels inspired by CPU protection rings.
7+
/// Lower values indicate higher privilege. Agents start at Ring 3 (Sandbox)
8+
/// and earn their way up based on trust score.
9+
/// </summary>
10+
public enum ExecutionRing
11+
{
12+
/// <summary>System-level — policy modification, kill switches. Typically reserved for human operators.</summary>
13+
Ring0 = 0,
14+
15+
/// <summary>Trusted — full tool access with standard resource limits.</summary>
16+
Ring1 = 1,
17+
18+
/// <summary>Standard — limited tool access, rate-limited.</summary>
19+
Ring2 = 2,
20+
21+
/// <summary>Sandbox — heavily restricted, minimal permissions.</summary>
22+
Ring3 = 3
23+
}
24+
25+
/// <summary>
26+
/// Resource limits enforced per execution ring.
27+
/// </summary>
28+
public sealed class RingResourceLimits
29+
{
30+
/// <summary>Maximum tool calls per minute.</summary>
31+
public int MaxCallsPerMinute { get; init; }
32+
33+
/// <summary>Maximum execution time per tool call in seconds.</summary>
34+
public double MaxExecutionTimeSec { get; init; }
35+
36+
/// <summary>Maximum memory usage in megabytes (advisory).</summary>
37+
public int MaxMemoryMb { get; init; }
38+
39+
/// <summary>Whether the ring allows write operations.</summary>
40+
public bool AllowWrites { get; init; }
41+
42+
/// <summary>Whether the ring allows network access.</summary>
43+
public bool AllowNetwork { get; init; }
44+
45+
/// <summary>Whether the ring allows spawning sub-agents.</summary>
46+
public bool AllowDelegation { get; init; }
47+
48+
/// <summary>Default resource limits for each ring level.</summary>
49+
public static readonly IReadOnlyDictionary<ExecutionRing, RingResourceLimits> Defaults =
50+
new Dictionary<ExecutionRing, RingResourceLimits>
51+
{
52+
[ExecutionRing.Ring0] = new()
53+
{
54+
MaxCallsPerMinute = int.MaxValue,
55+
MaxExecutionTimeSec = double.MaxValue,
56+
MaxMemoryMb = int.MaxValue,
57+
AllowWrites = true,
58+
AllowNetwork = true,
59+
AllowDelegation = true
60+
},
61+
[ExecutionRing.Ring1] = new()
62+
{
63+
MaxCallsPerMinute = 1000,
64+
MaxExecutionTimeSec = 300,
65+
MaxMemoryMb = 4096,
66+
AllowWrites = true,
67+
AllowNetwork = true,
68+
AllowDelegation = true
69+
},
70+
[ExecutionRing.Ring2] = new()
71+
{
72+
MaxCallsPerMinute = 100,
73+
MaxExecutionTimeSec = 60,
74+
MaxMemoryMb = 1024,
75+
AllowWrites = true,
76+
AllowNetwork = true,
77+
AllowDelegation = false
78+
},
79+
[ExecutionRing.Ring3] = new()
80+
{
81+
MaxCallsPerMinute = 10,
82+
MaxExecutionTimeSec = 5,
83+
MaxMemoryMb = 256,
84+
AllowWrites = false,
85+
AllowNetwork = false,
86+
AllowDelegation = false
87+
}
88+
};
89+
}
90+
91+
/// <summary>
92+
/// Result of an execution ring access check.
93+
/// </summary>
94+
public sealed record RingCheckResult(
95+
bool Allowed,
96+
ExecutionRing AgentRing,
97+
ExecutionRing RequiredRing,
98+
string Reason);
99+
100+
/// <summary>
101+
/// Enforces execution ring privileges for agent operations.
102+
/// Agents are assigned to rings based on trust score and can be promoted
103+
/// or demoted dynamically.
104+
/// </summary>
105+
public sealed class RingEnforcer
106+
{
107+
private readonly Dictionary<ExecutionRing, double> _thresholds;
108+
private readonly Dictionary<ExecutionRing, RingResourceLimits> _limits;
109+
110+
/// <summary>
111+
/// Initializes a new <see cref="RingEnforcer"/> with configurable trust score thresholds.
112+
/// </summary>
113+
/// <param name="thresholds">
114+
/// Optional custom trust score thresholds for ring assignment.
115+
/// Keys are ring levels, values are minimum trust scores (0.0–1.0).
116+
/// Defaults: Ring0=0.95, Ring1=0.80, Ring2=0.60, Ring3=0.0.
117+
/// </param>
118+
/// <param name="limits">Optional custom resource limits per ring.</param>
119+
public RingEnforcer(
120+
Dictionary<ExecutionRing, double>? thresholds = null,
121+
Dictionary<ExecutionRing, RingResourceLimits>? limits = null)
122+
{
123+
_thresholds = thresholds ?? new Dictionary<ExecutionRing, double>
124+
{
125+
[ExecutionRing.Ring0] = 0.95,
126+
[ExecutionRing.Ring1] = 0.80,
127+
[ExecutionRing.Ring2] = 0.60,
128+
[ExecutionRing.Ring3] = 0.0
129+
};
130+
131+
_limits = limits ?? new Dictionary<ExecutionRing, RingResourceLimits>(
132+
RingResourceLimits.Defaults);
133+
}
134+
135+
/// <summary>
136+
/// Computes the execution ring for an agent based on their trust score.
137+
/// </summary>
138+
/// <param name="trustScore">Agent trust score (0.0–1.0).</param>
139+
/// <returns>The highest-privilege ring the agent qualifies for.</returns>
140+
public ExecutionRing ComputeRing(double trustScore)
141+
{
142+
if (trustScore >= _thresholds[ExecutionRing.Ring0]) return ExecutionRing.Ring0;
143+
if (trustScore >= _thresholds[ExecutionRing.Ring1]) return ExecutionRing.Ring1;
144+
if (trustScore >= _thresholds[ExecutionRing.Ring2]) return ExecutionRing.Ring2;
145+
return ExecutionRing.Ring3;
146+
}
147+
148+
/// <summary>
149+
/// Checks whether an agent at a given trust score is permitted to perform
150+
/// an operation requiring the specified ring level.
151+
/// </summary>
152+
/// <param name="trustScore">Agent trust score (0.0–1.0).</param>
153+
/// <param name="requiredRing">The minimum ring required for the operation.</param>
154+
/// <returns>A <see cref="RingCheckResult"/> with the decision.</returns>
155+
public RingCheckResult Check(double trustScore, ExecutionRing requiredRing)
156+
{
157+
var agentRing = ComputeRing(trustScore);
158+
159+
// Ring0 operations always require explicit elevation — never auto-granted.
160+
if (requiredRing == ExecutionRing.Ring0 && agentRing != ExecutionRing.Ring0)
161+
{
162+
return new RingCheckResult(false, agentRing, requiredRing,
163+
"Ring 0 operations require explicit elevation and are never auto-granted.");
164+
}
165+
166+
// Lower ring value = higher privilege. Agent ring must be <= required ring.
167+
bool allowed = (int)agentRing <= (int)requiredRing;
168+
169+
var reason = allowed
170+
? $"Agent at {agentRing} has sufficient privilege for {requiredRing}."
171+
: $"Agent at {agentRing} lacks privilege for {requiredRing} (trust: {trustScore:F2}).";
172+
173+
return new RingCheckResult(allowed, agentRing, requiredRing, reason);
174+
}
175+
176+
/// <summary>
177+
/// Determines whether an agent should be demoted based on a drop in trust score.
178+
/// </summary>
179+
/// <param name="currentRing">The agent's current ring.</param>
180+
/// <param name="newTrustScore">The agent's updated trust score.</param>
181+
/// <returns><c>true</c> if the agent should be moved to a less-privileged ring.</returns>
182+
public bool ShouldDemote(ExecutionRing currentRing, double newTrustScore)
183+
{
184+
var computedRing = ComputeRing(newTrustScore);
185+
return (int)computedRing > (int)currentRing;
186+
}
187+
188+
/// <summary>
189+
/// Returns the resource limits for a given ring.
190+
/// </summary>
191+
public RingResourceLimits GetLimits(ExecutionRing ring)
192+
{
193+
return _limits.TryGetValue(ring, out var limits)
194+
? limits
195+
: RingResourceLimits.Defaults[ring];
196+
}
197+
}

0 commit comments

Comments
 (0)