@agentcash/discovery 0.1.2 → 0.1.3

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.3".includes("-mmm");
16
16
 
17
17
  // src/core/constants.ts
18
18
  var OPENAPI_PATH_CANDIDATES = ["/openapi.json", "/.well-known/openapi.json"];
@@ -1400,6 +1400,471 @@ async function runDiscovery({
1400
1400
  };
1401
1401
  }
1402
1402
 
1403
+ // src/validation/codes.ts
1404
+ var VALIDATION_CODES = {
1405
+ COINBASE_SCHEMA_INVALID: "COINBASE_SCHEMA_INVALID",
1406
+ X402_NOT_OBJECT: "X402_NOT_OBJECT",
1407
+ X402_VERSION_MISSING: "X402_VERSION_MISSING",
1408
+ X402_VERSION_UNSUPPORTED: "X402_VERSION_UNSUPPORTED",
1409
+ X402_ACCEPTS_MISSING: "X402_ACCEPTS_MISSING",
1410
+ X402_ACCEPTS_INVALID: "X402_ACCEPTS_INVALID",
1411
+ X402_ACCEPTS_EMPTY: "X402_ACCEPTS_EMPTY",
1412
+ X402_ACCEPT_ENTRY_INVALID: "X402_ACCEPT_ENTRY_INVALID",
1413
+ NETWORK_CAIP2_INVALID: "NETWORK_CAIP2_INVALID",
1414
+ NETWORK_EIP155_REFERENCE_INVALID: "NETWORK_EIP155_REFERENCE_INVALID",
1415
+ NETWORK_SOLANA_ALIAS_INVALID: "NETWORK_SOLANA_ALIAS_INVALID",
1416
+ NETWORK_SOLANA_ALIAS_COMPAT: "NETWORK_SOLANA_ALIAS_COMPAT",
1417
+ NETWORK_REFERENCE_UNKNOWN: "NETWORK_REFERENCE_UNKNOWN",
1418
+ SCHEMA_INPUT_MISSING: "SCHEMA_INPUT_MISSING",
1419
+ SCHEMA_OUTPUT_MISSING: "SCHEMA_OUTPUT_MISSING",
1420
+ METADATA_TITLE_MISSING: "METADATA_TITLE_MISSING",
1421
+ METADATA_DESCRIPTION_MISSING: "METADATA_DESCRIPTION_MISSING",
1422
+ METADATA_FAVICON_MISSING: "METADATA_FAVICON_MISSING",
1423
+ METADATA_OG_IMAGE_MISSING: "METADATA_OG_IMAGE_MISSING"
1424
+ };
1425
+
1426
+ // src/validation/metadata.ts
1427
+ function hasText(value) {
1428
+ return typeof value === "string" && value.trim().length > 0;
1429
+ }
1430
+ function hasOgImage(metadata) {
1431
+ const images = metadata.ogImages;
1432
+ if (!Array.isArray(images) || images.length === 0) return false;
1433
+ return images.some((entry) => {
1434
+ if (typeof entry === "string") {
1435
+ return entry.trim().length > 0;
1436
+ }
1437
+ if (!entry || typeof entry !== "object") return false;
1438
+ return hasText(entry.url);
1439
+ });
1440
+ }
1441
+ function evaluateMetadataCompleteness(metadata) {
1442
+ const issues = [];
1443
+ if (!hasText(metadata.title)) {
1444
+ issues.push({
1445
+ code: VALIDATION_CODES.METADATA_TITLE_MISSING,
1446
+ severity: "warn",
1447
+ message: "Metadata title is missing",
1448
+ hint: "Provide a page title to improve discovery quality and UX.",
1449
+ path: "metadata.title",
1450
+ stage: "metadata"
1451
+ });
1452
+ }
1453
+ if (!hasText(metadata.description)) {
1454
+ issues.push({
1455
+ code: VALIDATION_CODES.METADATA_DESCRIPTION_MISSING,
1456
+ severity: "warn",
1457
+ message: "Metadata description is missing",
1458
+ hint: "Provide a concise description for integrators and search previews.",
1459
+ path: "metadata.description",
1460
+ stage: "metadata"
1461
+ });
1462
+ }
1463
+ if (!hasText(metadata.favicon)) {
1464
+ issues.push({
1465
+ code: VALIDATION_CODES.METADATA_FAVICON_MISSING,
1466
+ severity: "warn",
1467
+ message: "Metadata favicon is missing",
1468
+ hint: "Expose a favicon URL for stronger brand attribution.",
1469
+ path: "metadata.favicon",
1470
+ stage: "metadata"
1471
+ });
1472
+ }
1473
+ if (!hasOgImage(metadata)) {
1474
+ issues.push({
1475
+ code: VALIDATION_CODES.METADATA_OG_IMAGE_MISSING,
1476
+ severity: "warn",
1477
+ message: "Metadata OG image is missing",
1478
+ hint: "Provide an Open Graph image to improve sharing previews.",
1479
+ path: "metadata.ogImages",
1480
+ stage: "metadata"
1481
+ });
1482
+ }
1483
+ return issues;
1484
+ }
1485
+
1486
+ // src/validation/payment-required.ts
1487
+ import { parsePaymentRequired } from "@x402/core/schemas";
1488
+ var SOLANA_CANONICAL_BY_REF = {
1489
+ "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": "solana",
1490
+ EtWTRABZaYq6iMfeYKouRu166VU2xqa1: "solana-devnet",
1491
+ "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z": "solana-testnet"
1492
+ };
1493
+ var SOLANA_ALIAS_BY_REF = {
1494
+ mainnet: "solana",
1495
+ devnet: "solana-devnet",
1496
+ testnet: "solana-testnet"
1497
+ };
1498
+ function isRecord4(value) {
1499
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1500
+ }
1501
+ function asNonEmptyString(value) {
1502
+ if (typeof value !== "string") return void 0;
1503
+ const trimmed = value.trim();
1504
+ return trimmed.length > 0 ? trimmed : void 0;
1505
+ }
1506
+ function asPositiveNumber(value) {
1507
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
1508
+ }
1509
+ function pushIssue(issues, issue) {
1510
+ issues.push({
1511
+ stage: issue.stage ?? "payment_required",
1512
+ ...issue
1513
+ });
1514
+ }
1515
+ function summarizeIssues(issues) {
1516
+ const summary = {
1517
+ errorCount: 0,
1518
+ warnCount: 0,
1519
+ infoCount: 0,
1520
+ byCode: {}
1521
+ };
1522
+ for (const issue of issues) {
1523
+ if (issue.severity === "error") summary.errorCount += 1;
1524
+ else if (issue.severity === "warn") summary.warnCount += 1;
1525
+ else summary.infoCount += 1;
1526
+ summary.byCode[issue.code] = (summary.byCode[issue.code] ?? 0) + 1;
1527
+ }
1528
+ return summary;
1529
+ }
1530
+ function outputSchemaMissingSeverity(compatMode) {
1531
+ return compatMode === "strict" ? "error" : "warn";
1532
+ }
1533
+ function zodPathToString(path) {
1534
+ if (path.length === 0) return "$";
1535
+ let out = "";
1536
+ for (const segment of path) {
1537
+ if (typeof segment === "number") out += `[${segment}]`;
1538
+ else out += out.length === 0 ? segment : `.${segment}`;
1539
+ }
1540
+ return out;
1541
+ }
1542
+ function parseWithCoinbaseStructuralGate(payload, issues) {
1543
+ const baseParsed = parsePaymentRequired(payload);
1544
+ if (baseParsed.success) {
1545
+ return baseParsed.data;
1546
+ }
1547
+ for (const issue of baseParsed.error.issues) {
1548
+ pushIssue(issues, {
1549
+ code: VALIDATION_CODES.COINBASE_SCHEMA_INVALID,
1550
+ severity: "error",
1551
+ message: `Coinbase schema validation failed: ${issue.message}`,
1552
+ path: zodPathToString(issue.path),
1553
+ actual: issue.code
1554
+ });
1555
+ }
1556
+ return void 0;
1557
+ }
1558
+ function validateV2Network(networkRaw, index, issues) {
1559
+ if (!/^[^:\s]+:[^:\s]+$/.test(networkRaw)) {
1560
+ pushIssue(issues, {
1561
+ code: VALIDATION_CODES.NETWORK_CAIP2_INVALID,
1562
+ severity: "error",
1563
+ message: `Accept at index ${index} has invalid CAIP-2 network format`,
1564
+ hint: "Expected '<namespace>:<reference>', e.g. 'eip155:8453' or 'solana:5eykt4...'.",
1565
+ path: `accepts[${index}].network`,
1566
+ expected: "<namespace>:<reference>",
1567
+ actual: networkRaw
1568
+ });
1569
+ return void 0;
1570
+ }
1571
+ const [namespace, reference] = networkRaw.split(":");
1572
+ if (namespace === "eip155") {
1573
+ if (!/^\d+$/.test(reference)) {
1574
+ pushIssue(issues, {
1575
+ code: VALIDATION_CODES.NETWORK_EIP155_REFERENCE_INVALID,
1576
+ severity: "error",
1577
+ message: `Accept at index ${index} has invalid eip155 reference`,
1578
+ hint: "Use a numeric chain id, e.g. 'eip155:8453'.",
1579
+ path: `accepts[${index}].network`,
1580
+ expected: "eip155:<numeric-chain-id>",
1581
+ actual: networkRaw
1582
+ });
1583
+ return void 0;
1584
+ }
1585
+ return networkRaw;
1586
+ }
1587
+ if (namespace === "solana") {
1588
+ const canonical = SOLANA_CANONICAL_BY_REF[reference];
1589
+ if (canonical) return canonical;
1590
+ const alias = SOLANA_ALIAS_BY_REF[reference];
1591
+ if (alias) {
1592
+ pushIssue(issues, {
1593
+ code: VALIDATION_CODES.NETWORK_SOLANA_ALIAS_COMPAT,
1594
+ severity: "warn",
1595
+ message: `Accept at index ${index} uses compatibility Solana reference '${reference}'`,
1596
+ hint: "Use canonical Solana CAIP references for deterministic behavior.",
1597
+ path: `accepts[${index}].network`,
1598
+ actual: networkRaw
1599
+ });
1600
+ return alias;
1601
+ }
1602
+ pushIssue(issues, {
1603
+ code: VALIDATION_CODES.NETWORK_REFERENCE_UNKNOWN,
1604
+ severity: "error",
1605
+ message: `Accept at index ${index} uses unknown Solana reference '${reference}'`,
1606
+ hint: "Use canonical references for mainnet/devnet/testnet.",
1607
+ path: `accepts[${index}].network`,
1608
+ actual: networkRaw
1609
+ });
1610
+ return void 0;
1611
+ }
1612
+ return networkRaw;
1613
+ }
1614
+ function validateV1Network(networkRaw, index, issues) {
1615
+ if (networkRaw === "solana-mainnet-beta") {
1616
+ pushIssue(issues, {
1617
+ code: VALIDATION_CODES.NETWORK_SOLANA_ALIAS_INVALID,
1618
+ severity: "error",
1619
+ message: `Accept at index ${index} uses invalid Solana network alias`,
1620
+ hint: "Use 'solana' (v1) or canonical Solana CAIP reference (v2).",
1621
+ path: `accepts[${index}].network`,
1622
+ expected: "solana",
1623
+ actual: networkRaw
1624
+ });
1625
+ return void 0;
1626
+ }
1627
+ if (networkRaw.startsWith("solana-") && networkRaw !== "solana-devnet") {
1628
+ pushIssue(issues, {
1629
+ code: VALIDATION_CODES.NETWORK_SOLANA_ALIAS_INVALID,
1630
+ severity: "error",
1631
+ message: `Accept at index ${index} uses unsupported Solana network alias`,
1632
+ hint: "Use 'solana' or 'solana-devnet' for v1 payloads.",
1633
+ path: `accepts[${index}].network`,
1634
+ actual: networkRaw
1635
+ });
1636
+ return void 0;
1637
+ }
1638
+ if (networkRaw.includes(":")) {
1639
+ return validateV2Network(networkRaw, index, issues);
1640
+ }
1641
+ return networkRaw;
1642
+ }
1643
+ function validateAccept(acceptRaw, index, version, issues) {
1644
+ if (!isRecord4(acceptRaw)) {
1645
+ pushIssue(issues, {
1646
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1647
+ severity: "error",
1648
+ message: `Accept at index ${index} must be an object`,
1649
+ path: `accepts[${index}]`
1650
+ });
1651
+ return null;
1652
+ }
1653
+ const scheme = asNonEmptyString(acceptRaw.scheme);
1654
+ const networkRaw = asNonEmptyString(acceptRaw.network);
1655
+ const payTo = asNonEmptyString(acceptRaw.payTo);
1656
+ const asset = asNonEmptyString(acceptRaw.asset);
1657
+ const maxTimeoutSeconds = asPositiveNumber(acceptRaw.maxTimeoutSeconds);
1658
+ const amount = version === 2 ? asNonEmptyString(acceptRaw.amount) : asNonEmptyString(acceptRaw.maxAmountRequired);
1659
+ if (!scheme) {
1660
+ pushIssue(issues, {
1661
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1662
+ severity: "error",
1663
+ message: `Accept at index ${index} is missing scheme`,
1664
+ path: `accepts[${index}].scheme`
1665
+ });
1666
+ }
1667
+ if (!networkRaw) {
1668
+ pushIssue(issues, {
1669
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1670
+ severity: "error",
1671
+ message: `Accept at index ${index} is missing network`,
1672
+ path: `accepts[${index}].network`
1673
+ });
1674
+ }
1675
+ if (!amount) {
1676
+ pushIssue(issues, {
1677
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1678
+ severity: "error",
1679
+ message: `Accept at index ${index} is missing ${version === 2 ? "amount" : "maxAmountRequired"}`,
1680
+ path: `accepts[${index}].${version === 2 ? "amount" : "maxAmountRequired"}`
1681
+ });
1682
+ }
1683
+ if (!payTo) {
1684
+ pushIssue(issues, {
1685
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1686
+ severity: "error",
1687
+ message: `Accept at index ${index} is missing payTo`,
1688
+ path: `accepts[${index}].payTo`
1689
+ });
1690
+ }
1691
+ if (!asset) {
1692
+ pushIssue(issues, {
1693
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1694
+ severity: "error",
1695
+ message: `Accept at index ${index} is missing asset`,
1696
+ path: `accepts[${index}].asset`
1697
+ });
1698
+ }
1699
+ if (!maxTimeoutSeconds) {
1700
+ pushIssue(issues, {
1701
+ code: VALIDATION_CODES.X402_ACCEPT_ENTRY_INVALID,
1702
+ severity: "error",
1703
+ message: `Accept at index ${index} is missing or has invalid maxTimeoutSeconds`,
1704
+ path: `accepts[${index}].maxTimeoutSeconds`
1705
+ });
1706
+ }
1707
+ let normalizedNetwork;
1708
+ if (networkRaw) {
1709
+ normalizedNetwork = version === 2 ? validateV2Network(networkRaw, index, issues) : validateV1Network(networkRaw, index, issues);
1710
+ }
1711
+ return {
1712
+ index,
1713
+ network: normalizedNetwork ?? networkRaw ?? "unknown",
1714
+ networkRaw: networkRaw ?? "unknown",
1715
+ scheme,
1716
+ asset,
1717
+ payTo,
1718
+ amount,
1719
+ maxTimeoutSeconds
1720
+ };
1721
+ }
1722
+ function getSchemaPresence(payload, accepts, version) {
1723
+ if (version === 1) {
1724
+ const first = accepts[0];
1725
+ if (!isRecord4(first)) {
1726
+ return { hasInputSchema: false, hasOutputSchema: false };
1727
+ }
1728
+ const outputSchema = isRecord4(first.outputSchema) ? first.outputSchema : void 0;
1729
+ return {
1730
+ hasInputSchema: outputSchema?.input !== void 0 && outputSchema.input !== null,
1731
+ hasOutputSchema: outputSchema?.output !== void 0 && outputSchema.output !== null
1732
+ };
1733
+ }
1734
+ const extensions = isRecord4(payload.extensions) ? payload.extensions : void 0;
1735
+ const bazaar = extensions && isRecord4(extensions.bazaar) ? extensions.bazaar : void 0;
1736
+ const info = bazaar && isRecord4(bazaar.info) ? bazaar.info : void 0;
1737
+ return {
1738
+ hasInputSchema: info?.input !== void 0 && info.input !== null,
1739
+ hasOutputSchema: info?.output !== void 0 && info.output !== null
1740
+ };
1741
+ }
1742
+ function validatePaymentRequiredDetailed(payload, options = {}) {
1743
+ const issues = [];
1744
+ const compatMode = options.compatMode ?? DEFAULT_COMPAT_MODE;
1745
+ const requireInputSchema = options.requireInputSchema ?? true;
1746
+ const requireOutputSchema = options.requireOutputSchema ?? true;
1747
+ if (!isRecord4(payload)) {
1748
+ pushIssue(issues, {
1749
+ code: VALIDATION_CODES.X402_NOT_OBJECT,
1750
+ severity: "error",
1751
+ message: "Payment required payload must be a JSON object",
1752
+ path: "$"
1753
+ });
1754
+ if (options.metadata) {
1755
+ issues.push(...evaluateMetadataCompleteness(options.metadata));
1756
+ }
1757
+ return {
1758
+ valid: false,
1759
+ issues,
1760
+ summary: summarizeIssues(issues)
1761
+ };
1762
+ }
1763
+ const coinbaseParsed = parseWithCoinbaseStructuralGate(payload, issues);
1764
+ const versionRaw = payload.x402Version;
1765
+ let version;
1766
+ if (versionRaw === 1 || versionRaw === 2) {
1767
+ version = versionRaw;
1768
+ } else if (versionRaw === void 0) {
1769
+ pushIssue(issues, {
1770
+ code: VALIDATION_CODES.X402_VERSION_MISSING,
1771
+ severity: "error",
1772
+ message: "x402Version is required",
1773
+ path: "x402Version"
1774
+ });
1775
+ } else {
1776
+ pushIssue(issues, {
1777
+ code: VALIDATION_CODES.X402_VERSION_UNSUPPORTED,
1778
+ severity: "error",
1779
+ message: `Unsupported x402Version '${String(versionRaw)}'`,
1780
+ path: "x402Version",
1781
+ expected: "1 | 2",
1782
+ actual: String(versionRaw)
1783
+ });
1784
+ }
1785
+ const acceptsRaw = payload.accepts;
1786
+ let accepts = [];
1787
+ if (acceptsRaw === void 0) {
1788
+ pushIssue(issues, {
1789
+ code: VALIDATION_CODES.X402_ACCEPTS_MISSING,
1790
+ severity: "error",
1791
+ message: "accepts is required",
1792
+ path: "accepts"
1793
+ });
1794
+ } else if (!Array.isArray(acceptsRaw)) {
1795
+ pushIssue(issues, {
1796
+ code: VALIDATION_CODES.X402_ACCEPTS_INVALID,
1797
+ severity: "error",
1798
+ message: "accepts must be an array",
1799
+ path: "accepts"
1800
+ });
1801
+ } else {
1802
+ accepts = acceptsRaw;
1803
+ if (accepts.length === 0) {
1804
+ pushIssue(issues, {
1805
+ code: VALIDATION_CODES.X402_ACCEPTS_EMPTY,
1806
+ severity: "error",
1807
+ message: "accepts must contain at least one payment requirement",
1808
+ path: "accepts"
1809
+ });
1810
+ }
1811
+ }
1812
+ const normalizedAccepts = [];
1813
+ if (version && Array.isArray(accepts)) {
1814
+ accepts.forEach((accept, index) => {
1815
+ const normalized = validateAccept(accept, index, version, issues);
1816
+ if (normalized) normalizedAccepts.push(normalized);
1817
+ });
1818
+ const schemaPresence = getSchemaPresence(payload, accepts, version);
1819
+ if (requireInputSchema && !schemaPresence.hasInputSchema) {
1820
+ pushIssue(issues, {
1821
+ code: VALIDATION_CODES.SCHEMA_INPUT_MISSING,
1822
+ severity: "error",
1823
+ message: "Input schema is missing",
1824
+ hint: "Include input schema details so clients can invoke the endpoint correctly.",
1825
+ path: version === 1 ? "accepts[0].outputSchema.input" : "extensions.bazaar.info.input"
1826
+ });
1827
+ }
1828
+ if (requireOutputSchema && !schemaPresence.hasOutputSchema) {
1829
+ pushIssue(issues, {
1830
+ code: VALIDATION_CODES.SCHEMA_OUTPUT_MISSING,
1831
+ severity: outputSchemaMissingSeverity(compatMode),
1832
+ message: "Output schema is missing",
1833
+ hint: "Include output schema details so clients can validate responses.",
1834
+ path: version === 1 ? "accepts[0].outputSchema.output" : "extensions.bazaar.info.output"
1835
+ });
1836
+ }
1837
+ if (options.metadata) {
1838
+ issues.push(...evaluateMetadataCompleteness(options.metadata));
1839
+ }
1840
+ const summary2 = summarizeIssues(issues);
1841
+ return {
1842
+ valid: summary2.errorCount === 0,
1843
+ version,
1844
+ parsed: coinbaseParsed ?? payload,
1845
+ normalized: {
1846
+ version,
1847
+ accepts: normalizedAccepts,
1848
+ hasInputSchema: schemaPresence.hasInputSchema,
1849
+ hasOutputSchema: schemaPresence.hasOutputSchema
1850
+ },
1851
+ issues,
1852
+ summary: summary2
1853
+ };
1854
+ }
1855
+ if (options.metadata) {
1856
+ issues.push(...evaluateMetadataCompleteness(options.metadata));
1857
+ }
1858
+ const summary = summarizeIssues(issues);
1859
+ return {
1860
+ valid: summary.errorCount === 0,
1861
+ version,
1862
+ parsed: coinbaseParsed ?? payload,
1863
+ issues,
1864
+ summary
1865
+ };
1866
+ }
1867
+
1403
1868
  // src/adapters/mcp.ts
