Skip to content

ANCHOR

ANCHOR is ASHA's mission-aware governance layer for autonomous agents. It keeps agents aligned with a compiled mission contract, gates dangerous tool calls, detects suspicious action chains, validates plan outputs, and enforces OS-level side-effect constraints during tool execution.

Governance runs at runtime: agents and prompts do not need to mention ANCHOR.

Developer preview

ANCHOR is preview in v0.4.2. Pin asha==0.4.2 and validate on your agent stack. See status.md.


One-line adoption

from asha import Agent, anchor

agent = anchor(
    Agent(provider="mock", model="mock", tools=["read_file", "email"]),
    risk_tolerance="LOW",
    isolation="auto",
    interactive=False,  # required for scripts / CI
)
result = agent.run("Analyze trends locally and write a report.")

Framework-specific entry points:

from asha.runtime.anchor.adapters import (
    anchor_crewai,
    anchor_langchain,
    anchor_graph,  # LangGraph
    anchor_autogen,
    anchor_llamaindex,
    anchor_mcp,
    anchor_any,
)

Install only what you need (lite pip install asha-si stays free of framework deps):

pip install asha-si[crewai]
pip install asha-si[langchain]
pip install asha-si[langgraph]
pip install asha-si[llamaindex]
pip install asha-si[mcp]
pip install asha-si[anchor]   # all of the above

Adapter support matrix

Framework Tier Install Entry point
CrewAI Supported (golden) asha-si[crewai] anchor_crewai()
LangChain Supported (golden) asha-si[langchain] anchor_langchain()
LangGraph Supported (golden) asha-si[langgraph] anchor_graph()
LlamaIndex Best-effort asha-si[llamaindex] anchor_llamaindex()
MCP Best-effort asha-si[mcp] anchor_mcp()
AutoGen Experimental (duck-typed) anchor_autogen()
Generic Experimental (stdlib) anchor_generic() / anchor_any()

CI installs asha-si[anchor] and runs golden-path + mission-drift gates headless (ASHA_ANCHOR_INTERACTIVE=0, ASHA_ANCHOR_WARN_POLICY=strict). Interactive ANCHOR demo: streamlit run examples/showcase/streamlit_app.py (Rogue agent page).


What ANCHOR governs

Layer Applied
Mission baseline + phase refresh kickoff / invoke / generate_reply
Action guard + chain guard every tool call
Memory guard after each agent step
Plan guard after each agent step
OS sandbox during tool execution
Human approval BLOCK/REVIEW on TTY when interactive=True
Framework Entry point What's wrapped
CrewAI anchor_crewai() Crew, Agent, Task, Tool, LLM (litellm)
LangChain anchor_langchain() AgentExecutor, Runnable, BaseTool, callbacks
LangGraph anchor_graph() StateGraph / compiled graph (invoke / ainvoke / stream / astream)
AutoGen anchor_autogen() function_map, register_for_execution
LlamaIndex anchor_llamaindex() query/chat/run, agent_worker tools
MCP anchor_mcp() call_tool / acall_tool (non-destructive proxy)
Generic anchor_generic() any run/invoke + tools

Adapters are duck-typed. Missing optional packages raise a clear pip install asha-si[...] hint when the target module requires that framework.


Explicit ALLOW / BLOCK checks

from asha.runtime.anchor import AnchorRuntime
from asha.exceptions import ASHAAnchorBlocked

runtime = AnchorRuntime(warn_policy="strict", interactive=False, risk_tolerance="LOW")
runtime.initialize_mission(
    "Analyze Q1 sales locally and write a report. Do not send data externally.",
    context={
        "available_tools": ["load_sales_data", "write_report", "send_email"],
        "local_only": True,
    },
)

assert runtime.evaluate_action_request(
    "tool_call",
    {
        "tool_name": "write_report",
        "arguments": str({"args": (), "kwargs": {"report_content": "Q1 summary"}}),
    },
)

try:
    runtime.evaluate_action_request(
        "tool_call",
        {
            "tool_name": "send_email",
            "arguments": str({"args": (), "kwargs": {"to": "exfil@evil.com"}}),
        },
        raise_on_block=True,
    )
