Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docs/INTERFACE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ guild_mediated requires two-party cryptographic participation, a Guild-observed
- `GET /.well-known/glama.json`
- `POST /adjudicators/enroll`
- `POST /admin/agents/{agent_id}/first-party`
- `POST /admin/index/cycle`
- `POST /admin/issuer/rotate`
- `GET /agents`
- `GET /agents.md`
Expand Down
33 changes: 13 additions & 20 deletions live/guild/app/a2a.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,23 +392,17 @@ def _agent_card(base: str) -> dict[str, Any]:
"protocolVersion": "0.3.0",
"name": "Agent Guild",
"description": (
"Can I safely use or pay this endpoint right now? Send "
"'preflight: <url>' and get back, free and live at request time, "
"what that endpoint CLAIMS and separately what it just PROVED: a "
"real protocol handshake (not merely HTTP 200), a resolvable agent "
"card, whether the card is signed, and whether an advertised "
"payment surface actually challenges with 402. Measured "
"2026-07-31: 92.9% of registry-listed agents report healthy and "
"33.9% complete a task. Send 'index' to search every endpoint we "
"have observed. 'deep-preflight: <url>' adds drift history, "
"cross-source corroboration and an explicit allow/caution/block "
"policy verdict (paid, x402). Also free, and supporting rather "
"than the headline: a portable Agent Passport for your own agent "
'(POST /agents/register {"name": "<you>", "capabilities": [...], '
'"src": "passport_offer:agent_card"} -> POST /agents/{id}/prove '
"-> GET /agents/{id}/passport), and 'check: <capability>' to find "
"the safest agent for a job. Richer surface over MCP and REST; see "
"documentationUrl."
"The trust and settlement layer for AI agents. Claim a portable, "
"verifiable Agent Passport — free, three calls: POST "
'/agents/register {"name": "<you>", "capabilities": [...], '
'"src": "passport_offer:agent_card"} → POST /agents/{id}/prove '
"(then /prove/verify) → GET /agents/{id}/passport, a Guild-signed "
"credential any party verifies offline. Also: send a text message "
"naming a capability (e.g. 'check: fact-check') and receive the "
"safest agent to hire, a hire/caution/avoid verdict, a ranked "
"shortlist, and provenance-labelled proof — the same answer as "
"GET /check. Richer surface (attest, escrow) over MCP and REST; "
"see documentationUrl."
),
"url": f"{base}/a2a",
"preferredTransport": "JSONRPC",
Expand Down Expand Up @@ -936,7 +930,7 @@ async def a2a_endpoint(request: Request):
# task TEXT every A2A client renders — not only in metadata,
# which actor a2a:net:bba57b53… demonstrably never parsed.
task = a2a_x402.build_payment_required_task(
preq, preq.cost, demand_ctx=dem, actor=actor, ua=ua_tag)
preq, preq.cost, demand_ctx=dem)
store.record_event(actor, "x402_payment_required", ua=ua_tag,
endpoint="best_agent", transport="a2a",
capability=caller_cap)
Expand All @@ -959,8 +953,7 @@ async def a2a_endpoint(request: Request):
_target = _pfd.group(2)
if _x402_a2a_active():
preq = payments.deep_preflight_request(_target)
task = a2a_x402.build_payment_required_task(
preq, preq.cost, actor=actor, ua=ua_tag)
task = a2a_x402.build_payment_required_task(preq, preq.cost)
store.record_event(actor, "x402_payment_required", ua=ua_tag,
endpoint="preflight_deep", transport="a2a",
target=_target[:300])
Expand Down
69 changes: 4 additions & 65 deletions live/guild/app/a2a_x402.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,6 @@ def _challenge_text(required: dict[str, Any], ctx: Optional[dict[str, Any]],

def build_payment_required_task(preq: PaidRequest, credits_cost: int,
demand_ctx: Optional[dict[str, Any]] = None,
actor: str = "",
ua: str = "",
) -> dict[str, Any]:
"""Create + persist a payment task and return the input-required Task."""
task_id = "x402task_" + uuid.uuid4().hex
Expand All @@ -182,17 +180,6 @@ def build_payment_required_task(preq: PaidRequest, credits_cost: int,
"request_hash": preq.request_hash,
"credits_cost": credits_cost,
"capability": dict(preq.query).get("capability"),
# The EXACT operation and its canonical parameters. A2A quotes in one
# message and settles in another, so the operation must survive the
# round trip; rebuilding it from a default made a deep-preflight
# challenge settle the wrong operation and return the wrong product.
# Stored here, on OUR record, and never read back from the submission.
"operation_params": dict(preq.query),
# Who was quoted. Recorded at quote time so the settled event is
# attributable to the same caller under the central attribution rule —
# an unattributable settlement can never be a customer.
"actor": actor or "",
"ua": ua or "",
"required": required,
"receipts": [],
"created_at_epoch": time.time(),
Expand Down Expand Up @@ -302,7 +289,8 @@ def handle_payment_submission(message: dict[str, Any],
# Produce the paid result, bind receipt+evidence to its exact bytes.
# demand for this request was recorded pre-authorization (B1) when the
# payment-required task was created — never count it again on payment.
result = _produce_for(preq, settled, task)
result = store.check(dict(preq.query).get("capability") or "",
demand_recorded=True)
body = json.dumps(result, default=str).encode("utf-8")
fin = settled.finalize(body)
settle_response = _settle_response({
Expand Down Expand Up @@ -356,58 +344,9 @@ def _settle_response(settle: dict[str, Any],
return out


def _produce_for(preq: PaidRequest, settled: Any,
task: dict[str, Any]) -> dict[str, Any]:
"""Produce the product that was actually paid for.

Every branch records its own settlement metadata, because "the gateway
settled" and "money moved on mainnet" are different claims and only the
second one is revenue."""
from . import deepcheck
facts = {
"settlement_mode": "x402",
"settlement_confirmed": bool((settled.record or {}).get("confirmed")),
"settlement_mainnet": bool((settled.record or {}).get("mainnet")),
"settlement_network": (settled.record or {}).get("network"),
"settlement_amount_atomic": (settled.record or {}).get("amount_atomic"),
"settlement_tx": (settled.record or {}).get("transaction"),
}
params = dict(preq.query)
actor = task.get("actor") or "a2a"
ua = task.get("ua") or "a2a/x402"
if preq.operation == "deep_preflight":
url = params.get("url") or ""
out = deepcheck.deep_preflight(store, url)
store.record_event(actor, "deep_preflight_run", ua=ua,
endpoint="preflight_deep", transport="a2a",
target=url, paid=True,
verdict=(out.get("policy") or {}).get("decision"),
**facts)
return out
if preq.operation == "evidence_bundle":
url = params.get("url") or ""
out = deepcheck.evidence_bundle(
store, url, ttl_s=int(params.get("ttl_seconds") or 3600))
store.record_event(actor, "evidence_bundle_issued", ua=ua,
endpoint="evidence_bundle", transport="a2a",
target=url, paid=True, **facts)
return out
return store.check(params.get("capability") or "", demand_recorded=True)


def _preq_from_task(task: dict[str, Any]) -> PaidRequest:
"""Reconstruct the quoted request from OUR OWN stored task record.

Only `operation` and `operation_params` are consulted, both written by us
at quote time. Nothing from the payment submission reaches this function —
a caller must not be able to steer settlement onto a different operation
than the one they were quoted."""
operation = task.get("operation") or "best_agent"
params = task.get("operation_params")
if not isinstance(params, dict):
# legacy tasks quoted before operation_params existed
params = {"capability": task.get("capability") or ""}
return payments.request_from_stored(operation, params)
cap = task.get("capability") or ""
return payments.check_request(cap)


_ERR_CODES = {
Expand Down
Loading
Loading