@agent-score/commerce 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,6 @@
1
+ import { R as RailKey, C as CompatibleClients } from '../agent_instructions-d3UWTdam.mjs';
2
+ export { c as compatibleClientsByRails } from '../agent_instructions-d3UWTdam.mjs';
3
+
1
4
  /**
2
5
  * Build a sample x402 accepts entry for a CAIP-2 network. Looks up the USDC asset
3
6
  * for the network from the `USDC` registry and uses a placeholder payTo. Used by
@@ -379,4 +382,120 @@ declare function wrapNoindexResponse(path: string, response: Response, options?:
379
382
  * in Next.js docs/examples. */
380
383
  declare const applyNoindexHeader: typeof wrapNoindexResponse;
381
384
 
382
- export { type BazaarDiscoveryConfig, type BuildAgentScoreOpenApiSnippetsInput, type BuildLlmsTxtInput, type DiscoveryProbeOptions, type DiscoveryProbeResponse, type LlmsTxtIdentitySectionInput, type LlmsTxtPaymentSectionInput, type NoindexNonDiscoveryOptions, type PaymentMethodConfig, type RequestLike, type WellKnownMppInput, agentscoreDenialSchemas, agentscoreOpenApiSnippets, agentscorePaymentRequiredSchema, agentscoreSecuritySchemes, applyNoindexHeader, buildDiscoveryProbeResponse, buildLlmsTxt, buildWellKnownMpp, createBazaarDiscovery, defaultDiscoveryPaths, isDiscoveryPath, isDiscoveryProbeRequest, llmsTxtIdentitySection, llmsTxtPaymentSection, noindexNonDiscoveryPaths, noindexNonDiscoveryPathsExpress, noindexNonDiscoveryPathsFastify, sampleX402AcceptForNetwork, wrapNoindexResponse };
385
+ interface SkillMdEndpoint {
386
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
387
+ path: string;
388
+ authRequired: boolean;
389
+ description: string;
390
+ }
391
+ interface SkillMdIdentityRequirements {
392
+ /** Whether KYC is required for gated routes. */
393
+ kycRequired?: boolean;
394
+ /** Minimum age (e.g. 21 for alcohol). */
395
+ minAge?: number;
396
+ /** Allowed-jurisdictions list (ISO 3166-1 alpha-2 country codes). */
397
+ allowedJurisdictions?: string[];
398
+ /** Whether sanctions screening is enforced. */
399
+ sanctionsClear?: boolean;
400
+ }
401
+ interface SkillMdShippingPolicy {
402
+ /** Allowed shipping countries (ISO 3166-1 alpha-2). */
403
+ allowedCountries?: string[];
404
+ /** Blocked US states (2-letter codes). */
405
+ blockedStates?: string[];
406
+ }
407
+ interface SkillMdLink {
408
+ label: string;
409
+ url: string;
410
+ }
411
+ interface BuildSkillMdInput {
412
+ /** Skill manifest identifier — kebab-case per agentskills.io spec: 1-64 chars, lowercase
413
+ * alphanumeric + hyphens, no leading/trailing/consecutive hyphens. Validated at build
414
+ * time; invalid names throw. e.g. 'martin-estate-wine-commerce'. */
415
+ name: string;
416
+ /** Skill description — agentskills.io spec: 1-1024 chars, non-empty. Should describe both
417
+ * what the skill does AND when to use it; imperative phrasing recommended ("Use when…").
418
+ * Validated at build time; over-length throws. */
419
+ description: string;
420
+ /** Merchant homepage (or domain root). Emitted as `metadata.homepage` per spec
421
+ * (top-level non-spec fields go under metadata). */
422
+ homepage: string;
423
+ /** Skill schema version — increment when the skill body materially changes. Emitted as
424
+ * a quoted string under `metadata.version` per agentskills.io spec (metadata values
425
+ * must be strings). Accepts string or number; numbers are converted. Default "1". */
426
+ version?: string | number;
427
+ /** Optional license name or path to a bundled license file. Emitted as top-level
428
+ * frontmatter `license:` per spec. */
429
+ license?: string;
430
+ /** Optional environment-requirements note (max 500 chars). e.g. "Requires Node 20+".
431
+ * Emitted as top-level frontmatter `compatibility:` per spec. */
432
+ compatibility?: string;
433
+ /** Optional space-separated string of pre-approved tools (experimental per spec). */
434
+ allowedTools?: string;
435
+ /** Additional caller-defined metadata entries — flat key/value strings nested under
436
+ * `metadata:`. Spec requires string values. */
437
+ metadata?: Record<string, string | number>;
438
+ /** Human display name (e.g. "Martin Estate Winery"). */
439
+ merchantName: string;
440
+ /** Optional one-line tagline appearing under the title. */
441
+ tagline?: string;
442
+ /** Optional short prose intro describing what the merchant offers. Renders below the title. */
443
+ intro?: string;
444
+ /** Files / well-known URLs surfaced under the "Important Files" table. The skill.md URL
445
+ * itself is added automatically — list other discovery surfaces (llms.txt, mpp.json,
446
+ * openapi.json, agent-card.json). */
447
+ files?: SkillMdLink[];
448
+ /** Rails the merchant accepts. Drives the Payment + Compatible Clients sections. Order
449
+ * is preserved in render. Default to the rails actually declared on the merchant's
450
+ * `respond402` config — keep these in sync. */
451
+ acceptedRails: RailKey[];
452
+ /** Override the per-rail compatible-clients matrix. When omitted, derives from
453
+ * `acceptedRails` via the SDK's smoke-verified default. Override keys not in
454
+ * `acceptedRails` are dropped (the rail isn't accepted, so the row isn't rendered). */
455
+ compatibleClients?: CompatibleClients;
456
+ /** Identity requirements as agent-observable outcomes (kyc / age / jurisdiction /
457
+ * sanctions). Internal posture (`failOpen`, mount strategy, KYC vendor) is intentionally
458
+ * not part of this shape — agents act on outcomes, not implementation. */
459
+ identity?: SkillMdIdentityRequirements;
460
+ /** URL to the identity-bootstrap skill. Linked from the Identity Prerequisite section
461
+ * so an agent without a Passport can follow the bootstrap before attempting purchase. */
462
+ identityBootstrapUrl?: string;
463
+ /** Shipping policy, for physical-goods merchants. Omit for digital merchants. */
464
+ shipping?: SkillMdShippingPolicy;
465
+ /** Agent-facing endpoints — path, method, whether auth is required, brief purpose. */
466
+ endpoints: SkillMdEndpoint[];
467
+ /** When this skill should fire (skill loader uses for trigger matching). */
468
+ triggers: string[];
469
+ /** Optional numbered onboarding steps. Each entry renders as a numbered list item;
470
+ * may include shell snippets in markdown code fences. */
471
+ onboardingSteps?: string[];
472
+ /** Support / homepage / docs links rendered in the "Support" section. */
473
+ supportLinks?: SkillMdLink[];
474
+ /** When true (default), append a footer noting clients can refresh skill.md to pick
475
+ * up new endpoints. Set to false to suppress. */
476
+ refreshFooter?: boolean;
477
+ }
478
+ /**
479
+ * Render an agentskills.io-compatible `skill.md` for an agent-commerce merchant.
480
+ *
481
+ * Output is YAML frontmatter (`name` / `description` / optional `license` /
482
+ * `compatibility` / `allowed-tools` / `metadata`) followed by markdown sections
483
+ * describing payment rails, identity requirements, endpoints, triggers, and support
484
+ * links — strictly the agent-facing contract, with no internal posture (no `failOpen`,
485
+ * no mount-strategy names, no KYC vendor, no defense parameters).
486
+ *
487
+ * Spec compliance:
488
+ * - `name` validated against the agentskills.io regex (lowercase alphanumeric + hyphens,
489
+ * no leading/trailing/consecutive hyphens, ≤64 chars).
490
+ * - `description` length capped at 1024.
491
+ * - `metadata` values always emitted as quoted strings.
492
+ * - `description` (and other user scalars) double-quoted to defuse the colon /
493
+ * newline / quote pitfall the spec explicitly warns about.
494
+ *
495
+ * The compatible-clients-per-rail table sources from the same SDK constant
496
+ * (`compatibleClientsByRails`) that drives the live 402 body's `compatible_clients`
497
+ * field, so updating a smoke-verified client in one place propagates to every surface.
498
+ */
499
+ declare function buildSkillMd(input: BuildSkillMdInput): string;
500
+
501
+ export { type BazaarDiscoveryConfig, type BuildAgentScoreOpenApiSnippetsInput, type BuildLlmsTxtInput, type BuildSkillMdInput, CompatibleClients, type DiscoveryProbeOptions, type DiscoveryProbeResponse, type LlmsTxtIdentitySectionInput, type LlmsTxtPaymentSectionInput, type NoindexNonDiscoveryOptions, type PaymentMethodConfig, RailKey, type RequestLike, type SkillMdEndpoint, type SkillMdIdentityRequirements, type SkillMdLink, type SkillMdShippingPolicy, type WellKnownMppInput, agentscoreDenialSchemas, agentscoreOpenApiSnippets, agentscorePaymentRequiredSchema, agentscoreSecuritySchemes, applyNoindexHeader, buildDiscoveryProbeResponse, buildLlmsTxt, buildSkillMd, buildWellKnownMpp, createBazaarDiscovery, defaultDiscoveryPaths, isDiscoveryPath, isDiscoveryProbeRequest, llmsTxtIdentitySection, llmsTxtPaymentSection, noindexNonDiscoveryPaths, noindexNonDiscoveryPathsExpress, noindexNonDiscoveryPathsFastify, sampleX402AcceptForNetwork, wrapNoindexResponse };
@@ -1,3 +1,6 @@
1
+ import { R as RailKey, C as CompatibleClients } from '../agent_instructions-d3UWTdam.js';
2
+ export { c as compatibleClientsByRails } from '../agent_instructions-d3UWTdam.js';
3
+
1
4
  /**
2
5
  * Build a sample x402 accepts entry for a CAIP-2 network. Looks up the USDC asset
3
6
  * for the network from the `USDC` registry and uses a placeholder payTo. Used by
@@ -379,4 +382,120 @@ declare function wrapNoindexResponse(path: string, response: Response, options?:
379
382
  * in Next.js docs/examples. */