except ASHAAnchorBlocked as err:
    print(err)

Framework sketches

These assume the optional framework package is installed.

CrewAI

from crewai import Crew
from asha.runtime.anchor.adapters import anchor_crewai

crew = Crew(agents=[...], tasks=[...])
crew = anchor_crewai(crew, risk_tolerance="LOW", isolation="auto", interactive=False)
crew.kickoff(inputs={"topic": "Generative AI"})

LangChain

from langchain.agents import AgentExecutor
from asha.runtime.anchor.adapters import anchor_langchain

executor = AgentExecutor(agent=agent, tools=tools)
executor = anchor_langchain(executor, risk_tolerance="LOW", interactive=False)
result = executor.invoke({"input": "Analyze trends locally."})

LangGraph

from langgraph.graph import StateGraph
from asha.runtime.anchor.adapters import anchor_graph

graph = StateGraph(...)
# wrap before or after compile (both work)
app = anchor_graph(graph.compile(), risk_tolerance="LOW", interactive=False)
app.invoke({"messages": [...]})

AutoGen

from autogen import ConversableAgent
from asha.runtime.anchor.adapters import anchor_autogen

agent = ConversableAgent("analyst", llm_config={...})
agent = anchor_autogen(agent, risk_tolerance="LOW", interactive=False)
agent.generate_reply(messages=[{"role": "user", "content": "Analyze locally."}])

LlamaIndex

from llama_index.core.agent import ReActAgent
from asha.runtime.anchor.adapters import anchor_llamaindex

agent = ReActAgent.from_tools(tools, llm=llm)
agent = anchor_llamaindex(agent, risk_tolerance="LOW", interactive=False)
agent.query("Analyze trends locally and write a report.")

MCP

from asha.runtime.anchor.adapters import anchor_mcp

server = anchor_mcp(mcp_server, risk_tolerance="LOW", interactive=False)
server.initialize_session("Analyze data locally. Do not exfiltrate.")
result = server.call_tool("read_file", {"path": "data/trends.csv"})

OS sandbox

Mode Behavior
off No sandbox enforcement
auto (default) In-process hooks: guarded open(), blocked network modules (best-effort, not a full OS jail)
hard / subprocess Tool runs in an isolated child process
from asha import Agent, anchor

agent = anchor(Agent(provider="mock", model="mock"), isolation="hard", interactive=False)

Supported modes: off, auto (in-process hooks), hard / subprocess (child process).
This is preview isolation, not a container/seccomp jail. Container backends (docker, bwrap) raise a clear error and are deferred to 0.5.x; use hard today when you need process isolation.


Human approval

When interactive=True (or a TTY with default resolution), ANCHOR can prompt on BLOCK/REVIEW:

ANCHOR HUMAN APPROVAL REQUIRED
Reason: ...
Allow this action? [y/N]:

Configure with interactive=True/False, ASHA_ANCHOR_INTERACTIVE=0, or warn_policy="strict"|"permissive".


Mental model

User mission prompt
       │
       ▼
 Mission contract (immutable baseline + phases)
       │
       ▼
 Tool call / memory write / plan output
       │
       ├── Action / alignment checks
       ├── Chain checks
       ├── Memory checks
       ├── Plan checks
       └── Approval (TTY or headless policy)
       │
       ▼
 Sandbox (hooks or subprocess)

Public docs describe the governance shape, not internal detector scores or calibration tables.


API surface

Function Description
anchor(target) Universal one-line adoption (from asha import anchor)
anchor_any(target) Adapter registry dispatch
anchor_crewai / anchor_langchain / … Framework wrappers
AnchorRuntime Direct mission + evaluate_action_request

Environment variables

Variable Default Description
ASHA_ANCHOR_INTERACTIVE auto (TTY) 0 disables human approval prompts
ASHA_ANCHOR_WARN_POLICY permissive (headless) strict escalates WARN / REVIEW more aggressively
ASHA_ANCHOR_TELEMETRY off 1 enables local JSONL telemetry (opt-in)
ASHA_ANCHOR_TELEMETRY_PATH none Explicit telemetry file path (also enables logging)