From 453c63681219b2d3dafefa05cac540d60cccd101 Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Sat, 22 Aug 2026 10:39:51 -0700 Subject: [PATCH] Remove em-dashes from served discovery copy; 2.9.3 Every string these builders render onto a merchant's llms.txt, skill.md, OpenAPI and 402 body carried em-dashes, which the house rule bars on external surfaces. Because the identity and payment blocks are shared, one fix here clears the residue on every store at once: a store rendering against this build now emits zero. The empty compatible-clients table cell rendered a bare em-dash as a placeholder; it now reads "none", which is clearer anyway. Kept identical to the Python SDK, which renders the same table. Also corrects "agent-commerce" to "agentic-commerce" in three doc comments: that is the house category term. Comments and JSDoc are untouched. They are internal prose, they carry the load-bearing rationale, and rewriting them would be churn across nearly every file for no reader benefit. Co-Authored-By: Claude Opus 5 (1M context) --- package.json | 2 +- src/challenge/agent_instructions.ts | 2 +- src/challenge/how_to_pay.ts | 2 +- src/checkout.ts | 4 ++-- src/discovery/agentscore_content.ts | 6 +++--- src/discovery/bazaar.ts | 2 +- src/discovery/llms_txt.ts | 18 +++++++++--------- src/discovery/openapi.ts | 4 ++-- src/discovery/skill_md.ts | 12 ++++++------ tests/discovery/skill_md.test.ts | 7 +++++-- 10 files changed, 31 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index d7c5d5e..3dc79db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@agent-score/commerce", - "version": "2.9.2", + "version": "2.9.3", "description": "Agentic commerce SDK: identity middleware (Hono, Express, Fastify, Next.js, Web Fetch) + payment helpers + 402 builders + discovery + Stripe multichain. The full merchant-side toolkit for AgentScore-powered agentic commerce.", "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/src/challenge/agent_instructions.ts b/src/challenge/agent_instructions.ts index 35ec388..6e41518 100644 --- a/src/challenge/agent_instructions.ts +++ b/src/challenge/agent_instructions.ts @@ -25,7 +25,7 @@ const X402_WARNING = 'Do NOT send USDC manually to the deposit addresses. Use `agentscore-pay pay` so the credential is signed and submitted; otherwise the request will not complete even though the deposit lands.'; const TEMPO_TOOL = '`tempo request` for Tempo USDC'; -const AGENTSCORE_PAY_TOOL = '`agentscore-pay` — Base + Solana + Tempo from one CLI'; +const AGENTSCORE_PAY_TOOL = '`agentscore-pay`, one CLI for Base, Solana and Tempo'; const DEFAULT_WALLET_COMPATIBILITY = 'Any client that can produce a valid MPP credential (Authorization: Payment) or x402 X-Payment header. Use the CLI commands above; sign-it-yourself is also fine.'; diff --git a/src/challenge/how_to_pay.ts b/src/challenge/how_to_pay.ts index cb1aaf4..82e39c8 100644 --- a/src/challenge/how_to_pay.ts +++ b/src/challenge/how_to_pay.ts @@ -170,7 +170,7 @@ export function buildHowToPay({ stripe.what_it_does_link_cli = 'Mints a one-time-use SharedPaymentToken scoped to this purchase (user approves in Link wallet), then submits it as the payment credential.'; } else if (linkCliBlocked) { - stripe.note = `link-cli SPT path not available for this purchase — Stripe link-cli caps spend requests at $500.00 ($50000 cents); your total is $${totalNum}. Use your own Stripe account with the SharedPaymentToken API instead.`; + stripe.note = `link-cli SPT path not available for this purchase: Stripe link-cli caps spend requests at $500.00 ($50000 cents); your total is $${totalNum}. Use your own Stripe account with the SharedPaymentToken API instead.`; } block.stripe = stripe; } diff --git a/src/checkout.ts b/src/checkout.ts index 0948d5c..db30ced 100644 --- a/src/checkout.ts +++ b/src/checkout.ts @@ -1,7 +1,7 @@ /** * 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: * @@ -832,7 +832,7 @@ function resolveIssuerPolicy( } /** - * High-level agent-commerce orchestrator. + * High-level agentic-commerce orchestrator. * * @example * ```ts diff --git a/src/discovery/agentscore_content.ts b/src/discovery/agentscore_content.ts index 65a3402..ba5b889 100644 --- a/src/discovery/agentscore_content.ts +++ b/src/discovery/agentscore_content.ts @@ -103,17 +103,17 @@ export function buildAgentscoreOnboardingSteps(opts: { 'Install agentscore-pay if you don\'t already have a compatible client for your funded chain: ' + '`npm i -g @agent-score/pay` (or `brew install agentscore/tap/agentscore-pay`). ' + `${merchantName} accepts: ${railsHuman}. agentscore-pay speaks every supported rail; ` + - (compatibleFragment ? `the rails table also lists per-rail \`compatible_clients\` — ${compatibleFragment}. ` : '') + + (compatibleFragment ? `the rails table also lists per-rail \`compatible_clients\`: ${compatibleFragment}. ` : '') + 'Any spec-compliant client for an individual rail works too.'; const bootstrapStep = '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` ' + `(one-time KYC${requiresKyc ? '; required for this merchant' : ''}; the human completes a verify URL once and pay caches the operator_token), ` + 'and `agentscore-pay balance` to see which chain has USDC. Skip if your wallet+Passport are already provisioned.'; const stripeFallbackStep = '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.'; const returningUserStep = 'Returning user note: if you\'ve paid an AgentScore-gated merchant before from this wallet, ' + diff --git a/src/discovery/bazaar.ts b/src/discovery/bazaar.ts index 00f942b..a2d8dbb 100644 --- a/src/discovery/bazaar.ts +++ b/src/discovery/bazaar.ts @@ -56,7 +56,7 @@ export async function createBazaarDiscovery(config: BazaarDiscoveryConfig): Prom /* v8 ignore start -- peer-dep-absence guard; @x402/extensions is installed in test env */ if (!bazaar?.declareDiscoveryExtension) { throw new Error( - '@x402/extensions not installed — `npm install @x402/extensions` for createBazaarDiscovery.', + '@x402/extensions not installed. Run `npm install @x402/extensions` for createBazaarDiscovery.', ); } /* v8 ignore stop */ diff --git a/src/discovery/llms_txt.ts b/src/discovery/llms_txt.ts index 4502662..9e19547 100644 --- a/src/discovery/llms_txt.ts +++ b/src/discovery/llms_txt.ts @@ -26,7 +26,7 @@ export function llmsTxtIdentitySection({ return ''; } const aipBullet = aip - ? '\n- **`Agent-Identity: ` + RFC 9421 signature** — present an Agent Identity Token (AIP) ' + + ? '\n- **`Agent-Identity: ` + 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 ' + @@ -46,11 +46,11 @@ export function llmsTxtIdentitySection({ : ''; return `## Identity -AgentScore identity is reusable across every AgentScore-gated merchant — one KYC, no re-verification per site. Pick a header: +AgentScore identity is reusable across every AgentScore-gated merchant: one KYC, no re-verification per site. Pick a header: -- **\`X-Wallet-Address: 0x...\` or base58** — works on signing rails (Tempo, x402, Solana MPP). The wallet you claim must sign the payment. -- **\`X-Operator-Token: opc_...\`** — works on every rail, including Stripe SPT. Reusable across AgentScore merchants until expiry.${aipBullet} -- **Neither** — you get a 403 with \`verify_url\`. Complete the session flow once and reuse the resulting \`opc_...\` everywhere.${complianceNote}`; +- **\`X-Wallet-Address: 0x...\` or base58**: works on signing rails (Tempo, x402, Solana MPP). The wallet you claim must sign the payment. +- **\`X-Operator-Token: opc_...\`**: works on every rail, including Stripe SPT. Reusable across AgentScore merchants until expiry.${aipBullet} +- **Neither**: you get a 403 with \`verify_url\`. Complete the session flow once and reuse the resulting \`opc_...\` everywhere.${complianceNote}`; } interface LlmsTxtPaymentSectionConfig { @@ -94,16 +94,16 @@ function llmsTxtPaymentSectionCompact(input: LlmsTxtPaymentSectionConfig): strin const lines: string[] = ['## Payment', '']; const rails = input.rails; if (hasRailFamily(rails, 'tempo-')) { - lines.push('- **Tempo USDC via MPP** — `tempo request -X POST -H "X-Operator-Token: opc_..." --json \'{...}\' --max-spend N ' + input.appUrl + '`'); + lines.push('- **Tempo USDC via MPP**: `tempo request -X POST -H "X-Operator-Token: opc_..." --json \'{...}\' --max-spend N ' + input.appUrl + '`'); } if (hasRailFamily(rails, 'x402-base-')) { - lines.push('- **x402 USDC on Base** (EIP-3009) — `agentscore-pay pay POST ' + input.appUrl + ' --chain base -H "X-Operator-Token: opc_..." -d \'{...}\'`'); + lines.push('- **x402 USDC on Base** (EIP-3009): `agentscore-pay pay POST ' + input.appUrl + ' --chain base -H "X-Operator-Token: opc_..." -d \'{...}\'`'); } if (hasRailFamily(rails, 'mpp-solana-')) { - lines.push('- **USDC on Solana** — `agentscore-pay pay POST ' + input.appUrl + ' --chain solana -H "X-Operator-Token: opc_..." -d \'{...}\'`'); + lines.push('- **USDC on Solana**: `agentscore-pay pay POST ' + input.appUrl + ' --chain solana -H "X-Operator-Token: opc_..." -d \'{...}\'`'); } if (rails.includes('stripe-spt')) { - lines.push('- **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 ...`)'); + lines.push('- **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 ...`)'); } lines.push(''); lines.push('IMPORTANT: Do NOT use raw on-chain transfers. Use the CLI commands above so the payment credential is signed and submitted via the protocol handshake.'); diff --git a/src/discovery/openapi.ts b/src/discovery/openapi.ts index f7d0763..f270311 100644 --- a/src/discovery/openapi.ts +++ b/src/discovery/openapi.ts @@ -90,7 +90,7 @@ export function agentscoreDenialSchemas(): Record { 'payment_required', ], description: - "Denial code emitted by AgentScore's gate middleware in 403 responses. Every code carries a structured agent_instructions block describing recovery actions (per-code action: missing_identity → probe_identity_then_session, identity_verification_required / 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); fixable reasons re-route to identity_verification_required, payment_required → contact_merchant).", + "Denial code emitted by AgentScore's gate middleware in 403 responses. Every code carries a structured agent_instructions block describing recovery actions (per-code action: missing_identity → probe_identity_then_session, identity_verification_required / 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, for UNFIXABLE compliance only (sanctions/age/jurisdiction_restricted); fixable reasons re-route to identity_verification_required, payment_required → contact_merchant).", }, AgentScoreDenialBody: { type: 'object', @@ -101,7 +101,7 @@ export function agentscoreDenialSchemas(): Record { description: 'JSON-encoded { action, steps, user_message } block. Always present on every denial; agents parse this to learn how to recover (e.g., poll verify_url, switch headers, re-sign).', }, - verify_url: { type: 'string', format: 'uri', 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 won't change the outcome)." }, + verify_url: { type: 'string', format: 'uri', 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, so re-verification will not change the outcome).' }, session_id: { type: 'string' }, poll_url: { type: 'string', format: 'uri' }, poll_secret: { type: 'string' }, diff --git a/src/discovery/skill_md.ts b/src/discovery/skill_md.ts index 4f7e1c3..97f0371 100644 --- a/src/discovery/skill_md.ts +++ b/src/discovery/skill_md.ts @@ -130,7 +130,7 @@ const RAIL_NOTES: Record = { tempo_mpp: 'USDC. Use `agentscore-pay pay --chain tempo` (or `tempo request`); MPP credential goes in `Authorization: Payment`.', x402_base: 'USDC (EIP-3009). Use `agentscore-pay pay --chain base`; X-Payment header carries the signed credential.', solana_mpp: '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 handoff hint when this rail is picked.', + stripe: 'Card via Link wallet. Use `@stripe/link-cli`; `agentscore-pay` emits the handoff hint when this rail is picked.', }; const NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/; @@ -144,7 +144,7 @@ function validateInput(input: BuildSkillMdInput): void { } if (!NAME_RE.test(input.name)) { throw new Error( - `buildSkillMd: name "${input.name}" is invalid — must be lowercase alphanumeric and hyphens, no leading/trailing/consecutive hyphens (agentskills.io spec)`, + `buildSkillMd: name "${input.name}" is invalid: must be lowercase alphanumeric and hyphens, no leading/trailing/consecutive hyphens (agentskills.io spec)`, ); } if (!input.description || input.description.length === 0) { @@ -222,7 +222,7 @@ function paymentSection(input: BuildSkillMdInput): string { } const rows: string[] = ['| Rail | Notes | Compatible clients |', '|---|---|---|']; for (const r of input.acceptedRails) { - const list = (clients[r] ?? []).join(', ') || '—'; + const list = (clients[r] ?? []).join(', ') || 'none'; rows.push(`| **${RAIL_LABELS[r]}** | ${RAIL_NOTES[r]} | ${list} |`); } return [ @@ -253,7 +253,7 @@ function identitySection(input: BuildSkillMdInput): string { ? '\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.' : ''; return [ @@ -261,7 +261,7 @@ function identitySection(input: BuildSkillMdInput): string { '', `${reqLine}${aipNote}`, '', - '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.', + '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.', ].join('\n'); } @@ -321,7 +321,7 @@ function titleBlock(input: BuildSkillMdInput): string { } /** - * 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 diff --git a/tests/discovery/skill_md.test.ts b/tests/discovery/skill_md.test.ts index f178f1a..198ef75 100644 --- a/tests/discovery/skill_md.test.ts +++ b/tests/discovery/skill_md.test.ts @@ -251,13 +251,16 @@ describe('buildSkillMd', () => { expect(out).not.toContain('Stripe Shared Payment Token'); }); - it("renders '—' when a rail's compatible-clients list is explicitly empty", () => { + it("renders 'none' when a rail's compatible-clients list is explicitly empty", () => { const out = buildSkillMd({ ...baseInput, acceptedRails: ['x402_base'], compatibleClients: { x402_base: [] }, }); - expect(out).toMatch(/x402 on Base.+\| —/); + // '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. + expect(out).toMatch(/x402 on Base.+\| none/); + expect(out).not.toContain('—'); }); });