From fb9d63e7965056be8d6871f32a0b2a77c0f83a4e Mon Sep 17 00:00:00 2001 From: AgentTanuki <294486129+AgentTanuki@users.noreply.github.com> Date: Sun, 16 Aug 2026 19:05:57 +0100 Subject: [PATCH] Add AID v2 well-known discovery --- docs/INTERFACE.md | 2 + live/guild/app/main.py | 25 ++++++++++++ live/guild/contract/contract.json | 7 ++++ live/guild/contract/generate.py | 2 + live/guild/tests/test_agent_native.py | 1 + live/guild/tests/test_aid_discovery.py | 54 ++++++++++++++++++++++++++ 6 files changed, 91 insertions(+) create mode 100644 live/guild/tests/test_aid_discovery.py diff --git a/docs/INTERFACE.md b/docs/INTERFACE.md index 988c8e9..d69b268 100644 --- a/docs/INTERFACE.md +++ b/docs/INTERFACE.md @@ -5,6 +5,7 @@ - Host: https://agent-guild-5d5r.onrender.com - MCP (streamable HTTP): https://agent-guild-5d5r.onrender.com/mcp/ - MCP x402 payment safety: https://agent-guild-5d5r.onrender.com/mcp/payment-safety/ +- AID v2 discovery: https://agent-guild-5d5r.onrender.com/.well-known/agent - ARD catalogue: https://agent-guild-5d5r.onrender.com/.well-known/ai-catalog.json - A2A JSON-RPC: https://agent-guild-5d5r.onrender.com/a2a · agent card: https://agent-guild-5d5r.onrender.com/.well-known/agent-card.json - Issuer DID: https://agent-guild-5d5r.onrender.com/.well-known/agent-guild-did.json @@ -24,6 +25,7 @@ guild_mediated requires two-party cryptographic participation, a Guild-observed - `GET /` - `GET /.well-known/402index-verify.txt` +- `GET /.well-known/agent` - `GET /.well-known/agent-guild-did.json` - `GET /.well-known/agent-guild.json` - `GET /.well-known/agent-skills/agent-guild/SKILL.md` diff --git a/live/guild/app/main.py b/live/guild/app/main.py index cbb2e8a..8076956 100644 --- a/live/guild/app/main.py +++ b/live/guild/app/main.py @@ -230,6 +230,7 @@ async def _lifespan(app: "FastAPI"): # exactly ONE row per fetch here; downstream measurement deduplicates the # privacy-safe actor and excludes our traffic, generic tools and crawlers. _DISCOVERY_RESOURCE_SURFACES = { + "/.well-known/agent": "aid_v2", "/.well-known/ai-catalog.json": "ard_catalog", "/.well-known/agent-guild.json": "guild_manifest", "/.well-known/integrations.json": "integrations_owner_declaration", @@ -3897,6 +3898,7 @@ def _manifest() -> dict: }, "discovery": { "capabilities": "/capabilities", + "aid_v2": "/.well-known/agent", "ard_catalog": "/.well-known/ai-catalog.json", "ard_agentmap": "/robots.txt", "discovery_census": { @@ -4601,6 +4603,29 @@ def wellknown_integrations(): ) +@app.get("/.well-known/agent") +def wellknown_aid_v2(): + """AID v2 HTTPS fallback for domain-first agent discovery. + + Agent Identity & Discovery clients query ``_agent.`` first and may + fall back to this exact path when no DNS TXT record exists. Keep the + record deliberately minimal: TLS already binds it to this host, and + omitting ``k`` avoids claiming the separate RFC 9421 PKA profile. + """ + base = x402.public_host().rstrip("/") + return JSONResponse( + content={ + "v": "aid2", + "u": f"{base}/mcp", + "p": "mcp", + "a": "none", + "s": "Agent Guild trust and settlement", + "d": f"{base}/for-agents", + }, + headers={"Cache-Control": "public, max-age=300, s-maxage=300"}, + ) + + @app.get("/.well-known/mcp/payment-safety-server-card.json") async def wellknown_payment_safety_mcp_server_card(): """Static discovery for the focused, one-tool payment-safety server.""" diff --git a/live/guild/contract/contract.json b/live/guild/contract/contract.json index 7e28e2a..b0f6e70 100644 --- a/live/guild/contract/contract.json +++ b/live/guild/contract/contract.json @@ -212,6 +212,12 @@ ], "path": "/.well-known/402index-verify.txt" }, + { + "methods": [ + "GET" + ], + "path": "/.well-known/agent" + }, { "methods": [ "GET" @@ -1109,6 +1115,7 @@ "service": { "a2a_endpoint": "https://agent-guild-5d5r.onrender.com/a2a", "agent_card": "https://agent-guild-5d5r.onrender.com/.well-known/agent-card.json", + "aid_well_known": "https://agent-guild-5d5r.onrender.com/.well-known/agent", "ard_catalog": "https://agent-guild-5d5r.onrender.com/.well-known/ai-catalog.json", "did_web_document": "https://agent-guild-5d5r.onrender.com/.well-known/did.json", "host": "https://agent-guild-5d5r.onrender.com", diff --git a/live/guild/contract/generate.py b/live/guild/contract/generate.py index 42e1290..c38a22a 100644 --- a/live/guild/contract/generate.py +++ b/live/guild/contract/generate.py @@ -81,6 +81,7 @@ def build_contract() -> dict: f"{HOST}/.well-known/mcp/" "payment-safety-server-card.json" ), + "aid_well_known": f"{HOST}/.well-known/agent", "ard_catalog": f"{HOST}/.well-known/ai-catalog.json", "a2a_endpoint": f"{HOST}/a2a", "agent_card": f"{HOST}/.well-known/agent-card.json", @@ -409,6 +410,7 @@ def derived_interface_md(contract: dict) -> str: f"- Host: {s['host']}", f"- MCP (streamable HTTP): {s['mcp_url']}", f"- MCP x402 payment safety: {s['payment_safety_mcp_url']}", + f"- AID v2 discovery: {s['aid_well_known']}", f"- ARD catalogue: {s['ard_catalog']}", f"- A2A JSON-RPC: {s['a2a_endpoint']} · agent card: {s['agent_card']}", f"- Issuer DID: {s['issuer_did_document']}", diff --git a/live/guild/tests/test_agent_native.py b/live/guild/tests/test_agent_native.py index ab12be2..33fe80e 100644 --- a/live/guild/tests/test_agent_native.py +++ b/live/guild/tests/test_agent_native.py @@ -16,6 +16,7 @@ def test_discovery_manifest_describes_service(): assert m["economics"]["pricing_credits"]["best_agent"] == PRICING["best_agent"] # an agent can learn how to acquire credits with no human assert m["economics"]["acquire_credits"]["trial"]["human_free"] is True + assert m["discovery"]["aid_v2"] == "/.well-known/agent" assert "measured_lift" in m["evaluation_signals"] diff --git a/live/guild/tests/test_aid_discovery.py b/live/guild/tests/test_aid_discovery.py new file mode 100644 index 0000000..fca6adc --- /dev/null +++ b/live/guild/tests/test_aid_discovery.py @@ -0,0 +1,54 @@ +"""Agent Identity & Discovery (AID) v2 HTTPS fallback.""" +from urllib.parse import urlparse + +from fastapi.testclient import TestClient + +from app import main, x402 + + +def test_aid_v2_well_known_fallback_is_valid_and_cacheable(): + client = TestClient(main.app) + response = client.get( + "/.well-known/agent", + headers={"user-agent": "aid-python/2.1"}, + follow_redirects=False, + ) + + assert response.status_code == 200 + assert response.headers["content-type"].startswith("application/json") + assert response.headers["cache-control"] == ( + "public, max-age=300, s-maxage=300") + assert len(response.content) < 64 * 1024 + + base = x402.public_host().rstrip("/") + assert response.json() == { + "v": "aid2", + "u": f"{base}/mcp", + "p": "mcp", + "a": "none", + "s": "Agent Guild trust and settlement", + "d": f"{base}/for-agents", + } + assert urlparse(response.json()["u"]).scheme == "https" + assert len(response.json()["s"].encode("utf-8")) <= 60 + + +def test_aid_fetch_is_one_noncommercial_discovery_observation(): + client = TestClient(main.app) + before = len(main.store.events) + + response = client.get( + "/.well-known/agent", + headers={"user-agent": "aid-python/2.1"}, + ) + + assert response.status_code == 200 + new = main.store.events[before:] + observations = [event for event in new + if event["type"] == "discovery_resource_fetched"] + assert len(observations) == 1 + assert observations[0]["discovery_surface"] == "aid_v2" + assert not any(event["type"] in { + "capability_demand", "offer_served", "paid_offer_served", + "paid_offer_shown", + } for event in new)