1404
1869
  var DEFAULT_GUIDANCE_AUTO_INCLUDE_MAX_TOKENS = 1e3;
1405
1870
  var OPENAPI_METHODS = [
@@ -1412,7 +1877,7 @@ var OPENAPI_METHODS = [
1412
1877
  "OPTIONS",
1413
1878
  "TRACE"
1414
1879
  ];
1415
- function isRecord4(value) {
1880
+ function isRecord5(value) {
1416
1881
  return value != null && typeof value === "object" && !Array.isArray(value);
1417
1882
  }
1418
1883
  function toFiniteNumber(value) {
@@ -1472,7 +1937,7 @@ function inferFailureCause(warnings) {
1472
1937
  return { cause: "not_found" };
1473
1938
  }
1474
1939
  function getOpenApiInfo(document) {
1475
- if (!isRecord4(document) || !isRecord4(document.info)) return void 0;
1940
+ if (!isRecord5(document) || !isRecord5(document.info)) return void 0;
1476
1941
  if (typeof document.info.title !== "string") return void 0;
1477
1942
  return {
1478
1943
  title: document.info.title,
@@ -1515,15 +1980,15 @@ async function resolveGuidance(options) {
1515
1980
  };
1516
1981
  }
1517
1982
  function extractPathsDocument(document) {
1518
- if (!isRecord4(document)) return void 0;
1519
- if (!isRecord4(document.paths)) return void 0;
1983
+ if (!isRecord5(document)) return void 0;
1984
+ if (!isRecord5(document.paths)) return void 0;
1520
1985
  return document.paths;
1521
1986
  }
1522
1987
  function findMatchingOpenApiPath(paths, targetPath) {
1523
1988
  const exact = paths[targetPath];
1524
- if (isRecord4(exact)) return { matchedPath: targetPath, pathItem: exact };
1989
+ if (isRecord5(exact)) return { matchedPath: targetPath, pathItem: exact };
1525
1990
  for (const [specPath, entry] of Object.entries(paths)) {
1526
- if (!isRecord4(entry)) continue;
1991
+ if (!isRecord5(entry)) continue;
1527
1992
  const pattern = specPath.replace(/\{[^}]+\}/g, "[^/]+");
1528
1993
  const regex = new RegExp(`^${pattern}$`);
1529
1994
  if (regex.test(targetPath)) {
@@ -1539,11 +2004,11 @@ function resolveRef(document, ref, seen) {
1539
2004
  const parts = ref.slice(2).split("/");
1540
2005
  let current = document;
1541
2006
  for (const part of parts) {
1542
- if (!isRecord4(current)) return void 0;
2007
+ if (!isRecord5(current)) return void 0;
1543
2008
  current = current[part];
1544
2009
  if (current === void 0) return void 0;
1545
2010
  }
1546
- if (isRecord4(current)) return resolveRefs(document, current, seen);
2011
+ if (isRecord5(current)) return resolveRefs(document, current, seen);
1547
2012
  return current;
1548
2013
  }
1549
2014
  function resolveRefs(document, obj, seen, depth = 0) {
@@ -1552,20 +2017,20 @@ function resolveRefs(document, obj, seen, depth = 0) {
1552
2017
  for (const [key, value] of Object.entries(obj)) {
1553
2018
  if (key === "$ref" && typeof value === "string") {
1554
2019
  const deref = resolveRef(document, value, seen);
1555
- if (isRecord4(deref)) {
2020
+ if (isRecord5(deref)) {
1556
2021
  Object.assign(resolved, deref);
1557
2022
  } else {
1558
2023
  resolved[key] = value;
1559
2024
  }
1560
2025
  continue;
1561
2026
  }
1562
- if (isRecord4(value)) {
2027
+ if (isRecord5(value)) {
1563
2028
  resolved[key] = resolveRefs(document, value, seen, depth + 1);
1564
2029
  continue;
1565
2030
  }
1566
2031
  if (Array.isArray(value)) {
1567
2032
  resolved[key] = value.map(
1568
- (item) => isRecord4(item) ? resolveRefs(document, item, seen, depth + 1) : item
2033
+ (item) => isRecord5(item) ? resolveRefs(document, item, seen, depth + 1) : item
1569
2034
  );
1570
2035
  continue;
1571
2036
  }
@@ -1575,15 +2040,15 @@ function resolveRefs(document, obj, seen, depth = 0) {
1575
2040
  }
1576
2041
  function extractRequestBodySchema(operationSchema) {
1577
2042
  const requestBody = operationSchema.requestBody;
1578
- if (!isRecord4(requestBody)) return void 0;
2043
+ if (!isRecord5(requestBody)) return void 0;
1579
2044
  const content = requestBody.content;
1580
- if (!isRecord4(content)) return void 0;
2045
+ if (!isRecord5(content)) return void 0;
1581
2046
  const jsonMediaType = content["application/json"];
1582
- if (isRecord4(jsonMediaType) && isRecord4(jsonMediaType.schema)) {
2047
+ if (isRecord5(jsonMediaType) && isRecord5(jsonMediaType.schema)) {
1583
2048
  return jsonMediaType.schema;
1584
2049
  }
1585
2050
  for (const mediaType of Object.values(content)) {
1586
- if (isRecord4(mediaType) && isRecord4(mediaType.schema)) {
2051
+ if (isRecord5(mediaType) && isRecord5(mediaType.schema)) {
1587
2052
  return mediaType.schema;
1588
2053
  }
1589
2054
  }
@@ -1592,7 +2057,7 @@ function extractRequestBodySchema(operationSchema) {
1592
2057
  function extractParameters(operationSchema) {
1593
2058
  const params = operationSchema.parameters;
1594
2059
  if (!Array.isArray(params)) return [];
1595
- return params.filter((value) => isRecord4(value));
2060
+ return params.filter((value) => isRecord5(value));
1596
2061
  }
1597
2062
  function extractInputSchema(operationSchema) {
1598
2063
  const requestBody = extractRequestBodySchema(operationSchema);
@@ -1606,7 +2071,7 @@ function extractInputSchema(operationSchema) {
1606
2071
  }
1607
2072
  function parseOperationPrice(operation) {
1608
2073
  const paymentInfo = operation["x-payment-info"];
1609
- if (!isRecord4(paymentInfo)) return void 0;
2074
+ if (!isRecord5(paymentInfo)) return void 0;
1610
2075
  const fixed = toFiniteNumber(paymentInfo.price);
1611
2076
  if (fixed != null) return `$${String(fixed)}`;
1612
2077
  const min = toFiniteNumber(paymentInfo.minPrice);
@@ -1616,7 +2081,7 @@ function parseOperationPrice(operation) {
1616
2081
  }
1617
2082
  function parseOperationProtocols(operation) {
1618
2083
  const paymentInfo = operation["x-payment-info"];
1619
- if (!isRecord4(paymentInfo) || !Array.isArray(paymentInfo.protocols)) return void 0;
2084
+ if (!isRecord5(paymentInfo) || !Array.isArray(paymentInfo.protocols)) return void 0;
1620
2085
  const protocols = paymentInfo.protocols.filter(
1621
2086
  (protocol) => typeof protocol === "string" && protocol.length > 0
1622
2087
  );
@@ -1624,13 +2089,13 @@ function parseOperationProtocols(operation) {
1624
2089
  }
1625
2090
  function parseOperationAuthMode(operation) {
1626
2091
  const authExtension = operation["x-agentcash-auth"];
1627
- if (isRecord4(authExtension)) {
2092
+ if (isRecord5(authExtension)) {
1628
2093
  const mode = authExtension.mode;
1629
2094
  if (mode === "paid" || mode === "siwx" || mode === "apiKey" || mode === "unprotected") {
1630
2095
  return mode;
1631
2096
  }
1632
2097
  }
1633
- if (isRecord4(operation["x-payment-info"])) return "paid";
2098
+ if (isRecord5(operation["x-payment-info"])) return "paid";
1634
2099
  return void 0;
1635
2100
  }
1636
2101
  function createResourceMap(result) {
@@ -1724,7 +2189,7 @@ async function inspectEndpointForMcp(options) {
1724
2189
  if (matched) {
1725
2190
  for (const candidate of OPENAPI_METHODS) {
1726
2191
  const operation = matched.pathItem[candidate.toLowerCase()];
1727
- if (!isRecord4(operation) || !isRecord4(document)) continue;
2192
+ if (!isRecord5(operation) || !isRecord5(document)) continue;
1728
2193
  specMethods.push(candidate);
1729
2194
  const resolvedOperation = resolveRefs(document, operation, /* @__PURE__ */ new Set());
1730
2195
  const resource = resourceMap.get(toResourceKey(origin, candidate, matched.matchedPath));
@@ -2042,6 +2507,7 @@ export {
2042
2507
  LEGACY_SUNSET_DATE,
2043
2508
  STRICT_ESCALATION_CODES,
2044
2509
  UPGRADE_WARNING_CODES,
2510
+ VALIDATION_CODES,
2045
2511
  auditContextHarness,
2046
2512
  buildContextHarnessReport,
2047
2513
  computeUpgradeSignal,
@@ -2049,7 +2515,9 @@ export {
2049
2515
  discover,
2050
2516
  discoverDetailed,
2051
2517
  discoverForMcp,
2518
+ evaluateMetadataCompleteness,
2052
2519
  getHarnessClientProfile,
2053
2520
  inspectEndpointForMcp,
2054
- listHarnessClientProfiles
2521
+ listHarnessClientProfiles,
2522
+ validatePaymentRequiredDetailed
2055
2523
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/discovery",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Canonical OpenAPI-first discovery runtime for the agentcash ecosystem",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -64,6 +64,7 @@
64
64
  "access": "public"
65
65
  },
66
66
  "dependencies": {
67
+ "@x402/core": "^2.5.0",
67
68
  "table": "^6.9.0",
68
69
  "zod": "^4.1.13"
69
70
  },