Supervisor Protocol
Agent Mesh exposes a rich approval API so that external supervisor agents can review and resolve approval requests on behalf of humans.
The supervisor is not built into flux7-mesh. Any external process (a Python script, a Go service, a LangChain agent, a Claude API call) can poll for pending approvals and resolve them. Agent-mesh provides the protocol, and you provide the logic.
Why
Human-in-the-loop doesn't scale:
- Multiple concurrent agents generate approval requests faster than a human can review them
- Overnight runs stall at approval gates while the human sleeps
- 80% of approvals are routine ("yes, you can write to the project directory"), and only 20% need real judgment
A supervisor agent handles the routine and escalates the rest.
Trust hierarchy
Level 0: Policy engine — static rules, instant, no judgment
Level 1: Supervisor agent — dynamic evaluation, fast, bounded judgment
Level 2: Human — full judgment, slow, expensive attention
| Level | Handles | Example |
|---|---|---|
| Policy (L0) | Black/white rules | allow reads, deny deletes |
| Supervisor (L1) | Gray area, routine | write to project dir, draft email to known contact |
| Human (L2) | High-stakes, ambiguous | send to external recipient, write outside project scope |
The goal is to keep the human in the loop and to protect human attention for the decisions that need it.
Architecture
┌─────────────────────────────┐
│ flux7-mesh │
│ │
Worker Agent ────────────>│ policy ──> approval store │
│ │ │
│ v │
│ GET /approvals │
│ │ │
└──────────────┼───────────────┘
│
v
┌───────────────────┐
│ Supervisor Agent │
│ │
│ evaluate risk │
│ check context │
│ decide: │
│ - approve │──> POST /approvals/{id}/approve
│ - deny │──> POST /approvals/{id}/deny
│ - escalate │──> notify human
│ │
└───────────────────┘
│
(escalation only)
│
v
Human
API reference
Every endpoint below is on the control plane. From the mesh host with no admin_token configured, plain requests work; from another host, or whenever admin_token (or MESH_ADMIN_TOKEN) is set, each request must carry Authorization: Bearer <admin_token>, otherwise it gets 401. See Control Plane Auth.
List pending approvals
Optional tool filter for domain-specific supervisors:
The tool parameter supports glob patterns (Go filepath.Match syntax).
Response:
[
{
"id": "a1b2c3d4e5f67890",
"agent_id": "claude",
"tool": "filesystem.write_file",
"params": {"path": "/home/user/project/main.go", "content": "..."},
"policy_rule": "claude:rule-2",
"status": "pending",
"created_at": "2026-04-10T14:30:00Z",
"remaining": "4m30s",
"injection_risk": false
}
]
When supervisor.expose_content: false is configured, large or content-like param values are replaced with structural metadata:
{
"params": {
"path": "/home/user/project/main.go",
"content": {
"content_length": 1234,
"content_sha256": "a1b2c3d4...",
"content_type_detected": "text/plain"
}
}
}
Get approval detail (with context)
Returns the approval plus the agent's recent activity and active grants, as context for the evaluation:
{
"id": "a1b2c3d4e5f67890",
"agent_id": "claude",
"tool": "filesystem.write_file",
"params": {"path": "/home/user/project/main.go"},
"policy_rule": "claude:rule-2",
"status": "pending",
"created_at": "2026-04-10T14:30:00Z",
"remaining": "4m30s",
"injection_risk": false,
"recent_traces": [
{
"trace_id": "abc123...",
"tool": "filesystem.read_file",
"policy": "allow",
"timestamp": "2026-04-10T14:29:55Z"
},
{
"trace_id": "def456...",
"tool": "filesystem.list_directory",
"policy": "allow",
"timestamp": "2026-04-10T14:29:50Z"
}
],
"active_grants": [
{
"id": "g_789",
"agent": "claude",
"tools": "filesystem.read_*",
"expires_at": "2026-04-10T15:00:00Z",
"remaining": "30m0s",
"granted_by": "http:127.0.0.1:9090"
}
]
}
Approve
Body (all fields optional):
{
"resolved_by": "agent:supervisor",
"reasoning": "write to project source directory, valid Go content, consistent with recent activity",
"confidence": 0.95
}
| Field | Type | Description |
|---|---|---|
resolved_by |
string | Who resolved it. Defaults to http:<remote_addr> |
reasoning |
string | Explanation of the decision (stored in traces) |
confidence |
float64 | 0.0–1.0, how confident the supervisor is (stored in traces) |
Responses:
| Status | Meaning |
|---|---|
| 200 | Approved. The original tool call is forwarded to the backend. |
| 404 | Approval ID not found |
| 409 | Already resolved |
Deny
Same body format as approve. On deny, the agent receives a 403 response.
Grant after approval
A supervisor that approves a routine call can extend the decision into a temporal grant, so the same call does not ask again for a while. Pass the approval (and optionally the call) it answers, so every call the grant later authorises walks back to this decision through GET /traces/{id}/why:
{
"agent": "claude",
"tools": "filesystem.write_file",
"duration": "1h",
"approval_id": "a1b2c3d4e5f67890",
"trace_id": "abc123..."
}
agent, tools and duration are required; approval_id and trace_id are optional and recorded as the grant's origin. mesh approve <id> --grant 1h does the same from the CLI.
Over MCP
A supervisor can also run as an MCP client (for instance a Managed Agent on POST /mcp). The operator tools approval.pending, approval.resolve, grant.create and grant.revoke are listed and callable only for agents whose identity matches supervisor.supervisor_agents. Any other agent does not see them in tools/list and gets a -32601 error if it calls them by name; grant.list stays visible to everyone. grant.create accepts the same optional approval_id and trace_id.
supervisor.enabled: true is a separate switch: it makes worker agents on MCP block on human_approval until the supervisor (or a human) resolves the request, instead of receiving an immediate "pending" result.
Configuration
Content isolation
When expose_content is false, the approval API replaces raw content values with structural metadata. This reduces the prompt injection attack surface, because the supervisor then evaluates structure rather than content.
Content-like keys (content, body, data, file_content, text) and any string value longer than 256 bytes are redacted to:
Detected content types: application/json, text/xml, text/html, text/plain, application/octet-stream.
Injection detection
Every approval view includes an injection_risk boolean. It is true when tool params contain patterns commonly used in prompt injection attacks:
- "ignore previous instructions"
- "you are now"
- "system prompt:"
- "override policy"
- "pre-approved"
- "do not deny" / "do not escalate"
- "pretend you are" / "act as"
- "IMPORTANT: approve/ignore/override"
- "confidence should be"
This is a best-effort heuristic, not a security guarantee. It raises the cost of attack and flags suspicious requests for closer review.
Building a supervisor
A supervisor is any program that implements this loop:
import requests
import time
import os
MESH_URL = "http://localhost:9090"
CONFIDENCE_THRESHOLD = 0.8
# Control-plane credential: required off-host, or whenever admin_token is set.
TOKEN = os.environ.get("MESH_ADMIN_TOKEN")
HEADERS = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
while True:
# 1. Poll for pending approvals (optionally filter by tool)
pending = requests.get(f"{MESH_URL}/approvals", headers=HEADERS,
params={"status": "pending", "tool": "filesystem.*"}).json()
for approval in pending:
# 2. Get full context
detail = requests.get(f"{MESH_URL}/approvals/{approval['id']}",
headers=HEADERS).json()
# 3. Evaluate
verdict = evaluate(detail)
# 4. Act
if verdict["confidence"] >= CONFIDENCE_THRESHOLD:
requests.post(f"{MESH_URL}/approvals/{approval['id']}/{verdict['action']}",
headers=HEADERS, json={
"resolved_by": "agent:supervisor",
"reasoning": verdict["reasoning"],
"confidence": verdict["confidence"],
})
else:
# Escalate to human — leave pending
notify_human(detail, verdict["reasoning"])
time.sleep(2)
Evaluation guidelines
The supervisor should evaluate structural properties, not interpret content semantically:
| Good (structural) | Bad (semantic) |
|---|---|
| "path is within project directory" | "content says it's pre-approved" |
| "file type matches extension" | "the code looks safe" |
| "agent has been writing to this area recently" | "the comment says this is a fix" |
| "request rate is normal" | "the agent seems trustworthy" |
Structural evaluation resists prompt injection, whereas semantic evaluation is vulnerable to it.
Context signals available
From GET /approvals/{id}:
| Signal | Field | Use |
|---|---|---|
| What tool | tool |
Scope check |
| What params | params (or metadata if redacted) |
Path analysis, size check |
| Which agent | agent_id |
Trust level per agent |
| Which policy rule | policy_rule |
Why it needs approval |
| Injection risk | injection_risk |
Auto-escalate if true |
| Recent activity | recent_traces |
Pattern consistency check |
| Active grants | active_grants |
What's already trusted |
| Time remaining | remaining |
Urgency signal |
Escalation criteria
Recommended reasons to escalate to a human instead of auto-resolving:
| Condition | Why |
|---|---|
| Confidence below threshold | Supervisor is unsure |
injection_risk: true |
Suspicious content in params |
| Path outside project scope | Higher risk write |
| Tool is high-stakes | Email send, database write, deploy |
| Rate anomaly | Unusual number of requests from the agent |
| Agent is unknown | No recent trace history to establish pattern |
Observability
Every supervisor decision is recorded in traces:
# All supervisor-resolved traces (add -H "Authorization: Bearer $MESH_ADMIN_TOKEN" off-host)
curl http://localhost:9090/traces | \
jq '[.[] | select(.supervisor_reasoning != null)]'
# Average supervisor confidence
curl http://localhost:9090/traces | \
jq '[.[] | select(.supervisor_confidence > 0) | .supervisor_confidence] | add / length'
# Low-confidence approvals (potential miscalibration)
curl http://localhost:9090/traces | \
jq '[.[] | select(.supervisor_confidence > 0 and .supervisor_confidence < 0.85)]'
Trace fields added by supervisor resolution:
| Field | Type | Description |
|---|---|---|
supervisor_reasoning |
string | Why the supervisor approved/denied |
supervisor_confidence |
float64 | How confident the supervisor was (0.0–1.0) |
approved_by |
string | "agent:supervisor" (vs "http:127.0.0.1:..." for humans) |
The spectrum of autonomy
A single tool can move along this spectrum over time as trust is established:
deny <──── human_approval <──── supervisor <──── allow
"never" "human decides" "agent decides, "always"
human on escalation"
Example progression for gmail.send:
- Day 1:
deny(not ready) - Day 30:
human_approval(a human reviews every send) - Day 90: supervisor (an agent reviews and escalates external recipients)
- Day 180:
allowfor internal recipients (trusted pattern)
The policies evolve as trust is established through trace data.
Multi-supervisor topology
Multiple supervisors can coexist with different scopes using the ?tool= filter:
Worker Agents ──> flux7-mesh ──> Approval Store
│
┌────────────┼────────────┐
v v v
Supervisor A Supervisor B Human
(filesystem) (gmail) (everything else)
# Supervisor A — only filesystem approvals
GET /approvals?status=pending&tool=filesystem.*
# Supervisor B — only gmail approvals
GET /approvals?status=pending&tool=gmail.*
Domain-specific supervision improves judgment quality: the filesystem supervisor understands code patterns, the gmail supervisor understands communication norms.
Reference implementation
The reference implementation is flux7-supervisor (sup7). It was extracted from flux7-console, which no longer carries a supervisor.
Features
- Rule engine: first-match-wins rules with a simple DSL (
starts_with,equals,contains,in). Fast path for known patterns (0ms). - Decision model: when no rule matches, it asks narrow typed questions to a chain of providers (Jev, then the same questions on a local Ollama model if it is down) and decides in code from the probabilities (a few hundred milliseconds). Thresholds are measured on a bench.
- Memory integration: stores each decision in flux7-memory through the mem7 SDK, and reads past decisions as precedents.
- JSONL audit trail: every decision logged with reasoning, confidence, rule matched, provider provenance and evaluation time.
- Admin API: status, pause and resume, edits of rules and question sets,
POST /evaluatefor enforcement points outside the mesh.
Quick start
See docs/deployment-modes.md for full setup options.