sponsio.yaml reference

June 6, 2026 · View on GitHub

sponsio.yaml is the canonical way to declare contracts. sponsio scan writes it, sponsio init writes it, and Sponsio(config=...) reads it.

This page covers two things: the three ways to populate the file, and the full schema of what can live inside it.

A minimal valid file:

agents:
  bot:
    contracts:
      - name: "policy gate before refund"
        G: "must call `check_policy` before `issue_refund`"

How to populate sponsio.yaml

Three sources produce the same output: enforceable contracts loaded via Sponsio().

Source 1: Code scan          sponsio scan src/ -o sponsio.yaml
Source 2: Policy documents   sponsio scan src/ --policy security.md --llm
Source 3: Hand-written       (edit sponsio.yaml directly)


                                 sponsio.yaml


                                 guard = Sponsio(config="sponsio.yaml")

The three sources mix freely in one yaml. Each contract entry can carry a source: tag for provenance.

Source 1: code scan

Extract tools and infer constraints from agent source.

sponsio scan src/agents/ -o sponsio.yaml

Without --llm, the scan is rule-based:

  1. Finds tools (@tool decorators, Agent(tools=[...]), graph.add_node()).
  2. Extracts ordering from graph.add_edge("A", "B") and call graphs.
  3. Generates must_precede constraints for each ordering dependency.
  4. Outputs tools and constraints in yaml.

With --llm, the LLM sees the full source and discovers constraints a static scan cannot find:

  • always_followed_by (liveness obligations)
  • rate_limit (from constants like MAX_RETRIES = 3)
  • no_reversal (from business logic semantics)
sponsio scan src/agents/ --llm -o sponsio.yaml
sponsio scan src/agents/ --llm --provider gemini

Provider env vars and the full matrix: reference/cli.md.

Source 2: policy documents

Extract contracts from a policy or compliance document, using the tool inventory as context.

# Scan code first to populate the tool inventory, then add policy:
sponsio scan src/agents/ -o sponsio.yaml
sponsio scan src/agents/ --policy security_policy.md --llm -o sponsio.yaml --append

The tool inventory is critical. Without it the LLM produces generic constraints. With it, policy maps to specific tools:

Policy:           "All refunds require supervisor approval"
Tool inventory:   [check_policy, issue_refund, notify_customer]
Constraint:       must_precede(check_policy, issue_refund)

Supported document formats: .md, .txt, .pdf (pip install sponsio[pdf]).

Source 3: hand-written

Edit sponsio.yaml directly. The two forms (NL strings and structured entries) are described in Contracts below.

End-to-end workflow

sponsio scan src/agents/ --llm -o sponsio.yaml                     # 1. discover
sponsio scan src/agents/ --policy compliance.md --llm --append     # 2. policy
# 3. edit sponsio.yaml, add hand-written rules
sponsio validate --config sponsio.yaml                             # 4. validate
python my_agent.py                                                 # 5. run

Full schema

A complete file, with every top-level field:

mode: observe                          # observe | enforce
framework: langgraph                   # optional; auto-detected otherwise
sessions_dir: ~/.sponsio/sessions/     # where session logs are written

tools:                                 # optional; auto-discovered from scan
  check_policy:
    description: "Look up a customer's refund policy"
  issue_refund:
    description: "Issue a refund"

agents:
  support_bot:
    contracts:
      - name: "policy gate before refund"
        A: "called `issue_refund`"
        G: "must call `check_policy` before `issue_refund`"
        strategy: block
      - name: "refund rate limit"
        G: "tool `issue_refund` at most 5 times"
        strategy: escalate
      - name: "no destructive deletes"
        G: "bash command must not contain `rm -rf`"
        strategy: block

Top-level fields

FieldTypeDefaultNotes
modeobserve | enforceobserveGlobal default; per-contract mode: overrides.
frameworkstringauto-detectlanggraph, claude_agent, openai, openai_agents, crewai, google_adk, vercel_ai, mcp, or omitted.
sessions_dirpath~/.sponsio/sessions/Set to null to disable local session logging.
toolsmap{}Optional tool metadata; scan populates automatically.
tool_policymap{}Default-deny posture + approved-tool allowlist. See tool_policy below.
agentsmaprequiredPer-agent contract set.

tool_policy

