@agentcash/discovery 0.1.2 → 0.1.4

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.
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ var STRICT_ESCALATION_CODES = [
12
12
  ];
13
13
 
14
14
  // src/mmm-enabled.ts
15
- var isMmmEnabled = () => "0.1.2".includes("-mmm");
15
+ var isMmmEnabled = () => "0.1.4".includes("-mmm");
16
16
 
17
17
  // src/core/constants.ts
18
18
  var OPENAPI_PATH_CANDIDATES = ["/openapi.json", "/.well-known/openapi.json"];
@@ -182,7 +182,7 @@ function parseWellKnownPayload(payload, origin, sourceUrl) {
182
182
  "Using /.well-known/x402.instructions as compatibility guidance fallback. Prefer llms.txt.",
183
183
  {
184
184
  stage: "well-known/x402",
185
- hint: "Move guidance to llms.txt and reference via x-agentcash-guidance.llmsTxtUrl."
185
+ hint: "Move guidance to llms.txt and reference via x-discovery.llmsTxtUrl in OpenAPI."
186
186
  }
187
187
  )
188
188
  );
@@ -195,7 +195,7 @@ function parseWellKnownPayload(payload, origin, sourceUrl) {
195
195
  "Using /.well-known/x402.ownershipProofs compatibility field. Prefer OpenAPI provenance extension.",
196
196
  {
197
197
  stage: "well-known/x402",
198
- hint: "Move ownership proofs to x-agentcash-provenance.ownershipProofs in OpenAPI."
198
+ hint: "Move ownership proofs to x-discovery.ownershipProofs in OpenAPI."
199
199
  }
200
200
  )
201
201
  );
@@ -591,10 +591,31 @@ function estimateTokenCount(text) {
591
591
  return Math.ceil(text.length / 4);
592
592
  }
593
593
 
594
- // src/core/openapi.ts
594
+ // src/core/openapi-utils.ts
595
595
  function isRecord3(value) {
596
596
  return value !== null && typeof value === "object" && !Array.isArray(value);
597
597
  }
598
+ function hasSecurity(operation, scheme) {
599
+ const security = operation.security;
600
+ return Array.isArray(security) && security.some((s) => isRecord3(s) && scheme in s);
601
+ }
602
+ function has402Response(operation) {
603
+ const responses = operation.responses;
604
+ if (!isRecord3(responses)) return false;
605
+ return Boolean(responses["402"]);
606
+ }
607
+ function inferAuthMode(operation) {
608
+ if (isRecord3(operation["x-payment-info"])) return "paid";
609
+ if (has402Response(operation)) {
610
+ if (hasSecurity(operation, "siwx")) return "siwx";
611
+ return "paid";
612
+ }
613
+ if (hasSecurity(operation, "apiKey")) return "apiKey";
614
+ if (hasSecurity(operation, "siwx")) return "siwx";
615
+ return void 0;
616
+ }
617
+
618
+ // src/core/openapi.ts
598
619
  function asString(value) {
599
620
  return typeof value === "string" && value.length > 0 ? value : void 0;
600
621
  }
@@ -603,11 +624,6 @@ function parsePriceValue(value) {
603
624
  if (typeof value === "number" && Number.isFinite(value)) return String(value);
604
625
  return void 0;
605
626
  }
606
- function require402Response(operation) {
607
- const responses = operation.responses;
608
- if (!isRecord3(responses)) return false;
609
- return Boolean(responses["402"]);
610
- }
611
627
  function parseProtocols(paymentInfo) {
612
628
  const protocols = paymentInfo.protocols;
613
629
  if (!Array.isArray(protocols)) return [];
@@ -615,15 +631,6 @@ function parseProtocols(paymentInfo) {
615
631
  (entry) => typeof entry === "string" && entry.length > 0
616
632
  );
617
633
  }
618
- function parseAuthMode(operation) {
619
- const auth = operation["x-agentcash-auth"];
620
- if (!isRecord3(auth)) return void 0;
621
- const mode = auth.mode;
622
- if (mode === "paid" || mode === "siwx" || mode === "apiKey" || mode === "unprotected") {
623
- return mode;
624
- }
625
- return void 0;
626
- }
627
634
  async function runOpenApiStage(options) {
628
635
  const warnings = [];
629
636
  const resources = [];
@@ -695,10 +702,9 @@ async function runOpenApiStage(options) {
695
702
  };
696
703
  }
697
704
  const paths = document.paths;