380
383
  declare const applyNoindexHeader: typeof wrapNoindexResponse;
381
384
 
382
- export { type BazaarDiscoveryConfig, type BuildAgentScoreOpenApiSnippetsInput, type BuildLlmsTxtInput, type DiscoveryProbeOptions, type DiscoveryProbeResponse, type LlmsTxtIdentitySectionInput, type LlmsTxtPaymentSectionInput, type NoindexNonDiscoveryOptions, type PaymentMethodConfig, type RequestLike, type WellKnownMppInput, agentscoreDenialSchemas, agentscoreOpenApiSnippets, agentscorePaymentRequiredSchema, agentscoreSecuritySchemes, applyNoindexHeader, buildDiscoveryProbeResponse, buildLlmsTxt, buildWellKnownMpp, createBazaarDiscovery, defaultDiscoveryPaths, isDiscoveryPath, isDiscoveryProbeRequest, llmsTxtIdentitySection, llmsTxtPaymentSection, noindexNonDiscoveryPaths, noindexNonDiscoveryPathsExpress, noindexNonDiscoveryPathsFastify, sampleX402AcceptForNetwork, wrapNoindexResponse };
385
+ interface SkillMdEndpoint {
386
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
387
+ path: string;
388
+ authRequired: boolean;
389
+ description: string;
390
+ }
391
+ interface SkillMdIdentityRequirements {
392
+ /** Whether KYC is required for gated routes. */
393
+ kycRequired?: boolean;
394
+ /** Minimum age (e.g. 21 for alcohol). */
395
+ minAge?: number;
396
+ /** Allowed-jurisdictions list (ISO 3166-1 alpha-2 country codes). */
397
+ allowedJurisdictions?: string[];
398
+ /** Whether sanctions screening is enforced. */
399
+ sanctionsClear?: boolean;
400
+ }
401
+ interface SkillMdShippingPolicy {
402
+ /** Allowed shipping countries (ISO 3166-1 alpha-2). */
403
+ allowedCountries?: string[];
404
+ /** Blocked US states (2-letter codes). */
405
+ blockedStates?: string[];
406
+ }
407
+ interface SkillMdLink {
408
+ label: string;
409
+ url: string;
410
+ }
411
+ interface BuildSkillMdInput {
412
+ /** Skill manifest identifier — kebab-case per agentskills.io spec: 1-64 chars, lowercase
413
+ * alphanumeric + hyphens, no leading/trailing/consecutive hyphens. Validated at build
414
+ * time; invalid names throw. e.g. 'martin-estate-wine-commerce'. */
415
+ name: string;
416
+ /** Skill description — agentskills.io spec: 1-1024 chars, non-empty. Should describe both
417
+ * what the skill does AND when to use it; imperative phrasing recommended ("Use when…").
418
+ * Validated at build time; over-length throws. */
419
+ description: string;
420
+ /** Merchant homepage (or domain root). Emitted as `metadata.homepage` per spec
421
+ * (top-level non-spec fields go under metadata). */
422
+ homepage: string;
423
+ /** Skill schema version — increment when the skill body materially changes. Emitted as
424
+ * a quoted string under `metadata.version` per agentskills.io spec (metadata values
425
+ * must be strings). Accepts string or number; numbers are converted. Default "1". */
426
+ version?: string | number;
427
+ /** Optional license name or path to a bundled license file. Emitted as top-level
428
+ * frontmatter `license:` per spec. */
429
+ license?: string;
430
+ /** Optional environment-requirements note (max 500 chars). e.g. "Requires Node 20+".
431
+ * Emitted as top-level frontmatter `compatibility:` per spec. */
432
+ compatibility?: string;
433
+ /** Optional space-separated string of pre-approved tools (experimental per spec). */
434
+ allowedTools?: string;
435
+ /** Additional caller-defined metadata entries — flat key/value strings nested under
436
+ * `metadata:`. Spec requires string values. */
437
+ metadata?: Record<string, string | number>;
438
+ /** Human display name (e.g. "Martin Estate Winery"). */
439
+ merchantName: string;
440
+ /** Optional one-line tagline appearing under the title. */
441
+ tagline?: string;
442
+ /** Optional short prose intro describing what the merchant offers. Renders below the title. */
443
+ intro?: string;
444
+ /** Files / well-known URLs surfaced under the "Important Files" table. The skill.md URL
445
+ * itself is added automatically — list other discovery surfaces (llms.txt, mpp.json,
446
+ * openapi.json, agent-card.json). */
447
+ files?: SkillMdLink[];
448
+ /** Rails the merchant accepts. Drives the Payment + Compatible Clients sections. Order
449
+ * is preserved in render. Default to the rails actually declared on the merchant's
450
+ * `respond402` config — keep these in sync. */
451
+ acceptedRails: RailKey[];
452
+ /** Override the per-rail compatible-clients matrix. When omitted, derives from
453
+ * `acceptedRails` via the SDK's smoke-verified default. Override keys not in
454
+ * `acceptedRails` are dropped (the rail isn't accepted, so the row isn't rendered). */
455
+ compatibleClients?: CompatibleClients;
456
+ /** Identity requirements as agent-observable outcomes (kyc / age / jurisdiction /
457
+ * sanctions). Internal posture (`failOpen`, mount strategy, KYC vendor) is intentionally
458
+ * not part of this shape — agents act on outcomes, not implementation. */
459
+ identity?: SkillMdIdentityRequirements;
460
+ /** URL to the identity-bootstrap skill. Linked from the Identity Prerequisite section
461
+ * so an agent without a Passport can follow the bootstrap before attempting purchase. */
462
+ identityBootstrapUrl?: string;
463
+ /** Shipping policy, for physical-goods merchants. Omit for digital merchants. */
464
+ shipping?: SkillMdShippingPolicy;
465
+ /** Agent-facing endpoints — path, method, whether auth is required, brief purpose. */
466
+ endpoints: SkillMdEndpoint[];
467
+ /** When this skill should fire (skill loader uses for trigger matching). */
468
+ triggers: string[];
469
+ /** Optional numbered onboarding steps. Each entry renders as a numbered list item;
470
+ * may include shell snippets in markdown code fences. */
471
+ onboardingSteps?: string[];
472
+ /** Support / homepage / docs links rendered in the "Support" section. */
473
+ supportLinks?: SkillMdLink[];
474
+ /** When true (default), append a footer noting clients can refresh skill.md to pick
475
+ * up new endpoints. Set to false to suppress. */
476
+ refreshFooter?: boolean;
477
+ }
478
+ /**
479
+ * Render an agentskills.io-compatible `skill.md` for an agent-commerce merchant.
480
+ *
481
+ * Output is YAML frontmatter (`name` / `description` / optional `license` /
482
+ * `compatibility` / `allowed-tools` / `metadata`) followed by markdown sections
483
+ * describing payment rails, identity requirements, endpoints, triggers, and support
484
+ * links — strictly the agent-facing contract, with no internal posture (no `failOpen`,
485
+ * no mount-strategy names, no KYC vendor, no defense parameters).
486
+ *
487
+ * Spec compliance:
488
+ * - `name` validated against the agentskills.io regex (lowercase alphanumeric + hyphens,
489
+ * no leading/trailing/consecutive hyphens, ≤64 chars).
490
+ * - `description` length capped at 1024.
491
+ * - `metadata` values always emitted as quoted strings.
492
+ * - `description` (and other user scalars) double-quoted to defuse the colon /
493
+ * newline / quote pitfall the spec explicitly warns about.
494
+ *
495
+ * The compatible-clients-per-rail table sources from the same SDK constant
496
+ * (`compatibleClientsByRails`) that drives the live 402 body's `compatible_clients`
497
+ * field, so updating a smoke-verified client in one place propagates to every surface.
498
+ */
499
+ declare function buildSkillMd(input: BuildSkillMdInput): string;
500
+
501
+ export { type BazaarDiscoveryConfig, type BuildAgentScoreOpenApiSnippetsInput, type BuildLlmsTxtInput, type BuildSkillMdInput, CompatibleClients, type DiscoveryProbeOptions, type DiscoveryProbeResponse, type LlmsTxtIdentitySectionInput, type LlmsTxtPaymentSectionInput, type NoindexNonDiscoveryOptions, type PaymentMethodConfig, RailKey, type RequestLike, type SkillMdEndpoint, type SkillMdIdentityRequirements, type SkillMdLink, type SkillMdShippingPolicy, type WellKnownMppInput, agentscoreDenialSchemas, agentscoreOpenApiSnippets, agentscorePaymentRequiredSchema, agentscoreSecuritySchemes, applyNoindexHeader, buildDiscoveryProbeResponse, buildLlmsTxt, buildSkillMd, buildWellKnownMpp, createBazaarDiscovery, defaultDiscoveryPaths, isDiscoveryPath, isDiscoveryProbeRequest, llmsTxtIdentitySection, llmsTxtPaymentSection, noindexNonDiscoveryPaths, noindexNonDiscoveryPathsExpress, noindexNonDiscoveryPathsFastify, sampleX402AcceptForNetwork, wrapNoindexResponse };
@@ -27,7 +27,9 @@ __export(discovery_exports, {
27
27
  applyNoindexHeader: () => applyNoindexHeader,
28
28
  buildDiscoveryProbeResponse: () => buildDiscoveryProbeResponse,
29
29
  buildLlmsTxt: () => buildLlmsTxt,
30
+ buildSkillMd: () => buildSkillMd,
30
31
  buildWellKnownMpp: () => buildWellKnownMpp,
32
+ compatibleClientsByRails: () => compatibleClientsByRails,
31
33
  createBazaarDiscovery: () => createBazaarDiscovery,
32
34
  defaultDiscoveryPaths: () => defaultDiscoveryPaths,
33
35
  isDiscoveryPath: () => isDiscoveryPath,
@@ -584,6 +586,8 @@ function agentscoreOpenApiSnippets(opts = {}) {
584
586
  var defaultDiscoveryPaths = /* @__PURE__ */ new Set([
585
587
  "/openapi.json",
586
588
  "/llms.txt",
589
+ "/skill.md",
590
+ "/SKILL.md",
587
591
  "/.well-known/mpp.json",
588
592
  "/.well-known/agent-card.json",
589
593
  "/.well-known/ucp",
@@ -650,6 +654,204 @@ function wrapNoindexResponse(path, response, options) {
650
654
  });
651
655
  }
652
656
  var applyNoindexHeader = wrapNoindexResponse;
657
+
658
+ // src/challenge/agent_instructions.ts
659
+ var RAIL_CLIENTS = {
660
+ tempo_mpp: ["agentscore-pay", "tempo request", "x402-proxy"],
661
+ x402_base: ["agentscore-pay", "x402-proxy", "purl (omit --network flag)"],
662
+ x402_solana: ["agentscore-pay"],
663
+ stripe: ["link-cli"]
664
+ };
665
+ function compatibleClientsByRails(rails2) {
666
+ const out = {};
667
+ for (const r of rails2) out[r] = [...RAIL_CLIENTS[r]];
668
+ return Object.keys(out).length === 0 ? void 0 : out;
669
+ }
670
+
671
+ // src/discovery/skill_md.ts
672
+ var RAIL_LABELS = {
673
+ tempo_mpp: "MPP on Tempo",
674
+ x402_base: "x402 on Base",
675
+ x402_solana: "x402 on Solana",
676
+ stripe: "Stripe Shared Payment Token"
677
+ };
678
+ var RAIL_NOTES = {
679
+ tempo_mpp: "USDC. Use `agentscore-pay --chain tempo` (or `tempo request`); MPP credential goes in `Authorization: Payment`.",
680
+ x402_base: "USDC (EIP-3009). Use `agentscore-pay`; X-Payment header carries the signed credential.",
681
+ x402_solana: "USDC (SPL). Use `agentscore-pay`; X-Payment header carries the signed credential.",
682
+ stripe: "Card via Link wallet. Use `@stripe/link-cli` \u2014 `agentscore-pay` emits the handoff hint when this rail is picked."
683
+ };
684
+ var NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
685
+ var NAME_MAX = 64;
686
+ var DESCRIPTION_MAX = 1024;
687
+ var COMPATIBILITY_MAX = 500;
688
+ function validateInput(input) {
689
+ if (!input.name || input.name.length === 0 || input.name.length > NAME_MAX) {
690
+ throw new Error(`buildSkillMd: name must be 1-${NAME_MAX} characters (got ${input.name?.length ?? 0})`);
691
+ }
692
+ if (!NAME_RE.test(input.name)) {
693
+ throw new Error(
694
+ `buildSkillMd: name "${input.name}" is invalid \u2014 must be lowercase alphanumeric and hyphens, no leading/trailing/consecutive hyphens (agentskills.io spec)`
695
+ );
696
+ }
697
+ if (!input.description || input.description.length === 0) {
698
+ throw new Error("buildSkillMd: description is required and must be non-empty (agentskills.io spec)");
699
+ }
700
+ if (input.description.length > DESCRIPTION_MAX) {
701
+ throw new Error(
702
+ `buildSkillMd: description must be \u2264${DESCRIPTION_MAX} characters (got ${input.description.length})`
703
+ );
704
+ }
705
+ if (input.compatibility && input.compatibility.length > COMPATIBILITY_MAX) {
706
+ throw new Error(
707
+ `buildSkillMd: compatibility must be \u2264${COMPATIBILITY_MAX} characters (got ${input.compatibility.length})`
708
+ );
709
+ }
710
+ }
711
+ function quoteYaml(value) {
712
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
713
+ }
714
+ function tableCell(value) {
715
+ return value.replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
716
+ }
717
+ function frontmatter(input) {
718
+ const lines = ["---"];
719
+ lines.push(`name: ${input.name}`);
720
+ lines.push(`description: ${quoteYaml(input.description)}`);
721
+ if (input.license) lines.push(`license: ${quoteYaml(input.license)}`);
722
+ if (input.compatibility) lines.push(`compatibility: ${quoteYaml(input.compatibility)}`);
723
+ if (input.allowedTools) lines.push(`allowed-tools: ${quoteYaml(input.allowedTools)}`);
724
+ const meta = [];
725
+ meta.push(["version", String(input.version ?? "1")]);
726
+ meta.push(["homepage", input.homepage]);
727
+ for (const [k, v] of Object.entries(input.metadata ?? {})) {
728
+ if (k === "version" || k === "homepage") continue;
729
+ meta.push([k, String(v)]);
730
+ }
731
+ lines.push("metadata:");
732
+ for (const [k, v] of meta) {
733
+ lines.push(` ${k}: ${quoteYaml(v)}`);
734
+ }
735
+ lines.push("---");
736
+ return lines.join("\n");
737
+ }
738
+ function importantFiles(input) {
739
+ const skillUrl = `${input.homepage.replace(/\/$/, "")}/skill.md`;
740
+ const rows = [
741
+ "| File | URL |",
742
+ "|------|-----|",
743
+ `| **SKILL.md** (this file) | \`${skillUrl}\` |`
744
+ ];
745
+ for (const f of input.files ?? []) {
746
+ rows.push(`| ${tableCell(f.label)} | \`${tableCell(f.url)}\` |`);
747
+ }
748
+ return ["## Important Files", "", ...rows].join("\n");
749
+ }
750
+ function paymentSection(input) {
751
+ const override = input.compatibleClients;
752
+ const defaults = compatibleClientsByRails(input.acceptedRails) ?? {};
753
+ const clients = {};
754
+ for (const r of input.acceptedRails) {
755
+ clients[r] = override?.[r] ?? defaults[r] ?? [];
756
+ }
757
+ const rows = ["| Rail | Notes | Compatible clients |", "|---|---|---|"];
758
+ for (const r of input.acceptedRails) {
759
+ const list = (clients[r] ?? []).join(", ") || "\u2014";
760
+ rows.push(`| **${RAIL_LABELS[r]}** | ${RAIL_NOTES[r]} | ${list} |`);
761
+ }
762
+ return [
763
+ "## Payment",
764
+ "",
765
+ "Each gated route returns a 402 with `WWW-Authenticate` + `PAYMENT-REQUIRED` body listing the rails below with current pricing. Pick whichever your wallet is funded for.",
766
+ "",
767
+ ...rows
768
+ ].join("\n");
769
+ }
770
+ function identitySection(input) {
771
+ const id = input.identity;
772
+ if (!id) return "";
773
+ const reqs = [];
774
+ if (id.kycRequired) reqs.push("KYC verified Passport");
775
+ if (id.minAge) reqs.push(`age ${id.minAge}+`);
776
+ if (id.allowedJurisdictions?.length) reqs.push(`${id.allowedJurisdictions.join("/")} only`);
777
+ if (id.sanctionsClear) reqs.push("sanctions clear");
778
+ if (reqs.length === 0) return "";
779
+ const bootstrap = input.identityBootstrapUrl ? `
780
+
781
+ If you don't have a Passport, fetch \`${input.identityBootstrapUrl}\` and follow the onboarding there first. Bring back the \`opc_...\` operator token in \`X-Operator-Token\` on every gated request.` : "";
782
+ return [
783
+ "## Identity Prerequisite",
784
+ "",
785
+ `This merchant uses AgentScore identity. Required: ${reqs.join(", ")}.${bootstrap}`,
786
+ "",
787
+ "Denial bodies carry an `agent_instructions` block describing the recovery action \u2014 read the `action` field and follow it. See the identity-bootstrap skill for the canonical denial-code \u2192 action table."
788
+ ].join("\n");
789
+ }
790
+ function shippingSection(input) {
791
+ const s = input.shipping;
792
+ if (!s || !s.allowedCountries?.length && !s.blockedStates?.length) return "";
793
+ const lines = ["## Shipping", ""];
794
+ if (s.allowedCountries?.length) {
795
+ lines.push(`Ships to: ${s.allowedCountries.join(", ")}.`);
796
+ }
797
+ if (s.blockedStates?.length) {
798
+ if (lines.length > 2) lines.push("");
799
+ lines.push(`Blocked US states: ${s.blockedStates.join(", ")}.`);
800
+ }
801
+ return lines.join("\n");
802
+ }
803
+ function endpointsSection(input) {
804
+ if (input.endpoints.length === 0) return "";
805
+ const rows = ["| Method | Path | Auth | Purpose |", "|---|---|---|---|"];
806
+ for (const e of input.endpoints) {
807
+ rows.push(
808
+ `| ${e.method} | \`${tableCell(e.path)}\` | ${e.authRequired ? "identity required" : "anonymous"} | ${tableCell(e.description)} |`
809
+ );
810
+ }
811
+ return ["## Endpoints", "", ...rows].join("\n");
812
+ }
813
+ function onboardingSection(input) {
814
+ if (!input.onboardingSteps?.length) return "";
815
+ const rows = input.onboardingSteps.map((step, i) => `${i + 1}. ${step}`);
816
+ return ["## Onboarding Flow", "", ...rows].join("\n");
817
+ }
818
+ function triggersSection(input) {
819
+ if (input.triggers.length === 0) return "";
820
+ const rows = input.triggers.map((t) => `- ${t}`);
821
+ return ["## Triggers", "", "Use this skill when the user wants to:", "", ...rows].join("\n");
822
+ }
823
+ function supportSection(input) {
824
+ if (!input.supportLinks?.length) return "";
825
+ const rows = input.supportLinks.map((l) => `- **${l.label}**: ${l.url}`);
826
+ return ["## Support", "", ...rows].join("\n");
827
+ }
828
+ function refreshFooter(input) {
829
+ if (input.refreshFooter === false) return "";
830
+ return "_Re-fetch this file periodically to pick up new endpoints, rails, or policies._";
831
+ }
832
+ function titleBlock(input) {
833
+ const parts = [`# ${input.merchantName}`];
834
+ if (input.tagline) parts.push(`_${input.tagline}_`);
835
+ if (input.intro) parts.push(input.intro);
836
+ return parts.join("\n\n");
837
+ }
838
+ function buildSkillMd(input) {
839
+ validateInput(input);
840
+ const sections = [
841
+ frontmatter(input),
842
+ titleBlock(input),
843
+ importantFiles(input),
844
+ identitySection(input),
845
+ paymentSection(input),
846
+ shippingSection(input),
847
+ onboardingSection(input),
848
+ endpointsSection(input),
849
+ triggersSection(input),
850
+ supportSection(input),
851
+ refreshFooter(input)
852
+ ].filter((s) => s !== "");
853
+ return sections.join("\n\n").replace(/\n{3,}/g, "\n\n").trim() + "\n";
854
+ }
653
855
  // Annotate the CommonJS export names for ESM import in node:
654
856
  0 && (module.exports = {
655
857
  agentscoreDenialSchemas,
@@ -659,7 +861,9 @@ var applyNoindexHeader = wrapNoindexResponse;
659
861
  applyNoindexHeader,
660
862
  buildDiscoveryProbeResponse,
661
863
  buildLlmsTxt,
864
+ buildSkillMd,
662
865
  buildWellKnownMpp,
866
+ compatibleClientsByRails,
663
867
  createBazaarDiscovery,
664
868
  defaultDiscoveryPaths,
665
869
  isDiscoveryPath,