Agent Control Standard
The Agent Control Standard (ACS) is a wire-format specification that lets a separate Guardian Agent permit, deny, or modify what an AI agent does in real time, over an authenticated channel and with a verifiable audit trail.
Agents that implement ACS-Core run every step through a Guardian over an authenticated channel and honor its decisions in real time. With the Trace, Inspect, and Crypto profiles they also emit OpenTelemetry spans and OCSF events, expose a dynamic Agent Bill of Materials (models, MCP servers, A2A peers, tools, knowledge sources, memory stores), and gain cryptographic non-repudiation of the audit chain. ACS-Core authenticates the channel and binds the agent to the Guardian's decisions; it does not by itself make a deployment secure or its policies strict, and tamper-evidence against a compromised Guardian is the Crypto and Audit profiles, not Core.
ACS extends existing standards rather than reinventing them: JSON-RPC 2.0 for the wire format, OpenTelemetry and OCSF for observability, CycloneDX / SPDX / SWID for the AgBOM, MCP and A2A intact for tool and peer communication.
What v0.1.0 ships
- ACS-Core (mandatory baseline) — capability-negotiation handshake, JSON-RPC envelope, 16 native lifecycle hooks (
sessionStart/End,agentTrigger,userMessage,agentResponse,turnStart/End,toolCallRequest/Result,knowledgeRetrieval,memoryContextRetrieval,memoryStore,preCompact/postCompact,subagentStart/Stop), wrapped MCP, five dispositions (allow,deny,modify,ask,defer), SessionContext with rolling SHA-256 chain hash, optional Intent with immutability rule, replay protection, andsystem/pingliveness. - ACS-Trace profile — OpenTelemetry semconv mapping + OCSF event-class mapping, with decisions emitted as span events on the parent step span.
- ACS-Inspect / ACS-Inspect-Dynamic profiles — canonical AgBOM with
agbom/snapshotandagbom/changed, deterministic CycloneDX / SPDX / SWID derivations. - ACS-Provenance profile — field-level
Provenanceobjects withorigin,source_id,derived_from, and an OPTIONAL wire-formattrustenum that obeys the monotonicity rule. - ACS-Crypto profile — crypto-agile signature registry: HMAC-SHA256 baseline, ML-DSA-65 / SLH-DSA-128s for PQC, hybrid composites for transitional deployments.
- ACS-Audit profile —
request_hashon every ContextEntry so the chain commits to request content, not just step metadata.
See Conformance Profiles for what each profile requires.
Trustworthy agents are
Init agent with ACS
from langchain.agents import initialize_agent, Tool
from langchain.llms import OpenAI
from acs import ACSInstrument
# Initialize LLM and tools
llm = OpenAI(temperature=0)
tools = [
Tool(
name="Search",
func=search_api,
description="Search the web for information"
),
Tool(
name="Calculator",
func=calculate,
description="Perform mathematical calculations"
)
]
# Create agent with ACS instrumentation
agent = initialize_agent(
tools,
llm,
agent="zero-shot-react-description",
verbose=True
)
# Wrap with ACS for observability
acs_agent = ACSInstrument(agent)
import { createAgent } from '@vercel/ai'
import { ACSInstrument } from '@acs/sdk'
// Define tools
const tools = {
search: {
description: 'Search the web for information',
parameters: z.object({
query: z.string()
}),
execute: async ({ query }) => searchAPI(query)
},
calculate: {
description: 'Perform mathematical calculations',
parameters: z.object({
expression: z.string()
}),
execute: async ({ expression }) => evaluate(expression)
}
}
// Create agent with ACS instrumentation
const agent = createAgent({
model: 'gpt-4',
tools,
system: 'You are a helpful assistant'
})
const acsAgent = new ACSInstrument(agent)
{
"jsonrpc": "2.0",
"method": "agent/create",
"params": {
"name": "research-assistant",
"capabilities": {
"tools": ["search", "calculate"],
"models": ["claude-3-opus"],
"protocols": ["mcp", "acs"]
},
"acs": {
"instrumentation": {
"enabled": true,
"hooks": ["agentTrigger", "toolCallRequest", "message"]
},
"trace": {
"format": "opentelemetry",
"endpoint": "https://observability.example.com/v1/traces"
}
}
}
}
# Agent manifest with ACS capabilities
apiVersion: a2a.io/v1
kind: Agent
metadata:
name: research-assistant
namespace: production
spec:
model:
provider: anthropic
name: claude-3-opus
capabilities:
- search
- calculate
observability:
acs:
version: "1.0"
instrumentation:
hooks:
- agentTrigger
- toolCallRequest
- message
trace:
provider: opentelemetry
endpoint: https://telemetry.example.com
inspect:
format: cyclonedx
dynamic: true
Instrumentable
Runtime Hooks
Value: Hooks to agent runtime and lifecycle events.
Description: Specifies hooks that allow intervention at agent's lifecycle and run-time execution.
Standards: ACS Instrument.
from acs import GuardianAgent, PolicyResponse
# Create a Guardian Agent to enforce policies
guardian = GuardianAgent(
endpoint="https://guardian.example.com",
policies=["data-protection", "cost-control"]
)
# Hook into agent runtime
@acs_agent.hook("toolCallRequest")
async def on_tool_call(context):
# Guardian evaluates the tool call
response = await guardian.evaluate({
"hook": "toolCallRequest",
"tool": context.tool_name,
"parameters": context.parameters,
"session": context.session_id
})
if response.action == "DENY":
raise PermissionError(f"Tool call denied: {response.reason}")
elif response.action == "MODIFY":
# Apply Guardian's modifications
context.parameters = response.modifications
return response
# Example: Guardian denies sensitive data access
result = await acs_agent.run("Search for employee SSN records")
# > PermissionError: Tool call denied: Accessing PII data violates policy
import { GuardianAgent, HookContext } from '@acs/guardian'
// Configure Guardian Agent
const guardian = new GuardianAgent({
endpoint: 'https://guardian.example.com',
policies: ['data-protection', 'cost-control']
})
// Register hooks for runtime control
acsAgent.registerHook('toolCallRequest', async (context: HookContext) => {
const response = await guardian.evaluate({
hook: 'toolCallRequest',
tool: context.toolName,
parameters: context.parameters,
session: context.sessionId
})
switch (response.action) {
case 'DENY':
throw new Error(`Denied: ${response.reason}`)
case 'MODIFY':
context.parameters = response.modifications
break
}
return response
})
// Example: Cost control policy in action
await acsAgent.run('Generate 1000 images using DALL-E')
// > Error: Denied: Request exceeds cost threshold ($50 limit)
{
"jsonrpc": "2.0",
"method": "acs/registerHook",
"params": {
"hook": "toolCallRequest",
"guardianEndpoint": "https://guardian.example.com/evaluate",
"policies": ["data-protection", "cost-control"]
}
}
// Example hook evaluation request from Guardian
{
"jsonrpc": "2.0",
"method": "guardian/evaluate",
"params": {
"hook": "toolCallRequest",
"context": {
"tool": "database_query",
"parameters": {
"query": "SELECT * FROM customers"
},
"session": "sess_123"
}
}
}
// Guardian response
{
"jsonrpc": "2.0",
"result": {
"action": "MODIFY",
"modifications": {
"query": "SELECT id, name FROM customers LIMIT 100"
},
"reason": "Limited query to prevent full data exposure"
}
}
# Available ACS hooks for instrumentation
agentTrigger:
description: Fires when agent starts processing
can_modify: true
can_deny: true
message:
description: Fires for each message in conversation
can_modify: true
can_deny: true
toolCallRequest:
description: Fires before tool execution
can_modify: true
can_deny: true
knowledgeRetrieval:
description: Fires when accessing knowledge bases
can_modify: false
can_deny: true
memoryStore:
description: Fires when storing to memory
can_modify: true
can_deny: true
Traceable
Comprehensive Audit Logs
Value: Comprehensive audit logs.
Description: Specifies events that capture AI agent lifecycle and runtime execution. Extends OpenTelemetry and OCSF specs with these properties.
Standards: ACS Trace. Extends OpenTelemetry, OCSF.
from opentelemetry import trace
from acs.trace import ACSTraceProvider
# Configure OpenTelemetry with ACS extensions
tracer = trace.get_tracer(
"research-assistant",
provider=ACSTraceProvider()
)
# Agent execution with automatic tracing
with tracer.start_as_current_span("agent_session") as span:
span.set_attribute("acs.session.id", "sess_123")
span.set_attribute("acs.agent.name", "research-assistant")
result = await acs_agent.run("Find recent AI safety papers")
# Trace output includes:
# - Complete reasoning chain
# - Tool calls with parameters
# - Model interactions
# - Decision points
{
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"operationName": "agent_session",
"startTime": "2024-01-15T10:30:00Z",
"attributes": {
"acs.session.id": "sess_123",
"acs.agent.name": "research-assistant",
"acs.reasoning.steps": 3,
"acs.tools.called": ["web-search", "summarize"]
},
"events": [
{
"name": "acs.step.reasoning",
"timestamp": "2024-01-15T10:30:01Z",
"attributes": {
"thought": "Need to search for recent AI safety papers",
"action": "tool_call",
"tool": "web-search"
}
}
]
}
import { OpenTelemetryTracer } from '@acs/trace'
// Configure tracing for TypeScript agent
const tracer = new OpenTelemetryTracer({
serviceName: 'research-assistant',
endpoint: 'https://telemetry.example.com'
})
// Wrap agent with tracing
const tracedAgent = tracer.instrument(acsAgent)
// Execute with automatic tracing
const result = await tracedAgent.run('Find recent AI safety papers')
// Trace includes same comprehensive data as Python example
{
"jsonrpc": "2.0",
"method": "acs/trace/emit",
"params": {
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"event": {
"type": "agent.execution",
"timestamp": "2024-01-15T10:30:00Z",
"attributes": {
"session.id": "sess_123",
"agent.name": "research-assistant",
"reasoning.steps": 3,
"tools.called": ["web-search", "summarize"]
}
}
}
}
# A2A trace event
apiVersion: a2a.io/v1
kind: TraceEvent
metadata:
traceId: "4bf92f3577b34da6a3ce929d0e0e4736"
spanId: "00f067aa0ba902b7"
timestamp: "2024-01-15T10:30:00Z"
spec:
eventType: agent.execution
agent:
name: research-assistant
session: sess_123
execution:
reasoningSteps: 3
toolsCalled:
- web-search
- summarize
observability:
format: opentelemetry
endpoint: https://telemetry.example.com
from acs.trace import OCSFLogger
# Configure OCSF security event logging
security_logger = OCSFLogger(
endpoint="https://siem.example.com/ocsf",
api_key="your-api-key"
)
# Attach to agent for security events
acs_agent.attach_logger(security_logger)
# Execute agent - security events logged automatically
result = await acs_agent.run("Access customer database")
{
"activity_id": 1,
"activity_name": "Agent Execution",
"category_uid": 3,
"category_name": "Application Activity",
"class_uid": 3001,
"class_name": "AI Agent Activity",
"severity_id": 1,
"time": 1705318200,
"metadata": {
"version": "1.0.0",
"product": {
"name": "ACS Agent",
"vendor_name": "Example Corp"
}
},
"actor": {
"session": {
"uid": "sess_123",
"created_time": 1705318200
},
"idp": {
"name": "research-assistant",
"uid": "agent_456"
}
},
"acs_extensions": {
"reasoning_chain": [
{
"step": 1,
"thought": "User wants database access",
"action": "evaluate_permissions"
}
],
"tools_invoked": ["database_query"],
"data_accessed": ["customers.personal_info"],
"risk_score": 8.5
}
}
import { ACSTraceStream } from '@acs/trace'
// Stream trace events in real-time
const traceStream = new ACSTraceStream(acsAgent)
traceStream.on('event', (event) => {
// Log to your observability platform
console.log(`[${event.timestamp}] ${event.type}:`, event.data)
// Send to security monitoring
if (event.severity === 'high') {
alertSecurityTeam(event)
}
})
// Enable streaming
await traceStream.start()
// Example output during agent execution:
// [2024-01-15T10:30:01Z] session.start: {id: 'sess_789', user: 'alice'}
// [2024-01-15T10:30:02Z] reasoning.step: {thought: 'Analyzing request...'}
// [2024-01-15T10:30:03Z] tool.call: {name: 'database_query', risk: 'high'}
// [2024-01-15T10:30:04Z] security.alert: {reason: 'Sensitive data access'}
Inspectable
Agent Bill of Materials (AgBOM)
Value: Dynamic agent-aware bill-of-material.
Description: Specifies properties that capture tools, models and capabilities of an AI agent. Extends SBOM standard specs with these properties – AgBOM. Goes further to add dynamic updates to AgBOM to account for dynamic agent capability discovery.
Standards: ACS Inspect. Extends CycloneDX, SPDX, SWID.
from acs.inspect import generate_agbom
# Generate Agent Bill of Materials in CycloneDX format
agbom = generate_agbom(acs_agent, format="cyclonedx")
print(agbom.to_json(indent=2))
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"metadata": {
"timestamp": "2024-01-15T10:30:00Z",
"component": {
"type": "ai-agent",
"name": "research-assistant",
"version": "1.0.0"
}
},
"components": [
{
"type": "ai-model",
"name": "gpt-4",
"version": "2024-01-01",
"supplier": "OpenAI",
"properties": [
{"name": "parameters", "value": "175B"},
{"name": "context_window", "value": "128000"}
]
},
{
"type": "tool",
"name": "web-search",
"version": "2.1.0",
"description": "Search the web for information"
}
]
}
{
"jsonrpc": "2.0",
"method": "acs/agbom/generate",
"params": {
"format": "cyclonedx",
"agent_id": "research-assistant"
}
}
// Response
{
"jsonrpc": "2.0",
"result": {
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"metadata": {
"timestamp": "2024-01-15T10:30:00Z",
"component": {
"type": "ai-agent",
"name": "research-assistant",
"version": "1.0.0"
}
},
"components": [
{
"type": "ai-model",
"name": "claude-3-opus",
"version": "2024-01-01",
"supplier": "Anthropic"
}
]
}
}
# Request AgBOM via A2A
apiVersion: a2a.io/v1
kind: AgBOMRequest
metadata:
name: research-assistant-agbom
spec:
agent: research-assistant
format: cyclonedx
includeRuntime: true
---
# Response
apiVersion: a2a.io/v1
kind: AgBOM
metadata:
name: research-assistant-agbom
timestamp: "2024-01-15T10:30:00Z"
spec:
format: cyclonedx
version: "1.0.0"
components:
- type: ai-agent
name: research-assistant
models:
- name: claude-3-opus
version: "2024-01-01"
supplier: Anthropic
tools:
- name: web-search
version: "2.1.0"
from acs.inspect import generate_agbom
# Generate Agent Bill of Materials in SPDX format
agbom = generate_agbom(acs_agent, format="spdx")
print(agbom.to_string())
SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: research-assistant-agbom
DocumentNamespace: https://example.com/agbom/research-assistant
Creator: Tool: acs-sdk-1.0
Created: 2024-01-15T10:30:00Z
# Package: AI Agent
PackageName: research-assistant
SPDXID: SPDXRef-Agent
PackageVersion: 1.0.0
PackageSupplier: Organization: Example Corp
PackageDownloadLocation: NOASSERTION
# AI Model Component
PackageName: gpt-4
SPDXID: SPDXRef-Model-GPT4
PackageVersion: 2024-01-01
PackageSupplier: Organization: OpenAI
PackageProperty: ModelParameters: 175B
PackageProperty: ContextWindow: 128000
# Tool Component
PackageName: web-search
SPDXID: SPDXRef-Tool-WebSearch
PackageVersion: 2.1.0
PackageDescription: Search the web for information
from acs.inspect import DynamicAgBOM
import asyncio
# Enable dynamic AgBOM updates
dynamic_agbom = DynamicAgBOM(acs_agent)
# Subscribe to capability changes
@dynamic_agbom.on_update
async def on_capability_change(event):
print(f"Agent capability changed: {event.change_type}")
print(f"Component: {event.component}")
# Automatically update AgBOM
new_agbom = await dynamic_agbom.regenerate()
# Notify security systems
await notify_security_team(new_agbom)
# Example: Agent discovers new tool at runtime
await acs_agent.run("I need to analyze this PDF document")
# Output:
# Agent capability changed: COMPONENT_ADDED
# Component: {"type": "tool", "name": "pdf-analyzer", "version": "1.2.0"}
#
# New component automatically added to AgBOM
import { DynamicAgBOM, AgBOMEvent } from '@acs/inspect'
// Create dynamic AgBOM monitor
const dynamicAgBOM = new DynamicAgBOM(acsAgent)
// Listen for capability changes
dynamicAgBOM.on('update', async (event: AgBOMEvent) => {
console.log(`Capability ${event.changeType}: ${event.component.name}`)
// Get updated AgBOM
const currentAgBOM = await dynamicAgBOM.getCurrent()
// Validate against policies
const validation = await validateAgainstPolicy(currentAgBOM)
if (!validation.approved) {
// Disable unapproved capability
await acsAgent.disableComponent(event.component.id)
console.warn(`Disabled unapproved component: ${event.component.name}`)
}
})
// Example output when agent adds new model:
// Capability ADDED: claude-3-opus
// Validating against policy...
// Component approved and active