698
- const provenance = isRecord3(document["x-agentcash-provenance"]) ? document["x-agentcash-provenance"] : void 0;
699
- const ownershipProofs = Array.isArray(provenance?.ownershipProofs) ? provenance.ownershipProofs.filter((entry) => typeof entry === "string") : [];
700
- const guidance = isRecord3(document["x-agentcash-guidance"]) ? document["x-agentcash-guidance"] : void 0;
701
- const llmsTxtUrl = asString(guidance?.llmsTxtUrl);
705
+ const discovery = isRecord3(document["x-discovery"]) ? document["x-discovery"] : void 0;
706
+ const ownershipProofs = Array.isArray(discovery?.ownershipProofs) ? discovery.ownershipProofs.filter((entry) => typeof entry === "string") : [];
707
+ const llmsTxtUrl = asString(discovery?.llmsTxtUrl);
702
708
  if (ownershipProofs.length > 0) {
703
709
  warnings.push(
704
710
  warning("OPENAPI_OWNERSHIP_PROOFS_PRESENT", "info", "OpenAPI ownership proofs detected", {
@@ -743,13 +749,13 @@ async function runOpenApiStage(options) {
743
749
  )
744
750
  );
745
751
  }
746
- const authMode = parseAuthMode(operation);
752
+ const authMode = inferAuthMode(operation);
747
753
  if (!authMode) {
748
754
  warnings.push(
749
755
  warning(
750
756
  "OPENAPI_AUTH_MODE_MISSING",
751
757
  "error",
752
- `${method} ${rawPath} missing x-agentcash-auth.mode`,
758
+ `${method} ${rawPath} missing auth mode (no x-payment-info, 402 response, or security scheme)`,
753
759
  {
754
760
  stage: "openapi"
755
761
  }
@@ -757,7 +763,7 @@ async function runOpenApiStage(options) {
757
763
  );
758
764
  continue;
759
765
  }
760
- if ((authMode === "paid" || authMode === "siwx") && !require402Response(operation)) {
766
+ if ((authMode === "paid" || authMode === "siwx") && !has402Response(operation)) {
761
767
  warnings.push(
762
768
  warning(
763
769
  "OPENAPI_402_MISSING",
@@ -1336,11 +1342,6 @@ async function runDiscovery({
1336
1342
  }
1337
1343
  }
1338
1344
  }
1339
- if (!stageResult.valid) {
1340
- continue;
1341
- }
1342
- const mergeWarnings = mergeResources(merged, stageResult.resources, resourceWarnings);
1343
- warnings.push(...mergeWarnings);
1344
1345
  if (includeRaw && stageResult.raw !== void 0) {
1345
1346
  if (stageResult.stage === "openapi" || stageResult.stage === "override") {
1346
1347
  const rawObject = stageResult.raw;
@@ -1360,6 +1361,11 @@ async function runDiscovery({
1360
1361
  rawSources.interopMpp = stageResult.raw;
1361
1362
  }
1362
1363
  }
1364
+ if (!stageResult.valid) {
1365
+ continue;
1366
+ }
1367
+ const mergeWarnings = mergeResources(merged, stageResult.resources, resourceWarnings);
1368
+ warnings.push(...mergeWarnings);
1363
1369
  if (!detailed) {
1364
1370
  selectedStage = selectedStage ?? stageResult.stage;
1365
1371
  break;
@@ -1400,6 +1406,471 @@ async function runDiscovery({
1400
1406
  };
1401
1407
  }
1402
1408
 
1409
+ // src/validation/codes.ts
1410
+ var VALIDATION_CODES = {
1411
+ COINBASE_SCHEMA_INVALID: "COINBASE_SCHEMA_INVALID",
1412
+ X402_NOT_OBJECT: "X402_NOT_OBJECT",
1413
+ X402_VERSION_MISSING: "X402_VERSION_MISSING",
1414
+ X402_VERSION_UNSUPPORTED: "X402_VERSION_UNSUPPORTED",
1415
+ X402_ACCEPTS_MISSING: "X402_ACCEPTS_MISSING",
1416
+ X402_ACCEPTS_INVALID: "X402_ACCEPTS_INVALID",
1417
+ X402_ACCEPTS_EMPTY: "X402_ACCEPTS_EMPTY",
1418
+ X402_ACCEPT_ENTRY_INVALID: "X402_ACCEPT_ENTRY_INVALID",
1419
+ NETWORK_CAIP2_INVALID: "NETWORK_CAIP2_INVALID",
1420
+ NETWORK_EIP155_REFERENCE_INVALID: "NETWORK_EIP155_REFERENCE_INVALID",
1421
+ NETWORK_SOLANA_ALIAS_INVALID: "NETWORK_SOLANA_ALIAS_INVALID",
1422
+ NETWORK_SOLANA_ALIAS_COMPAT: "NETWORK_SOLANA_ALIAS_COMPAT",
1423
+ NETWORK_REFERENCE_UNKNOWN: "NETWORK_REFERENCE_UNKNOWN",
1424
+ SCHEMA_INPUT_MISSING: "SCHEMA_INPUT_MISSING",
1425
+ SCHEMA_OUTPUT_MISSING: "SCHEMA_OUTPUT_MISSING",
1426
+ METADATA_TITLE_MISSING: "METADATA_TITLE_MISSING",
1427
+ METADATA_DESCRIPTION_MISSING: "METADATA_DESCRIPTION_MISSING",
1428
+ METADATA_FAVICON_MISSING: "METADATA_FAVICON_MISSING",
1429
+ METADATA_OG_IMAGE_MISSING: "METADATA_OG_IMAGE_MISSING"
1430
+ };
1431
+
1432
+ // src/validation/metadata.ts
1433
+ function hasText(value) {
1434
+ return typeof value === "string" && value.trim().length > 0;
1435
+ }
1436
+ function hasOgImage(metadata) {
1437
+ const images = metadata.ogImages;
1438
+ if (!Array.isArray(images) || images.length === 0) return false;
1439
+ return images.some((entry) => {
1440
+ if (typeof entry === "string") {
1441
+ return entry.trim().length > 0;
1442
+ }
1443
+ if (!entry || typeof entry !== "object") return false;
1444
+ return hasText(entry.url);
1445
+ });
1446
+ }
1447
+ function evaluateMetadataCompleteness(metadata) {
1448
+ const issues = [];
1449
+ if (!hasText(metadata.title)) {
1450
+ issues.push({
1451
+ code: VALIDATION_CODES.METADATA_TITLE_MISSING,
1452
+ severity: "warn",
1453
+ message: "Metadata title is missing",
1454
+ hint: "Provide a page title to improve discovery quality and UX.",
1455
+ path: "metadata.title",
1456
+ stage: "metadata"
1457
+ });
1458
+ }
1459
+ if (!hasText(metadata.description)) {
1460
+ issues.push({
1461
+ code: VALIDATION_CODES.METADATA_DESCRIPTION_MISSING,
1462
+ severity: "warn",
1463
+ message: "Metadata description is missing",
1464
+ hint: "Provide a concise description for integrators and search previews.",
1465
+ path: "metadata.description",
1466
+ stage: "metadata"
1467
+ });
1468
+ }
1469
+ if (!hasText(metadata.favicon)) {
1470
+ issues.push({
1471
+ code: VALIDATION_CODES.METADATA_FAVICON_MISSING,
1472
+ severity: "warn",
1473
+ message: "Metadata favicon is missing",
1474
+ hint: "Expose a favicon URL for stronger brand attribution.",
1475
+ path: "metadata.favicon",
1476
+ stage: "metadata"
1477
+ });
1478
+ }
1479
+ if (!hasOgImage(metadata)) {
1480
+ issues.push({
1481
+ code: VALIDATION_CODES.METADATA_OG_IMAGE_MISSING,
1482
+ severity: "warn",
1483
+ message: "Metadata OG image is missing",
1484
+ hint: "Provide an Open Graph image to improve sharing previews.",
1485
+ path: "metadata.ogImages",
1486
+ stage: "metadata"
1487
+ });
1488
+ }
1489
+ return issues;
1490
+ }
1491
+
1492
+ // src/validation/payment-required.ts
1493
+ import { parsePaymentRequired } from "@x402/core/schemas";
1494
+ var SOLANA_CANONICAL_BY_REF = {
1495
+ "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": "solana",
1496
+ EtWTRABZaYq6iMfeYKouRu166VU2xqa1: "solana-devnet",
1497
+ "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z": "solana-testnet"
1498
+ };
1499
+ var SOLANA_ALIAS_BY_REF = {
1500
+ mainnet: "solana",
1501
+ devnet: "solana-devnet",
1502
+ testnet: "solana-testnet"
1503
+ };
1504
+ function isRecord4(value) {
1505
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1506
+ }
1507
+ function asNonEmptyString(value) {
1508
+ if (typeof value !== "string") return void 0;
1509
+ const trimmed = value.trim();
1510
+ return trimmed.length > 0 ? trimmed : void 0;
1511
+ }
1512
+ function asPositiveNumber(value) {
1513
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
1514
+ }
1515
+ function pushIssue(issues, issue) {
1516
+ issues.push({
1517
+ stage: issue.stage ?? "payment_required",
1518
+ ...issue
1519
+ });
1520
+ }
1521
+ function summarizeIssues(issues) {
1522
+ const summary = {
1523
+ errorCount: 0,
1524
+ warnCount: 0,
1525
+ infoCount: 0,
1526
+ byCode: {}
1527
+ };
1528
+ for (const issue of issues) {
1529
+ if (issue.severity === "error") summary.errorCount += 1;
1530
+ else if (issue.severity === "warn") summary.warnCount += 1;
1531
+ else summary.infoCount += 1;
1532
+ summary.byCode[issue.code] = (summary.byCode[issue.code] ?? 0) + 1;
1533
+ }
1534
+ return summary;
1535
+ }
1536
+ function outputSchemaMissingSeverity(compatMode) {
1537
+ return compatMode === "strict" ? "error" : "warn";
1538
+ }
1539
+ function zodPathToString(path) {
1540
+ if (path.length === 0) return "$";
1541
+ let out = "";
1542
+ for (const segment of path) {
1543
+ if (typeof segment === "number") out += `[${segment}]`;
1544
+ else out += out.length === 0 ? segment : `.${segment}`;
1545
+ }
1546
+ return out;
1547
+ }
1548
+ function parseWithCoinbaseStructuralGate(payload, issues) {
1549
+ const baseParsed = parsePaymentRequired(payload);
1550
+ if (baseParsed.success) {
1551
+ return baseParsed.data;
1552
+ }
1553
+ for (const issue of baseParsed.error.issues) {
1554
+ pushIssue(issues, {
1555
+ code: VALIDATION_CODES.COINBASE_SCHEMA_INVALID,
1556
+ severity: "error",
1557
+ message: `Coinbase schema validation failed: ${issue.message}`,
1558
+ path: zodPathToString(issue.path),
1559
+ actual: issue.code
1560
+ });
1561
+ }
1562
+ return void 0;
1563
+ }
1564
+ function validateV2Network(networkRaw, index, issues) {
1565
+ if (!/^[^:\s]+:[^:\s]+$/.test(networkRaw)) {
1566
+ pushIssue(issues, {
1567
+ code: VALIDATION_CODES.NETWORK_CAIP2_INVALID,
1568
+ severity: "error",
1569
+ message: `Accept at index ${index} has invalid CAIP-2 network format`,
1570
+ hint: "Expected '<namespace>:<reference>', e.g. 'eip155:8453' or 'solana:5eykt4...'.",
1571
+ path: `accepts[${index}].network`,
1572
+ expected: "<namespace>:<reference>",
1573
+ actual: networkRaw
1574
+ });
1575
+ return void 0;
1576
+ }
1577
+ const [namespace, reference] = networkRaw.split(":");
1578
+ if (namespace === "eip155") {
1579
+ if (!/^\d+$/.test(reference)) {
1580
+ pushIssue(issues, {
1581
+ code: VALIDATION_CODES.NETWORK_EIP155_REFERENCE_INVALID,
1582
+ severity: "error",
1583
+ message: `Accept at index ${index} has invalid eip155 reference`,
1584
+ hint: "Use a numeric chain id, e.g. 'eip155:8453'.",
1585
+ path: `accepts[${index}].network`,
1586
+ expected: "eip155:<numeric-chain-id>",
1587
+ actual: networkRaw
1588
+ });
1589
+ return void 0;
1590
+ }
1591
+ return networkRaw;
1592
+ }
1593
+ if (namespace === "solana") {
1594
+ const canonical = SOLANA_CANONICAL_BY_REF[reference];
1595
+ if (canonical) return canonical;
1596
+ const alias = SOLANA_ALIAS_BY_REF[reference];
1597
+ if (alias) {
1598
+ pushIssue(issues, {
1599
+ code: VALIDATION_CODES.NETWORK_SOLANA_ALIAS_COMPAT,
1600
+ severity: "warn",
1601
+ message: `Accept at index ${index} uses compatibility Solana reference '${reference}'`,
1602
+ hint: "Use canonical Solana CAIP references for deterministic behavior.",
1603
+ path: `accepts[${index}].network`,
1604
+ actual: networkRaw
1605
+ });
1606
+ return alias;
1607
+ }
1608
+ pushIssue(issues, {
1609
+ code: VALIDATION_CODES.NETWORK_REFERENCE_UNKNOWN,
1610
+ severity: "error",
1611
+ message: `Accept at index ${index} uses unknown Solana reference '${reference}'`,
1612
+ hint: "Use canonical references for mainnet/devnet/testnet.",
1613
+ path: `accepts[${index}].network`,
1614
+ actual: networkRaw
1615
+ });
1616
+ return void 0;
1617
+ }
1618
+ return networkRaw;
1619
+ }
1620
+ function validateV1Network(networkRaw, index, issues) {
1621
+ if (networkRaw === "solana-mainnet-beta") {
1622
+ pushIssue(issues, {
1623
+ code: VALIDATION_CODES.NETWORK_SOLANA_ALIAS_INVALID,
1624
+ severity: "error",
1625
+ message: `Accept at index ${index} uses invalid Solana network alias`,
1626
+ hint: "Use 'solana' (v1) or canonical Solana CAIP reference (v2).",
1627
+ path: `accepts[${index}].network`,
1628
+ expected: "solana",
1629
+ actual: networkRaw
1630
+ });
1631
+ return void 0;
1632
+ }
1633
+ if (networkRaw.startsWith("solana-") && networkRaw !== "solana-devnet") {
1634
+ pushIssue(issues, {
1635
+ code: VALIDATION_CODES.NETWORK_SOLANA_ALIAS_INVALID,
1636
+ severity: "error",
1637
+ message: `Accept at index ${index} uses unsupported Solana network alias`,
1638
+ hint: "Use 'solana' or 'solana-devnet' for v1 payloads.",
1639
+ path: `accepts[${index}].network`,
1640
+ actual: networkRaw
1641
+ });
1642
+ return void 0;
1643
+ }
1644
+ if (networkRaw.includes(":")) {
1645
+ return validateV2Network(networkRaw, index, issues);
1646
+ }
1647
+ return networkRaw;
1648
+ }
1649
+ function validateAccept(acceptRaw, index, version, issues) {
1650
+ if (!isRecord4(acceptRaw)) {
1651
+ pushIssue(issues, {
1652
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1653
+ severity: "error",
1654
+ message: `Accept at index ${index} must be an object`,
1655
+ path: `accepts[${index}]`
1656
+ });
1657
+ return null;
1658
+ }
1659
+ const scheme = asNonEmptyString(acceptRaw.scheme);
1660
+ const networkRaw = asNonEmptyString(acceptRaw.network);
1661
+ const payTo = asNonEmptyString(acceptRaw.payTo);
1662
+ const asset = asNonEmptyString(acceptRaw.asset);
1663
+ const maxTimeoutSeconds = asPositiveNumber(acceptRaw.maxTimeoutSeconds);
1664
+ const amount = version === 2 ? asNonEmptyString(acceptRaw.amount) : asNonEmptyString(acceptRaw.maxAmountRequired);
1665
+ if (!scheme) {
1666
+ pushIssue(issues, {
1667
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1668
+ severity: "error",
1669
+ message: `Accept at index ${index} is missing scheme`,
1670
+ path: `accepts[${index}].scheme`
1671
+ });
1672
+ }
1673
+ if (!networkRaw) {
1674
+ pushIssue(issues, {
1675
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1676
+ severity: "error",
1677
+ message: `Accept at index ${index} is missing network`,
1678
+ path: `accepts[${index}].network`
1679
+ });
1680
+ }
1681
+ if (!amount) {
1682
+ pushIssue(issues, {
1683
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1684
+ severity: "error",
1685
+ message: `Accept at index ${index} is missing ${version === 2 ? "amount" : "maxAmountRequired"}`,
1686
+ path: `accepts[${index}].${version === 2 ? "amount" : "maxAmountRequired"}`
1687
+ });
1688
+ }
1689
+ if (!payTo) {
1690
+ pushIssue(issues, {
1691
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1692
+ severity: "error",
1693
+ message: `Accept at index ${index} is missing payTo`,
1694
+ path: `accepts[${index}].payTo`
1695
+ });
1696
+ }
1697
+ if (!asset) {
1698
+ pushIssue(issues, {
1699
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1700
+ severity: "error",
1701
+ message: `Accept at index ${index} is missing asset`,
1702
+ path: `accepts[${index}].asset`
1703
+ });
1704
+ }
1705
+ if (!maxTimeoutSeconds) {
1706
+ pushIssue(issues, {
1707
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1708
+ severity: "error",
1709
+ message: `Accept at index ${index} is missing or has invalid maxTimeoutSeconds`,
1710
+ path: `accepts[${index}].maxTimeoutSeconds`
1711
+ });
1712
+ }
1713
+ let normalizedNetwork;
1714
+ if (networkRaw) {
1715
+ normalizedNetwork = version === 2 ? validateV2Network(networkRaw, index, issues) : validateV1Network(networkRaw, index, issues);
1716
+ }
1717
+ return {
1718
+ index,
1719
+ network: normalizedNetwork ?? networkRaw ?? "unknown",
1720
+ networkRaw: networkRaw ?? "unknown",
1721
+ scheme,
1722
+ asset,
1723
+ payTo,
1724
+ amount,
1725
+ maxTimeoutSeconds
1726
+ };
1727
+ }
1728
+ function getSchemaPresence(payload, accepts, version) {
1729
+ if (version === 1) {
1730
+ const first = accepts[0];
1731
+ if (!isRecord4(first)) {
1732
+ return { hasInputSchema: false, hasOutputSchema: false };
1733
+ }
1734
+ const outputSchema = isRecord4(first.outputSchema) ? first.outputSchema : void 0;
1735
+ return {
1736
+ hasInputSchema: outputSchema?.input !== void 0 && outputSchema.input !== null,
1737
+ hasOutputSchema: outputSchema?.output !== void 0 && outputSchema.output !== null
1738
+ };
1739
+ }
1740
+ const extensions = isRecord4(payload.extensions) ? payload.extensions : void 0;
1741
+ const bazaar = extensions && isRecord4(extensions.bazaar) ? extensions.bazaar : void 0;
1742
+ const info = bazaar && isRecord4(bazaar.info) ? bazaar.info : void 0;
1743
+ return {
1744
+ hasInputSchema: info?.input !== void 0 && info.input !== null,
1745
+ hasOutputSchema: info?.output !== void 0 && info.output !== null
1746
+ };
1747
+ }
1748
+ function validatePaymentRequiredDetailed(payload, options = {}) {
1749
+ const issues = [];
1750
+ const compatMode = options.compatMode ?? DEFAULT_COMPAT_MODE;
1751
+ const requireInputSchema = options.requireInputSchema ?? true;
1752
+ const requireOutputSchema = options.requireOutputSchema ?? true;
1753
+ if (!isRecord4(payload)) {
1754
+ pushIssue(issues, {
1755
+ code: VALIDATION_CODES.X402_NOT_OBJECT,
1756
+ severity: "error",
1757
+ message: "Payment required payload must be a JSON object",
1758
+ path: "$"
1759
+ });
1760
+ if (options.metadata) {
1761
+ issues.push(...evaluateMetadataCompleteness(options.metadata));
1762
+ }
1763
+ return {
1764
+ valid: false,
1765
+ issues,
1766
+ summary: summarizeIssues(issues)
1767
+ };
1768
+ }
1769
+ const coinbaseParsed = parseWithCoinbaseStructuralGate(payload, issues);
1770
+ const versionRaw = payload.x402Version;
1771
+ let version;
1772
+ if (versionRaw === 1 || versionRaw === 2) {
1773
+ version = versionRaw;
1774
+ } else if (versionRaw === void 0) {
1775
+ pushIssue(issues, {
1776
+ code: VALIDATION_CODES.X402_VERSION_MISSING,
1777
+ severity: "error",
1778
+ message: "x402Version is required",
1779
+ path: "x402Version"
1780
+ });
1781
+ } else {
1782
+ pushIssue(issues, {
1783
+ code: VALIDATION_CODES.X402_VERSION_UNSUPPORTED,
1784
+ severity: "error",
1785
+ message: `Unsupported x402Version '${String(versionRaw)}'`,
1786
+ path: "x402Version",
1787
+ expected: "1 | 2",
1788
+ actual: String(versionRaw)
1789
+ });
1790
+ }
1791
+ const acceptsRaw = payload.accepts;
1792
+ let accepts = [];
1793
+ if (acceptsRaw === void 0) {
1794
+ pushIssue(issues, {
1795
+ code: VALIDATION_CODES.X402_ACCEPTS_MISSING,
1796
+ severity: "error",
1797
+ message: "accepts is required",
1798
+ path: "accepts"
1799
+ });
1800
+ } else if (!Array.isArray(acceptsRaw)) {
1801
+ pushIssue(issues, {
1802
+ code: VALIDATION_CODES.X402_ACCEPTS_INVALID,
1803
+ severity: "error",
1804
+ message: "accepts must be an array",
1805
+ path: "accepts"
1806
+ });
1807
+ } else {
1808
+ accepts = acceptsRaw;
1809
+ if (accepts.length === 0) {
1810
+ pushIssue(issues, {
1811
+ code: VALIDATION_CODES.X402_ACCEPTS_EMPTY,
1812
+ severity: "error",
1813
+ message: "accepts must contain at least one payment requirement",
1814
+ path: "accepts"
1815
+ });
1816
+ }
1817
+ }
1818
+ const normalizedAccepts = [];
1819
+ if (version && Array.isArray(accepts)) {
1820
+ accepts.forEach((accept, index) => {
1821
+ const normalized = validateAccept(accept, index, version, issues);
1822
+ if (normalized) normalizedAccepts.push(normalized);
1823
+ });
1824
+ const schemaPresence = getSchemaPresence(payload, accepts, version);
1825
+ if (requireInputSchema && !schemaPresence.hasInputSchema) {
1826
+ pushIssue(issues, {
1827
+ code: VALIDATION_CODES.SCHEMA_INPUT_MISSING,
1828
+ severity: "error",
1829
+ message: "Input schema is missing",
1830
+ hint: "Include input schema details so clients can invoke the endpoint correctly.",
1831
+ path: version === 1 ? "accepts[0].outputSchema.input" : "extensions.bazaar.info.input"
1832
+ });
1833
+ }
1834
+ if (requireOutputSchema && !schemaPresence.hasOutputSchema) {
1835
+ pushIssue(issues, {
1836
+ code: VALIDATION_CODES.SCHEMA_OUTPUT_MISSING,
1837
+ severity: outputSchemaMissingSeverity(compatMode),
1838
+ message: "Output schema is missing",
1839
+ hint: "Include output schema details so clients can validate responses.",
1840
+ path: version === 1 ? "accepts[0].outputSchema.output" : "extensions.bazaar.info.output"
1841
+ });
1842
+ }
1843
+ if (options.metadata) {
1844
+ issues.push(...evaluateMetadataCompleteness(options.metadata));
1845
+ }
1846
+ const summary2 = summarizeIssues(issues);
1847
+ return {
1848
+ valid: summary2.errorCount === 0,
1849
+ version,
1850
+ parsed: coinbaseParsed ?? payload,
1851
+ normalized: {
1852
+ version,
1853
+ accepts: normalizedAccepts,
1854
+ hasInputSchema: schemaPresence.hasInputSchema,
1855
+ hasOutputSchema: schemaPresence.hasOutputSchema
1856
+ },
1857
+ issues,
1858
+ summary: summary2
1859
+ };
1860
+ }
1861
+ if (options.metadata) {
1862
+ issues.push(...evaluateMetadataCompleteness(options.metadata));
1863
+ }
1864
+ const summary = summarizeIssues(issues);
1865
+ return {
1866
+ valid: summary.errorCount === 0,
1867
+ version,
1868
+ parsed: coinbaseParsed ?? payload,
1869
+ issues,
1870
+ summary
1871
+ };
1872
+ }
1873
+
1403
1874
  // src/adapters/mcp.ts
1404
1875
  var DEFAULT_GUIDANCE_AUTO_INCLUDE_MAX_TOKENS = 1e3;
1405
1876
  var OPENAPI_METHODS = [
@@ -1412,9 +1883,6 @@ var OPENAPI_METHODS = [
1412
1883
  "OPTIONS",
1413
1884
  "TRACE"
1414
1885
  ];
1415
- function isRecord4(value) {
1416
- return value != null && typeof value === "object" && !Array.isArray(value);
1417
- }
1418
1886
  function toFiniteNumber(value) {
1419
1887
  if (typeof value === "number" && Number.isFinite(value)) return value;
1420
1888
  if (typeof value === "string" && value.trim().length > 0) {
@@ -1472,7 +1940,7 @@ function inferFailureCause(warnings) {
1472
1940
  return { cause: "not_found" };
1473
1941
  }
1474
1942
  function getOpenApiInfo(document) {
1475
- if (!isRecord4(document) || !isRecord4(document.info)) return void 0;
1943
+ if (!isRecord3(document) || !isRecord3(document.info)) return void 0;
1476
1944
  if (typeof document.info.title !== "string") return void 0;
1477
1945
  return {
1478
1946
  title: document.info.title,
@@ -1515,15 +1983,15 @@ async function resolveGuidance(options) {
1515
1983
  };
1516
1984
  }
1517
1985
  function extractPathsDocument(document) {
1518
- if (!isRecord4(document)) return void 0;
1519
- if (!isRecord4(document.paths)) return void 0;
1986
+ if (!isRecord3(document)) return void 0;
1987
+ if (!isRecord3(document.paths)) return void 0;
1520
1988
  return document.paths;
1521
1989
  }
1522
1990
  function findMatchingOpenApiPath(paths, targetPath) {
1523
1991
  const exact = paths[targetPath];
1524
- if (isRecord4(exact)) return { matchedPath: targetPath, pathItem: exact };
1992
+ if (isRecord3(exact)) return { matchedPath: targetPath, pathItem: exact };
1525
1993
  for (const [specPath, entry] of Object.entries(paths)) {
1526
- if (!isRecord4(entry)) continue;
1994
+ if (!isRecord3(entry)) continue;
1527
1995
  const pattern = specPath.replace(/\{[^}]+\}/g, "[^/]+");
1528
1996
  const regex = new RegExp(`^${pattern}$`);
1529
1997
  if (regex.test(targetPath)) {
@@ -1539,11 +2007,11 @@ function resolveRef(document, ref, seen) {
1539
2007
  const parts = ref.slice(2).split("/");
1540
2008
  let current = document;
1541
2009
  for (const part of parts) {
1542
- if (!isRecord4(current)) return void 0;
2010
+ if (!isRecord3(current)) return void 0;
1543
2011
  current = current[part];
1544
2012
  if (current === void 0) return void 0;
1545
2013
  }
1546
- if (isRecord4(current)) return resolveRefs(document, current, seen);
2014
+ if (isRecord3(current)) return resolveRefs(document, current, seen);
1547
2015
  return current;
1548
2016
  }
1549
2017
  function resolveRefs(document, obj, seen, depth = 0) {
@@ -1552,20 +2020,20 @@ function resolveRefs(document, obj, seen, depth = 0) {
1552
2020
  for (const [key, value] of Object.entries(obj)) {
1553
2021
  if (key === "$ref" && typeof value === "string") {
1554
2022
  const deref = resolveRef(document, value, seen);
1555
- if (isRecord4(deref)) {
2023
+ if (isRecord3(deref)) {
1556
2024
  Object.assign(resolved, deref);
1557
2025
  } else {
1558
2026
  resolved[key] = value;
1559
2027
  }
1560
2028
  continue;
1561
2029
  }
1562
- if (isRecord4(value)) {
2030
+ if (isRecord3(value)) {
1563
2031
  resolved[key] = resolveRefs(document, value, seen, depth + 1);
1564
2032
  continue;
1565
2033
  }
1566
2034
  if (Array.isArray(value)) {
1567
2035
  resolved[key] = value.map(
1568
- (item) => isRecord4(item) ? resolveRefs(document, item, seen, depth + 1) : item
2036
+ (item) => isRecord3(item) ? resolveRefs(document, item, seen, depth + 1) : item
1569
2037
  );
1570
2038
  continue;
1571
2039
  }
@@ -1575,15 +2043,15 @@ function resolveRefs(document, obj, seen, depth = 0) {
1575
2043
  }
1576
2044
  function extractRequestBodySchema(operationSchema) {
1577
2045
  const requestBody = operationSchema.requestBody;
1578
- if (!isRecord4(requestBody)) return void 0;
2046
+ if (!isRecord3(requestBody)) return void 0;
1579
2047
  const content = requestBody.content;
1580
- if (!isRecord4(content)) return void 0;
2048
+ if (!isRecord3(content)) return void 0;
1581
2049
  const jsonMediaType = content["application/json"];
1582
- if (isRecord4(jsonMediaType) && isRecord4(jsonMediaType.schema)) {
2050
+ if (isRecord3(jsonMediaType) && isRecord3(jsonMediaType.schema)) {
1583
2051
  return jsonMediaType.schema;
1584
2052
  }
1585
2053
  for (const mediaType of Object.values(content)) {
1586
- if (isRecord4(mediaType) && isRecord4(mediaType.schema)) {
2054
+ if (isRecord3(mediaType) && isRecord3(mediaType.schema)) {
1587
2055
  return mediaType.schema;
1588
2056
  }
1589
2057
  }
@@ -1592,7 +2060,7 @@ function extractRequestBodySchema(operationSchema) {
1592
2060
  function extractParameters(operationSchema) {
1593
2061
  const params = operationSchema.parameters;
1594
2062
  if (!Array.isArray(params)) return [];
1595
- return params.filter((value) => isRecord4(value));
2063
+ return params.filter((value) => isRecord3(value));
1596
2064
  }
1597
2065
  function extractInputSchema(operationSchema) {
1598
2066
  const requestBody = extractRequestBodySchema(operationSchema);
@@ -1606,7 +2074,7 @@ function extractInputSchema(operationSchema) {
1606
2074
  }
1607
2075
  function parseOperationPrice(operation) {
1608
2076
  const paymentInfo = operation["x-payment-info"];
1609
- if (!isRecord4(paymentInfo)) return void 0;
2077
+ if (!isRecord3(paymentInfo)) return void 0;
1610
2078
  const fixed = toFiniteNumber(paymentInfo.price);
1611
2079
  if (fixed != null) return `$${String(fixed)}`;
1612
2080
  const min = toFiniteNumber(paymentInfo.minPrice);
@@ -1616,23 +2084,12 @@ function parseOperationPrice(operation) {
1616
2084
  }
1617
2085
  function parseOperationProtocols(operation) {
1618
2086
  const paymentInfo = operation["x-payment-info"];
1619
- if (!isRecord4(paymentInfo) || !Array.isArray(paymentInfo.protocols)) return void 0;
2087
+ if (!isRecord3(paymentInfo) || !Array.isArray(paymentInfo.protocols)) return void 0;
1620
2088
  const protocols = paymentInfo.protocols.filter(
1621
2089
  (protocol) => typeof protocol === "string" && protocol.length > 0
1622
2090
  );
1623
2091
  return protocols.length > 0 ? protocols : void 0;
1624
2092
  }
1625
- function parseOperationAuthMode(operation) {
1626
- const authExtension = operation["x-agentcash-auth"];
1627
- if (isRecord4(authExtension)) {
1628
- const mode = authExtension.mode;
1629
- if (mode === "paid" || mode === "siwx" || mode === "apiKey" || mode === "unprotected") {
1630
- return mode;
1631
- }
1632
- }
1633
- if (isRecord4(operation["x-payment-info"])) return "paid";
1634
- return void 0;
1635
- }
1636
2093
  function createResourceMap(result) {
1637
2094
  const map = /* @__PURE__ */ new Map();
1638
2095
  for (const resource of result.resources) {
@@ -1724,7 +2181,7 @@ async function inspectEndpointForMcp(options) {
1724
2181
  if (matched) {
1725
2182
  for (const candidate of OPENAPI_METHODS) {
1726
2183
  const operation = matched.pathItem[candidate.toLowerCase()];
1727
- if (!isRecord4(operation) || !isRecord4(document)) continue;
2184
+ if (!isRecord3(operation) || !isRecord3(document)) continue;
1728
2185
  specMethods.push(candidate);
1729
2186
  const resolvedOperation = resolveRefs(document, operation, /* @__PURE__ */ new Set());
1730
2187
  const resource = resourceMap.get(toResourceKey(origin, candidate, matched.matchedPath));
@@ -1732,7 +2189,7 @@ async function inspectEndpointForMcp(options) {
1732
2189
  const operationSummary = typeof resolvedOperation.summary === "string" ? resolvedOperation.summary : typeof resolvedOperation.description === "string" ? resolvedOperation.description : void 0;
1733
2190
  const operationPrice = parseOperationPrice(resolvedOperation);
1734
2191
  const operationProtocols = parseOperationProtocols(resolvedOperation);
1735
- const operationAuthMode = parseOperationAuthMode(resolvedOperation);
2192
+ const operationAuthMode = inferAuthMode(resolvedOperation) ?? "unprotected";
1736
2193
  const inputSchema = extractInputSchema(resolvedOperation);
1737
2194
  advisories[candidate] = {
1738
2195
  method: candidate,
@@ -2042,6 +2499,7 @@ export {
2042
2499
  LEGACY_SUNSET_DATE,
2043
2500
  STRICT_ESCALATION_CODES,
2044
2501
  UPGRADE_WARNING_CODES,
2502
+ VALIDATION_CODES,
2045
2503
  auditContextHarness,
2046
2504
  buildContextHarnessReport,
2047
2505
  computeUpgradeSignal,
@@ -2049,7 +2507,9 @@ export {
2049
2507
  discover,
2050
2508
  discoverDetailed,
2051
2509
  discoverForMcp,
2510
+ evaluateMetadataCompleteness,
2052
2511
  getHarnessClientProfile,
2053
2512
  inspectEndpointForMcp,
2054
- listHarnessClientProfiles
2513
+ listHarnessClientProfiles,
2514
+ validatePaymentRequiredDetailed
2055
2515
  };