Skip to main content

Documentation Index

Fetch the complete documentation index at: https://quintsecurity.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Sub-Agent Detection

Quint detects when AI agents spawn child agents — even without explicit instrumentation. The system combines passive detection (traffic analysis heuristics) with active detection (pattern matching, spawn tickets, and trace propagation) to build high-confidence parent-child attribution.

In-Process Task Dispatch

Claude Code (and some other agent frameworks) can spawn child tasks inside the same process and the same CONNECT tunnel — no new TCP connection, no new model, no new PID. The passive detection layers below assume the child shows up as a distinct tunnel, so they miss this case. Quint handles it in forwardproxy.SubagentDetector.

Mechanism

For every POST with a JSON body, the detector:
  1. Hashes the top-level system prompt with FNV-1a
  2. Tracks per-tunnel state: {parentHash, currentHash, msgCount, currentSession}
  3. Compares the incoming hash and messages.length to previous turns

Transition Rules

State changeTriggerEmits
subagent_startprompt hash changes AND messages.length <= 2{parentSession}:sub:{N}
parent_resumeprompt hash reverts to parentHash AND messages.length > 2back to parentSession
no-ophash changes but msgCount > 2(mid-session prompt rewrite — MCP connect, CLAUDE.md edit)

Child Session IDs

Child session IDs are deterministic and human-readable:
parent: 59247-1777085379101
child:  59247-1777085379101:sub:1
second: 59247-1777085379101:sub:2
The counter increments per parent session, so a parent that spawns three sequential subagents ends up with :sub:1, :sub:2, :sub:3. Each child carries ParentSessionID pointing at the root, which is written to audit_log.parent_session_id at capture time. The local viewer renders nested rows with a purple left border and a sub badge.

Why Not Just Use Tunnel State

The in-process path deliberately avoids relying on CONNECT tunnel boundaries:
  • Subagents share the parent’s keep-alive connection — no new tunnel
  • The process tree is identical — no new PID to bind to
  • The request model is often unchanged — no Layer 1 divergence signal
Prompt-hash + message-count is the only signal that reliably fires in this case without instrumentation in the agent framework.

Passive Detection

Passive detection works via traffic analysis in the forward proxy, using three independent layers that feed signals into a correlation engine.

Detection Layers

Layer 1: Model Divergence

When an agent uses a different model than previously seen in the same tunnel, Quint infers a sub-agent spawn. The first model seen becomes the parent model; subsequent different models trigger a split. Known divergence patterns:
Parent ModelChild ModelProvider
*opus* / *sonnet**haiku*Anthropic
*gpt-4o*gpt-4o-mini*OpenAI
*pro**flash*Google
Any model change (not just known patterns) triggers detection. Each tunnel can only split once.

Layer 2: Concurrency Spike

Quint tracks CONNECT tunnel counts per IP address. During the first 10 seconds (stabilization window), it learns the baseline concurrency. After that, if active tunnels exceed baseline + 2, a sub-agent is detected.
Stabilization: 10 seconds
Spike threshold: baseline + 2 tunnels
Pending children are tracked until their model can be confirmed via Layer 1.

Layer 3: Temporal Gap

When time between CONNECT requests from the same IP exceeds the burst window (2000ms default), Quint evaluates:
  • Has parent trace (X-Quint-Trace header) → confirmed child (source: child_detect)
  • No trace, active tunnels exist → inferred child (source: inferred_child)
  • No trace, no active tunnels → new peer agent

Configuration

Passive detection is enabled by default in forward proxy mode. Tuning parameters:
ParameterDefaultDescription
Burst window2000msTime gap threshold for temporal detection
Stabilization window10sTime to learn baseline concurrency
Spike thresholdbaseline + 2Tunnel count increase to trigger detection
Max body preview8192 bytesHow much of POST body to read for model extraction

Active Detection

Active detection identifies agent creation in MCP tool calls and propagates trace context through the agent hierarchy.

Spawn Patterns

Quint ships with 8 built-in patterns that match tool calls indicating agent creation:
IDDescriptionTool PatternConfidenceSpawn Type
openai-handoffOpenAI Agents SDK transfer*transfer_to_*0.90delegation
generic-create-agentGeneric agent creation*create*agent*0.85direct
a2a-delegationAgent-to-Agent protocol*send_task*0.85delegation
run-agentRunning another agent*run*agent*0.85direct
invoke-assistantInvoking an assistant*invoke*assistant*0.80direct
delegation-flagDelegation keywords in args* (any tool)0.75delegation
shell-agent-spawnShell exec launching agents*exec*0.70fork
subtask-spawnTask decomposition*task*0.65delegation

Argument Scanning

Some patterns also check tool call arguments for keywords:
  • delegation-flag: delegate, handoff, transfer, spawn_agent
  • shell-agent-spawn: agent, assistant, claude, gpt, llm
  • subtask-spawn: subtask, sub_task, child_task, delegate_task
  • generic-create-agent: agent, assistant, model

