Skip to content
Merged
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
4 changes: 2 additions & 2 deletions agentscore_commerce/challenge/how_to_pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ async def build_how_to_pay(
"what_it_does": (
f"Hits this endpoint, receives this same 402, signs the MPP challenge on {network_name}, and "
"submits the credential back via Authorization: Payment. Either client (tempo request or "
"agentscore-pay pay --chain tempo) works both run the full MPP handshake."
"agentscore-pay pay --chain tempo) works; both run the full MPP handshake."
),
}
if recommend == "both":
Expand Down Expand Up @@ -203,7 +203,7 @@ async def build_how_to_pay(
)
elif link_cli_blocked:
stripe_block["note"] = (
"link-cli SPT path not available for this purchase Stripe link-cli caps spend requests at $500.00 "
"link-cli SPT path not available for this purchase: Stripe link-cli caps spend requests at $500.00 "
f"($50000 cents); your total is ${total_num}. Use your own Stripe account with the SharedPaymentToken "
"API instead."
)
Expand Down
8 changes: 4 additions & 4 deletions agentscore_commerce/checkout.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""High-level Checkout orchestrator; composes 402-emit + verify+settle.

The Checkout primitive collapses the agent-commerce dance (emit 402 →
The Checkout primitive collapses the agentic-commerce dance (emit 402 →
verify+settle on retry → respond) into a single ``await
checkout.handle(request)`` call. It services every merchant shape:

Expand Down Expand Up @@ -30,7 +30,7 @@
``reference_id`` (a UUID minted on first contact). Goods merchants persist
this as their order id; API merchants treat it as a per-call request id.

Usage (goods seller, full agent-commerce flow)::
Usage (goods seller, full agentic-commerce flow)::

checkout = Checkout(
rails={
Expand Down Expand Up @@ -811,7 +811,7 @@ def _strip_payment_headers_from_raw(raw: Any) -> Any:


class Checkout:
"""High-level agent-commerce orchestrator.
"""High-level agentic-commerce orchestrator.

Composes :func:`build_accepted_methods`, :func:`build_how_to_pay`,
:func:`respond_402`, :func:`verify_x402_request`, and
Expand Down Expand Up @@ -1116,7 +1116,7 @@ def _x402_base_network(self) -> str | None:
return None

async def handle(self, request: CheckoutRequest) -> CheckoutResult:
"""One-call agent-commerce flow.
"""One-call agentic-commerce flow.

* x402 ``X-Payment`` header present → verify, settle via ``x402_server``,
run ``on_settled`` hook, return 200 with the hook's body (or
Expand Down
6 changes: 3 additions & 3 deletions agentscore_commerce/discovery/agentscore_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def build_agentscore_onboarding_steps(
compatible_fragment = ", ".join(hint for rail, hint in compatible_hints if rail in accepted_rails)

compatible_clients_clause = (
f"the rails table also lists per-rail `compatible_clients` {compatible_fragment}. "
f"the rails table also lists per-rail `compatible_clients`: {compatible_fragment}. "
if compatible_fragment
else ""
)
Expand All @@ -114,7 +114,7 @@ def build_agentscore_onboarding_steps(
)
bootstrap_step = (
"First-run only: bootstrap wallet + Passport. Run `agentscore-pay agent-guide --json` "
"for the canonical cold-start path it walks `agentscore-pay init` "
"for the canonical cold-start path; it walks `agentscore-pay init` "
"(creates keystore + per-chain wallet), `agentscore-pay passport login` "
f"(one-time KYC{'; required for this merchant' if requires_kyc else ''}; "
"the human completes a verify URL once and pay caches the operator_token), "
Expand All @@ -123,7 +123,7 @@ def build_agentscore_onboarding_steps(
)
stripe_fallback_step = (
"If your only payment method is a Stripe / Link card (no crypto), install `@stripe/link-cli` "
"instead of agentscore-pay and use it on the SPT rail. Identity gating still applies the "
"instead of agentscore-pay and use it on the SPT rail. Identity gating still applies: the "
"merchant's 403 with `verify_url` lets you bootstrap a Passport even with no crypto wallet involved."
)
returning_user_step = (
Expand Down
18 changes: 9 additions & 9 deletions agentscore_commerce/discovery/llms_txt.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def llms_txt_identity_section(
aip_bullet = ""
if aip:
aip_bullet = (
"\n- **`Agent-Identity: <JWT>` + RFC 9421 signature** present an Agent Identity Token (AIP) "
"\n- **`Agent-Identity: <JWT>` + RFC 9421 signature**: present an Agent Identity Token (AIP) "
"from a trusted issuer (AgentScore is always trusted). Short-lived and bound to your key; sign "
"the request (`Signature-Input` + `Signature` over `@method @authority @path agent-identity`, "
"tag `agent-identity`) to prove possession. No long-lived token on the wire. Mint one with "
Expand All @@ -50,13 +50,13 @@ def llms_txt_identity_section(
compliance_note = f"\n\nCompliance: {', '.join(parts)}."
return (
"## Identity\n\n"
"AgentScore identity is reusable across every AgentScore-gated merchant one KYC, "
"AgentScore identity is reusable across every AgentScore-gated merchant: one KYC, "
"no re-verification per site. Pick a header:\n\n"
"- **`X-Wallet-Address: 0x...` or base58** works on signing rails (Tempo, x402, "
"- **`X-Wallet-Address: 0x...` or base58**: works on signing rails (Tempo, x402, "
"Solana MPP). The wallet you claim must sign the payment.\n"
"- **`X-Operator-Token: opc_...`** works on every rail, including Stripe SPT. "
"- **`X-Operator-Token: opc_...`**: works on every rail, including Stripe SPT. "
f"Reusable across AgentScore merchants until expiry.{aip_bullet}\n"
"- **Neither** you get a 403 with `verify_url`. Complete the session flow once and "
"- **Neither**: you get a 403 with `verify_url`. Complete the session flow once and "
f"reuse the resulting `opc_...` everywhere.{compliance_note}"
)

Expand Down Expand Up @@ -103,22 +103,22 @@ def _llms_txt_payment_section_compact(*, rails: list[str], app_url: str) -> str:
rails_list = list(rails)
if _has_rail_family(rails_list, "tempo-"):
lines.append(
"- **Tempo USDC via MPP** "
"- **Tempo USDC via MPP**: "
f"`tempo request -X POST -H \"X-Operator-Token: opc_...\" --json '{{...}}' --max-spend N {app_url}`"
)
if _has_rail_family(rails_list, "x402-base-"):
lines.append(
f"- **x402 USDC on Base** (EIP-3009) `agentscore-pay pay POST {app_url} --chain base "
f"- **x402 USDC on Base** (EIP-3009): `agentscore-pay pay POST {app_url} --chain base "
"-H \"X-Operator-Token: opc_...\" -d '{...}'`"
)
if _has_rail_family(rails_list, "mpp-solana-"):
lines.append(
f"- **USDC on Solana** `agentscore-pay pay POST {app_url} --chain solana "
f"- **USDC on Solana**: `agentscore-pay pay POST {app_url} --chain solana "
"-H \"X-Operator-Token: opc_...\" -d '{...}'`"
)
if "stripe-spt" in rails_list:
lines.append(
"- **Stripe Shared Payment Token** agent mints SPT (own Stripe account scoped to networkId, "
"- **Stripe Shared Payment Token**: agent mints SPT (own Stripe account scoped to networkId, "
"OR `link-cli spend-request create --credential-type shared_payment_token --network-id "
"<profileId> ...`)"
)
Expand Down
4 changes: 2 additions & 2 deletions agentscore_commerce/discovery/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def agentscore_denial_schemas() -> dict[str, Any]:
"token_expired → deliver_verify_url_and_poll, invalid_credential → "
"switch_token_or_restart_session, wallet_signer_mismatch → resign_or_switch_to_operator_token, "
"wallet_auth_requires_wallet_signing → switch_to_operator_token, wallet_not_trusted → "
"contact_support UNFIXABLE compliance only (sanctions/age/jurisdiction_restricted); "
"contact_support, for UNFIXABLE compliance only (sanctions/age/jurisdiction_restricted); "
"fixable reasons re-route to identity_verification_required, payment_required → contact_merchant)."
),
},
Expand All @@ -255,7 +255,7 @@ def agentscore_denial_schemas() -> dict[str, Any]:
"description": (
"Present for missing_identity / identity_verification_required / token_expired "
"denials. Agent shares this with the user to complete KYC or claim a wallet. "
"Not present on wallet_not_trusted (UNFIXABLE compliance re-verification "
"Not present on wallet_not_trusted (UNFIXABLE compliance, so re-verification "
"won't change the outcome)."
),
},
Expand Down
14 changes: 7 additions & 7 deletions agentscore_commerce/discovery/skill_md.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class _SkillCtx:
"USDC (SPL). Use `agentscore-pay pay --chain solana`; MPP credential goes in `Authorization: Payment`."
),
"stripe": (
"Card via Link wallet. Use `@stripe/link-cli` `agentscore-pay` emits the "
"Card via Link wallet. Use `@stripe/link-cli`; `agentscore-pay` emits the "
"handoff hint when this rail is picked."
),
}
Expand All @@ -141,7 +141,7 @@ def _validate(input: _SkillCtx) -> None:
raise ValueError(f"build_skill_md: name must be 1-{_NAME_MAX} characters (got {len(n) if n else 0})")
if not _NAME_RE.match(n):
raise ValueError(
f'build_skill_md: name "{n}" is invalid must be lowercase alphanumeric and hyphens, '
f'build_skill_md: name "{n}" is invalid: must be lowercase alphanumeric and hyphens, '
"no leading/trailing/consecutive hyphens (agentskills.io spec)"
)
if not input.description:
Expand Down Expand Up @@ -232,7 +232,7 @@ def _payment_section(input: _SkillCtx) -> str:
clients[r] = defaults.get(r, [])
rows = ["| Rail | Notes | Compatible clients |", "|---|---|---|"]
for r in input.accepted_rails:
client_list = ", ".join(clients.get(r, [])) or ""
client_list = ", ".join(clients.get(r, [])) or "none"
rows.append(f"| **{_RAIL_LABELS[r]}** | {_RAIL_NOTES[r]} | {client_list} |")
intro = (
"Each gated route returns a 402 with `WWW-Authenticate` + `PAYMENT-REQUIRED` "
Expand Down Expand Up @@ -276,12 +276,12 @@ def _identity_section(input: _SkillCtx) -> str:
"\n\nThis merchant accepts AIP Agent Identity Tokens. If you hold an AIT from a trusted issuer "
"(AgentScore is always trusted), present the JWT in an `Agent-Identity` header plus an RFC 9421 "
"HTTP Message Signature (`Signature-Input` + `Signature` over `@method @authority @path agent-identity`, "
"tag `agent-identity`) signed with the token-bound cnf key — it satisfies identity in one round trip, "
"tag `agent-identity`) signed with the token-bound cnf key, satisfying identity in one round trip, "
"no separate AgentScore credential needed. `agentscore-pay pay --identity aip` does this automatically."
)
denial_note = (
"Denial bodies carry an `agent_instructions` block describing the recovery action "
"— read the `action` field and follow it. See the identity-bootstrap skill for the "
"Denial bodies carry an `agent_instructions` block describing the recovery action. "
"Read the `action` field and follow it. See the identity-bootstrap skill for the "
"canonical denial-code → action table."
)
return "\n".join(
Expand Down Expand Up @@ -375,7 +375,7 @@ def build_skill_md(
support_links: list[SkillMdLink] | None = None,
refresh_footer: bool = True,
) -> str:
"""Render an agentskills.io-compatible ``skill.md`` for an agent-commerce merchant.
"""Render an agentskills.io-compatible ``skill.md`` for an agentic-commerce merchant.

Output is YAML frontmatter (``name`` / ``description`` / optional ``license`` /
``compatibility`` / ``allowed-tools`` / ``metadata``) followed by markdown sections
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "agentscore-commerce"
version = "2.7.1"
version = "2.7.2"
description = "Agentic commerce SDK for Python: identity middleware (FastAPI, Flask, Django, AIOHTTP, Sanic, ASGI) + payment helpers + 402 builders + discovery + Stripe multichain. The full merchant-side toolkit for AgentScore-powered agentic commerce."
readme = "README.md"
license = "MIT"
Expand Down
8 changes: 6 additions & 2 deletions tests/test_skill_md.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,16 @@ def test_drops_overrides_for_rails_not_in_accepted(self) -> None:
assert "rogue-cli" not in out
assert "Stripe Shared Payment Token" not in out

def test_renders_em_dash_when_clients_empty(self) -> None:
def test_renders_none_when_clients_empty(self) -> None:
# "none" rather than an em-dash: this table is served on every store's
# skill.md, and the org rule bars em-dashes on external surfaces. Kept
# identical to the Node SDK, which renders the same table.
cfg = _base()
cfg["accepted_rails"] = ["x402_base"]
cfg["compatible_clients"] = {"x402_base": []}
out = build_skill_md(**cfg)
assert re.search("x402 on Base.+\\| —", out)
assert re.search(r"x402 on Base.+\| none", out)
assert "—" not in out


class TestIdentitySection:
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.