Declarative default-deny posture. The agent can only call tools in approved: when default: deny is set. Adding a new tool to the underlying framework does not auto-trust it.

tool_policy:
  default: deny          # allow (default, backwards-compat) | deny
  approved: [search, read_file, list_dir]
  enforcement: reactive  # reactive (default) | proactive
FieldDefaultBehavior
defaultallowdeny synthesizes a tool_allowlist contract that blocks every tool not in approved. allow is a no-op (backwards-compat).
approved[]Explicit allowlist. Empty plus deny blocks every tool (useful for a complete lockdown). Accepts a flat list or {tools: [...]} for future per-host scoping.
enforcementreactivereactive: the agent still sees the full tool menu; denied calls get blocked at call time via guard_before. proactive: wrap-time adapters (LangGraph, CrewAI, OpenAI Agents SDK, Google ADK) strip denied tools from the bound toolset before the model ever sees them.

Inline equivalent on Sponsio(tool_policy={...}). The two paths produce the same synthesized contract.


agents.<id>

Each agent has a dedicated contract list. Contracts do not leak across agents.

agents:
  support_bot:
    contracts: [...]
  orchestrator:
    contracts: [...]

Contracts

Each entry in contracts: has these fields:

FieldTypeRequiredNotes
namestringnoHuman-readable label for logs and reports.
Astring | objectnoAssumption. When the rule fires. Omit for unconditional rules.
Gstring | objectyesGuarantee. The rule itself.
strategystringnoblock (DetBlock), escalate (EscalateToHuman, accepts notify: list of dotted callable paths), redirect_to_safe (substitute a pre-approved tool), warn_only (log without blocking), or a dotted callable path.
modeobserve | enforcenoPer-contract override.

Shorthand form (natural-language strings)

A: and G: accept a natural-language string; the parser matches it to a pattern:

agents:
  customer_bot:
    contracts:
      - G: "tool `check_policy` must precede `issue_refund`"
      - G: "tool `issue_refund` at most 3 times"
      - G: "response must not contain PII"

Each entry is one (assumption, guarantee) pair. A is optional, G is required. Each field can be a scalar or a list (lists are ANDed). The legacy keys E: and enforcement: are still accepted for backward compatibility.

Sponsio parses NL strings through two stages: rule-based first (free), LLM fallback last (requires API key).

Common NL forms:

tool `A` must precede `B`
tool `X` at most N times
tool `A` requires permission `perm_name`
tools `A` and `B` are mutually exclusive
after `A`, tool `B` is forbidden
tool `A` cooldown of N steps

Structured form

For patterns that need typed arguments (lists, regex tuples, threshold floats), use the structured form:

agents:
  customer_bot:
    contracts:
      - pattern: must_precede
        args: [check_policy, issue_refund]
        source: scan
      - pattern: rate_limit
        args: [issue_refund, 3]
      - G:
          pattern: arg_blacklist
          args: ["bash", "rm -rf"]

Compiled directly. No NL parsing. Auto-emitted by sponsio scan.

See the pattern catalog for the full list of deterministic patterns.


Tool declarations (optional)

tools:
  check_policy:
    description: "Look up a customer's refund policy"
    tags: [read_only, customer_data]
  issue_refund:
    description: "Issue a refund"
    tags: [destructive, financial]

Tags are arbitrary strings and can be referenced in patterns (for example, destructive_action_gate(tag="destructive")). sponsio scan populates these from your tool definitions automatically.


Validating a config

sponsio validate --config sponsio.yaml             # parse + structural
sponsio validate --config sponsio.yaml --json      # CI-friendly

Parses, type-checks, resolves every pattern reference, and reports unresolved names, mis-typed args, or atoms referenced but not registered.

sponsio doctor

Broader. Also checks framework detection, provider credentials, and session-log writability.


Loading from Python

from sponsio.langgraph import Sponsio

guard = Sponsio(config="sponsio.yaml", agent_id="support_bot")
agent = create_react_agent(model, guard.wrap(tools))

agent_id picks which entry in agents: applies. If omitted, the default is the first agent in the file.

Inline contracts add on top of yaml:

guard = Sponsio(
    config="sponsio.yaml",
    agent_id="support_bot",
    contracts=["tool `notify` at most 5 times"],
)

Next