Child Hint Extraction

When a spawn is detected, Quint extracts the child agent’s identity hint from:
  1. Tool name patterns (e.g., transfer_to_research_botresearch_bot)
  2. Argument fields: agent, agent_name, agent_id, assistant, assistant_id, target_agent, delegate_to

Trace Propagation

Quint propagates trace context through two mechanisms:

HTTP Header: X-Quint-Trace

X-Quint-Trace: {trace_id}.{depth}
Example: X-Quint-Trace: a1b2c3d4-e5f6-7890.2 The trace ID is a UUID generated at the root agent. Depth increments at each spawn. This header is used in the forward proxy to link CONNECT tunnels to parent agents.

In-Band Field: _quint

For MCP stdio transport (where HTTP headers aren’t available), trace context is embedded in JSON-RPC params:
{
  "method": "tools/call",
  "params": {
    "name": "query_database",
    "arguments": { "query": "SELECT *" },
    "_quint": {
      "trace_id": "a1b2c3d4",
      "depth": 1,
      "spawn_ticket": "eyJwaWQi...signature"
    }
  }
}
The _quint field is stripped before forwarding to the actual MCP server.

In-Band Auth

The _quint field in MCP initialize params can also carry authentication:
{
  "_quint": {
    "api_key": "sk-...",
    "token": "qt_agent_...",
    "agent_name": "my-bot",
    "spawn_ticket": "eyJwaWQi..."
  }
}

Spawn Tickets

For cryptographically verified parent-child links, Quint issues HMAC-SHA256 spawn tickets when a spawn is detected.

Ticket Format

base64url(claims_json).base64url(hmac_sha256_signature)

Claims

{
  "pid": "parent-agent-id",
  "pname": "anthropic:bold-amber-falcon",
  "child": "research_bot",
  "d": 1,
  "sc": "tools:read",
  "tid": "trace-uuid",
  "st": "delegation",
  "exp": 1709654400,
  "n": "random-16-byte-nonce"
}
FieldDescription
pidParent agent ID
pnameParent agent name
childChild hint (extracted from tool call)
dDepth in agent hierarchy
scNarrowed scopes for child
tidTrace ID for correlation
stSpawn type: direct, delegation, fork
expExpiration (default: 5 minutes from creation)
nRandom nonce (replay protection)
Tickets are signed with a per-instance 32-byte random secret. The child presents the ticket to the proxy, which verifies the HMAC and establishes the parent-child link with 1.0 confidence.

Correlation Engine

Detection signals from both passive and active layers merge in the correlation engine, which maintains a relationship graph with confidence scores.

Signal Types

SignalConstantBase Confidence
Spawn pattern matchspawn~0.85
Trace context headercontext~0.95
Temporal correlationtemporal~0.50
HMAC-verified ticketsignature1.0

Confidence Merging

Multiple signals for the same parent-child pair are merged using diminishing returns:
merged = max(existing, new) + (1 - max) * 0.1
For example, a spawn signal (0.85) followed by a context signal (0.95) produces:
max(0.85, 0.95) + (1 - 0.95) * 0.1 = 0.95 + 0.005 = 0.955

Relationship Graph

The engine tracks:
type AgentRelationship struct {
    ParentAgentID string
    ChildAgentID  string
    Confidence    float64
    Signals       []Signal   // all contributing signals
    SpawnType     string     // "direct", "delegation", "fork"
    Depth         int        // nesting level
}
Relationships are published to the quint.relationships.{org_id} NATS subject for downstream consumption.

Signal Flow

When a spawn is detected, the following happens:
1

Pattern Match

The tool call matches a spawn pattern. A SpawnEvent is emitted with the pattern ID, confidence, and child hint.
2

Ticket Generation

An HMAC-SHA256 spawn ticket is created with the parent’s identity, narrowed scopes, and a 5-minute TTL.
3

Ticket Injection

The ticket is injected into the tool call’s _quint field (MCP) or made available via X-Quint-Trace (HTTP).
4

Child Verification

When the child agent connects, it presents the ticket. Quint verifies the HMAC signature and establishes the relationship with 1.0 confidence.
5

Correlation

The spawn signal is sent to the correlation engine, which merges it with any other signals (temporal, context, model divergence).
6

Streaming

The spawn event is published to the quint.spawns.{org_id} NATS subject.

Dashboard Visualization

The dashboard renders parent-child trees from the correlation engine data. Each node shows:
  • Agent name and provider
  • Model in use
  • Confidence score for the parent-child link
  • Signal types that contributed to detection
Passive sub-agent detection in forward proxy mode relies on heuristics (model divergence, timing, concurrency). For guaranteed detection, use spawn tickets with HMAC-SHA256 verification.