Connect Your Own Agent

Advanced path. Complete the Quickstart first. This page assumes autonomy is installed and autonomy demo validate exits 0.

Two modes are available. Choose based on your workflow:

Mode

Command

Best for

Subprocess wrap

autonomy run <cmd>

One-shot agent runs; runtime starts and stops with the process

Persistent daemon

autonomy runtime start --demo

Iterating on agent logic without restarting governance each time


Mode 1 — Subprocess wrap

autonomy run python3 my_agent.py

ROS2 case. The same subprocess-wrap pattern is used for ROS2 governance via autonomy run ros2.launch <package> <launch_file>. That dispatch path is CE-safe (runs on the autonomy CLI from the CE distribution; no orchestrator required) and emits the five WAL-backed PASS markers described in Markers and Observability. Full walkthrough: Robotics Quickstart.

What this does:

  1. Loads the embedded demo policy.

  2. Starts an in-process policy-gated runtime on a random localhost port.

  3. Injects AUTONOMY_RUNTIME_URL=http://127.0.0.1:<port> into the subprocess environment.

  4. Exec’s python3 my_agent.py as the subprocess.

  5. Shuts the runtime down when the subprocess exits.

  6. Propagates the subprocess exit code.

Your agent reads AUTONOMY_RUNTIME_URL from the environment and calls the runtime over HTTP.

Minimal Python client

import json
import os
import urllib.error
import urllib.request

def call_tool(kind: str, params: dict) -> dict:
    base_url = os.environ["AUTONOMY_RUNTIME_URL"].rstrip("/")
    payload = json.dumps({"kind": kind, "params": params}).encode()
    req = urllib.request.Request(
        f"{base_url}/v1/tool",
        data=payload,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req) as resp:
            return json.loads(resp.read())
    except urllib.error.HTTPError as exc:
        return json.loads(exc.read())

A working reference implementation is at examples/agent.py. Run it against the demo policy with:

autonomy run python3 examples/agent.py

Mode 2 — Persistent daemon

Start the runtime with the embedded demo policy and leave it running:

autonomy runtime start --demo

The runtime listens on 127.0.0.1:7777 by default.

# Override the listen address
autonomy runtime start --demo --listen 127.0.0.1:8888

To enable tool.http_get calls, pass an allowlist at startup (default includes api.anthropic.com and ifconfig.me):

autonomy runtime start --demo --allowed-domains api.example.com,api.anthropic.com

Point your agent at the persistent runtime by setting the environment variable directly:

AUTONOMY_RUNTIME_URL=http://127.0.0.1:7777 python3 my_agent.py

Verify it is running before connecting:

curl http://127.0.0.1:7777/health
{"status":"ok"}

The tool call API

All policy-gated actions go through POST /v1/tool.

Request

{"kind": "tool.echo", "params": {"message": "hello"}}

Field

Type

Description

kind

string

Dot-separated tool identifier, e.g. tool.echo

params

object

Tool-specific parameters

Response

{"decision": "allow", "output": "hello", "policy_ref": "embedded:demo", "audit_id": "..."}

Field

Description

decision

"allow" or "deny"

output

Tool output, present on allow only

reason

Denial reason, present on deny

policy_ref

Policy bundle that produced this decision

audit_id

WAL entry identifier for this decision

HTTP status

Meaning

200

Tool call allowed and executed

403

Tool call denied — the tool was never executed

A 403 is not a server error. It means policy evaluated the call and denied it before execution.


Embedded demo policy behavior

When running without a custom policy (autonomy run with no --policy flag, or autonomy runtime start --demo), the embedded demo policy is active.

Tool kind

Policy verdict

autonomy run

autonomy runtime start --demo

tool.echo

allow

Executed

Executed

tool.http_get

allow at policy layer

Fail-closed — no allowlist flag

Allowed if endpoint is in --allowed-domains

tool.shell

deny

Never executed

Never executed

anything else

deny

Never executed

Never executed

tool.http_get is fail-closed in subprocess wrap mode. autonomy run creates the runtime with an empty allowlist and exposes no flag to override it. If your agent needs outbound HTTP calls, use the persistent daemon (Mode 2) and pass --allowed-domains.


Using a custom policy

Pass a custom bundle OCI reference from the local managed cache:

autonomy run --policy oci://localhost:5000/my-policy:v1.0 python3 my_agent.py

Or load a bundle into the managed cache first and start the runtime against it:

autonomy policy load --bundle my-policy.tar.gz
autonomy runtime start --listen 127.0.0.1:7777

See policy.md for bundle authoring, versioning, and LKG rollback.


Next steps