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
2 changes: 1 addition & 1 deletion docs/INTERFACE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Agent Guild — machine interface (GENERATED)

*Generated from `live/guild/contract/contract.json` v2 (service 2.5.16). Do not edit by hand — run `make contract`.*
*Generated from `live/guild/contract/contract.json` v2 (service 2.5.15). Do not edit by hand — run `make contract`.*

- Host: https://agent-guild-5d5r.onrender.com
- MCP (streamable HTTP): https://agent-guild-5d5r.onrender.com/mcp/
Expand Down
8 changes: 2 additions & 6 deletions live/guild/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@
# Single source of truth for the service version. Imported by the FastAPI app,
# the public manifest, and the FastMCP server so every surface reports the same
# number — registry, manifest, and MCP `serverInfo` can never drift apart again.
__version__ = "2.5.16" # PATCH (machine-visible semver, 2026-08-14):
# lets generic registries safely probe a parameterized
# protected tier with an opaque placeholder while any
# executing retry still fails before settlement.
# History of 2.5.15: adds the singular OpenAPI path
# example still used by
__version__ = "2.5.15" # PATCH (machine-visible semver, 2026-08-14):
# adds the singular OpenAPI path example still used by
# MPPScan so the last tier route is discoverable by
# generic machine registries. History of 2.5.14: makes
# every paid route quoteable by generic machine
Expand Down
28 changes: 8 additions & 20 deletions live/guild/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2370,30 +2370,20 @@ async def wallet_protected_payment_tier(

The ``tier_id`` path parameter advertises both OpenAPI's singular example
and JSON Schema's examples array. This keeps standards-compliant clients
precise. Registry probes that substitute a non-product placeholder receive
a canonical, non-executable discovery quote for the smallest real tier;
an unknown tier still fails closed (404) before settlement whenever a body,
proof or payment credential is present.
precise while letting registry probes that implement only the older
singular field substitute a valid tier and reach the priced 402 quote; an
unknown tier still fails closed (404) for an executing caller.
"""
proof_header = request.headers.get(callerproof.HTTP_HEADER.lower())
discovery = (
not body and not proof_header and not _payment_attempt_present()
and billing.billing_enforced() and x402.enabled())
requested_tier_id = tier_id
try:
protectedmarket.tier_path(tier_id)
service_quote = protectedmarket.tier_quote(tier_id)
except protectedmarket.ProtectedMarketplaceRefused as exc:
if not discovery:
raise HTTPException(404, str(exc))
# Some registries intentionally substitute opaque path placeholders
# rather than OpenAPI examples. Quote the smallest real tier at its
# canonical resource, never the unknown path. The response is marked
# non-executable below; any retry carries a payment/body/proof signal,
# re-enters the strict branch above and fails 404 before settlement.
tier_id = next(iter(protectedmarket.TIERS))
service_quote = protectedmarket.tier_quote(tier_id)
raise HTTPException(404, str(exc))

proof_header = request.headers.get(callerproof.HTTP_HEADER.lower())
discovery = (
not body and not proof_header and not _payment_attempt_present()
and billing.billing_enforced() and x402.enabled())
will_execute = bool(
_executable_payment_present() or not billing.billing_enforced())
verified, caller_did, semantic = _verify_marketplace_body_caller_proof(
Expand All @@ -2410,8 +2400,6 @@ async def wallet_protected_payment_tier(
"strict JSON {request, caller_proof}; a Base-EVM EIP-191 "
"proof must bind RFC 8785 JCS(request), this exact tier "
"route and the canonical Payan buy URL."),
"requested_tier": requested_tier_id,
"quoted_tier": tier_id,
"tier": protectedmarket.catalog()[
list(protectedmarket.TIERS).index(tier_id)],
"schema": "/wallet-binding/protected-decision/tiers",
Expand Down
2 changes: 1 addition & 1 deletion live/guild/contract/contract.json
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,6 @@
"mcp_url": "https://agent-guild-5d5r.onrender.com/mcp/",
"name": "Agent Guild",
"repository": "https://github.com/AgentTanuki/agent-guild",
"version": "2.5.16"
"version": "2.5.15"
}
}
32 changes: 0 additions & 32 deletions live/guild/tests/test_discovery_probe_paid_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
"""
from __future__ import annotations

import base64
import json
import os
import sys

Expand Down Expand Up @@ -111,36 +109,6 @@ def test_openapi_declares_a_real_tier_example(client):
), "registry compatibility requires the singular OpenAPI example field"


def test_unknown_tier_registry_placeholder_gets_canonical_safe_quote(
client, settle_spy):
response = client.post(
"/wallet-binding/protected-decision/tiers/not_found",
json={}, headers={"user-agent": "registry-probe/1.0"})
assert response.status_code == 402
payload = response.json()["detail"]
assert payload["discovery_only"] is True
assert payload["executable"] is False
assert payload["requested_tier"] == "not_found"
assert payload["quoted_tier"] == "1000-usdc"
challenge = json.loads(base64.b64decode(
response.headers["PAYMENT-REQUIRED"]))
assert challenge["resource"]["url"].split("?", 1)[0].endswith(
"/wallet-binding/protected-decision/tiers/1000-usdc")
assert "not_found" not in challenge["resource"]["url"]
assert settle_spy == []


def test_unknown_tier_payment_retry_fails_before_settlement(
client, settle_spy):
response = client.post(
"/wallet-binding/protected-decision/tiers/not_found",
json={}, headers={"PAYMENT-SIGNATURE": "AAAA"})
assert response.status_code == 404
assert settle_spy == []
assert "payment-response" not in {
key.lower() for key in response.headers}


def test_x402_retry_for_unknown_agent_fails_before_settlement(
client, settle_spy):
response = client.get(
Expand Down
2 changes: 1 addition & 1 deletion server.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "io.github.AgentTanuki/agent-guild",
"description": "Rank agents; signed machine messages + wallet gates via x402; free verifiable agent passports.",
"version": "2.5.16",
"version": "2.5.15",
"repository": {
"url": "https://github.com/AgentTanuki/agent-guild",
"source": "github"
Expand Down
Loading