Skip to content

Latest commit

 

History

History
74 lines (59 loc) · 3.85 KB

README.md

File metadata and controls

74 lines (59 loc) · 3.85 KB

YEA

yea-sdk (Python)

YEA (Your Explicit Approval) is the open protocol for AI agents acting on behalf of people. Agents state an intent, services reply with proposals whose effects are listed up front, the human's signed policy decides what can go ahead without asking, and reversible commits come with an undo window.

This is the Python implementation of the spec. It's checked against the shared conformance vectors and tested for interop, in both directions, against the TypeScript reference. Python ≥ 3.10; its only dependency is cryptography.

uv add yea-sdk               # or: pip install yea-sdk; imports as `yea`
cd python && uv run pytest   # from a checkout: conformance + TS interop (node ≥ 22.18)

Unreleased: yea-sdk isn't on PyPI yet. Until it is, install it from the repo:

uv add "git+https://github.com/yea-protocol/yea#subdirectory=python"

Quickstart

from yea import Plan, Service, send, serve_tcp, update

svc = Service("cal.example", "Calendar", "Move meetings.", trust=["ed25519:…principal…"])

@svc.intent("calendar.move", "Move a meeting", {"event": "string", "to": "datetime"}, risk="low")
def move(ctx):
    e, to = ctx.params["event"], ctx.params["to"]
    return Plan(f"Move {e} to {to}", [update(f"event/{e}", "start", "…", to), send("ana@x.co", "invite")],
                apply=lambda c: {"moved": e}, revert=lambda c: None, undo_window=3600)

# await serve_tcp(svc, port=7447)   (or serve_http(svc, port=8080) for POST /yea)
from yea import connect, consent_from, consent_grant, consent_lines, generate_key, issue_grant

principal, agent = generate_key(), generate_key()   # normally: the human's key, and the agent's
g = issue_grant(principal, agent.public, [{"svc": ["cal.example"]}, {"each": {"of": "spend", "max": 50, "unit": "USD"}}])
async with await connect("yea://127.0.0.1:7447", key=agent, grants=[g]) as c:
    props = await c.intent("calendar.move", {"event": "e2", "to": "2026-09-24T15:00:00Z"})
    print(props.lens)                          # what the model reads
    r = await c.commit(props.proposals[0])     # signs the proof automatically
    if r.code == "consent_required":           # ask the human (consent_code(r.consent, p) for out-of-band):
        p = props.proposals[0]
        shown = consent_lines(r.consent, p, "cal.example")  # checks the request names p, and p is well formed
        if "why" in shown:
            raise SystemExit(shown["why"])
        print("\n".join(shown["lines"]))              # show the person p itself, escaped
        r = await c.commit(p, grants=[consent_grant(principal, agent.public, consent_from(r.consent, p))])
    await c.undo(r.receipt["id"])
    # auto=True: commit in one round trip when the grant already allows it and it's undoable
    r = await c.intent("calendar.move", {"event": "e3", "to": "2026-09-25T10:00:00Z"}, auto=True)

consent_grant signs with the principal's key. Run it in the human's own tool, never somewhere the agent can trigger it (SPEC §6.6).

API names mirror ts/src in snake_case (issue_grant, verify_grant, consent_grant, lean, lens, fit, Plan.expires_in/undo_window). examples/serve.py runs the shared example calendar on yea://127.0.0.1:7457 and http://127.0.0.1:8457/yea.

The CLI (yea), the MCP bridge for Claude Code, and the OpenAPI adapter ship with the TypeScript packages: npx @yea-protocol/cli --help once it's on npm (until then, node cli/bin/yea.js --help from a built clone of the repo). See the main README and the docs.