@nevermined-io/payments 1.12.0 → 1.14.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.
Files changed (98) hide show
  1. package/README.md +33 -0
  2. package/dist/a2a/clientRegistry.d.ts +10 -1
  3. package/dist/a2a/clientRegistry.d.ts.map +1 -1
  4. package/dist/a2a/clientRegistry.js +13 -4
  5. package/dist/a2a/clientRegistry.js.map +1 -1
  6. package/dist/a2a/paymentsClient.d.ts +60 -3
  7. package/dist/a2a/paymentsClient.d.ts.map +1 -1
  8. package/dist/a2a/paymentsClient.js +129 -8
  9. package/dist/a2a/paymentsClient.js.map +1 -1
  10. package/dist/a2a/paymentsRequestHandler.d.ts +5 -1
  11. package/dist/a2a/paymentsRequestHandler.d.ts.map +1 -1
  12. package/dist/a2a/paymentsRequestHandler.js +156 -13
  13. package/dist/a2a/paymentsRequestHandler.js.map +1 -1
  14. package/dist/a2a/types.d.ts +30 -0
  15. package/dist/a2a/types.d.ts.map +1 -1
  16. package/dist/a2a/types.js.map +1 -1
  17. package/dist/api/agents-api.d.ts +4 -3
  18. package/dist/api/agents-api.d.ts.map +1 -1
  19. package/dist/api/agents-api.js +7 -4
  20. package/dist/api/agents-api.js.map +1 -1
  21. package/dist/api/base-payments.d.ts +7 -2
  22. package/dist/api/base-payments.d.ts.map +1 -1
  23. package/dist/api/base-payments.js +15 -8
  24. package/dist/api/base-payments.js.map +1 -1
  25. package/dist/api/nvm-api.d.ts +5 -5
  26. package/dist/api/observability-api/types.d.ts +34 -6
  27. package/dist/api/observability-api/types.d.ts.map +1 -1
  28. package/dist/api/observability-api/types.js.map +1 -1
  29. package/dist/api/plans-api.d.ts +15 -12
  30. package/dist/api/plans-api.d.ts.map +1 -1
  31. package/dist/api/plans-api.js +17 -14
  32. package/dist/api/plans-api.js.map +1 -1
  33. package/dist/api/query-api.d.ts +1 -1
  34. package/dist/common/types.d.ts +175 -18
  35. package/dist/common/types.d.ts.map +1 -1
  36. package/dist/common/types.js.map +1 -1
  37. package/dist/index.d.ts +2 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +1 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/mcp/core/paywall.d.ts.map +1 -1
  42. package/dist/mcp/core/paywall.js +69 -8
  43. package/dist/mcp/core/paywall.js.map +1 -1
  44. package/dist/mcp/http/index.d.ts +1 -1
  45. package/dist/mcp/http/index.d.ts.map +1 -1
  46. package/dist/mcp/http/index.js +1 -1
  47. package/dist/mcp/http/index.js.map +1 -1
  48. package/dist/mcp/http/oauth-metadata.d.ts +32 -0
  49. package/dist/mcp/http/oauth-metadata.d.ts.map +1 -1
  50. package/dist/mcp/http/oauth-metadata.js +329 -34
  51. package/dist/mcp/http/oauth-metadata.js.map +1 -1
  52. package/dist/mcp/index.d.ts +4 -4
  53. package/dist/mcp/index.d.ts.map +1 -1
  54. package/dist/mcp/index.js +5 -5
  55. package/dist/mcp/index.js.map +1 -1
  56. package/dist/mcp/types/http.types.d.ts +38 -3
  57. package/dist/mcp/types/http.types.d.ts.map +1 -1
  58. package/dist/mcp/types/http.types.js.map +1 -1
  59. package/dist/mcp/types/paywall.types.d.ts +19 -3
  60. package/dist/mcp/types/paywall.types.d.ts.map +1 -1
  61. package/dist/mcp/types/paywall.types.js.map +1 -1
  62. package/dist/mpp/fetch.d.ts +8 -3
  63. package/dist/mpp/fetch.d.ts.map +1 -1
  64. package/dist/mpp/fetch.js +6 -0
  65. package/dist/mpp/fetch.js.map +1 -1
  66. package/dist/mpp/mpp-api.d.ts +11 -5
  67. package/dist/mpp/mpp-api.d.ts.map +1 -1
  68. package/dist/mpp/mpp-api.js +10 -3
  69. package/dist/mpp/mpp-api.js.map +1 -1
  70. package/dist/payments.d.ts.map +1 -1
  71. package/dist/x402/delegation-api.d.ts +5 -17
  72. package/dist/x402/delegation-api.d.ts.map +1 -1
  73. package/dist/x402/delegation-api.js +2 -2
  74. package/dist/x402/delegation-api.js.map +1 -1
  75. package/dist/x402/express/middleware.d.ts.map +1 -1
  76. package/dist/x402/express/middleware.js +98 -1
  77. package/dist/x402/express/middleware.js.map +1 -1
  78. package/dist/x402/facilitator-api.d.ts +90 -7
  79. package/dist/x402/facilitator-api.d.ts.map +1 -1
  80. package/dist/x402/facilitator-api.js +64 -3
  81. package/dist/x402/facilitator-api.js.map +1 -1
  82. package/dist/x402/index.d.ts +3 -2
  83. package/dist/x402/index.d.ts.map +1 -1
  84. package/dist/x402/index.js +1 -0
  85. package/dist/x402/index.js.map +1 -1
  86. package/dist/x402/token-request.d.ts +8 -0
  87. package/dist/x402/token-request.d.ts.map +1 -1
  88. package/dist/x402/token-request.js +101 -1
  89. package/dist/x402/token-request.js.map +1 -1
  90. package/dist/x402/token-version.d.ts +97 -0
  91. package/dist/x402/token-version.d.ts.map +1 -0
  92. package/dist/x402/token-version.js +127 -0
  93. package/dist/x402/token-version.js.map +1 -0
  94. package/dist/x402/token.d.ts +33 -2
  95. package/dist/x402/token.d.ts.map +1 -1
  96. package/dist/x402/token.js +47 -2
  97. package/dist/x402/token.js.map +1 -1
  98. package/package.json +22 -13
@@ -1 +1 @@
1
- {"version":3,"file":"token-request.js","sourceRoot":"","sources":["../../src/x402/token-request.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAAoB,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAGxE,MAAM,UAAU,yBAAyB,CAAC,MAKzC;IACC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,GAAG,MAAM,CAAA;IACjE,MAAM,MAAM,GAAG,YAAY,EAAE,MAAM,IAAI,aAAa,CAAA;IACpD,MAAM,OAAO,GAAG,YAAY,EAAE,OAAO,IAAI,iBAAiB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAEnF,uFAAuF;IACvF,IAAI,CAAC,YAAY,EAAE,gBAAgB,EAAE,CAAC;QACpC,MAAM,aAAa,CAAC,UAAU,CAC5B,oCAAoC,MAAM,qBAAqB;YAC7D,yEAAyE;YACzE,4DAA4D,CAC/D,CAAA;IACH,CAAC;IAED,0EAA0E;IAC1E,yEAAyE;IACzE,uEAAuE;IACvE,wEAAwE;IACxE,wEAAwE;IACxE,wEAAwE;IACxE,0EAA0E;IAC1E,yEAAyE;IACzE,sCAAsC;IACtC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,YAAY,EAAE,GACvF,YAAY,CAAC,gBAAgB,CAAA;IAC/B,4EAA4E;IAC5E,0EAA0E;IAC1E,8EAA8E;IAC9E,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC7D,MAAM,aAAa,CAAC,UAAU,CAC5B,6DAA6D;YAC3D,iDAAiD,CACpD,CAAA;IACH,CAAC;IACD,MAAM,cAAc,GAClB,CAAC,YAAY;QACb,CAAC,MAAM,KAAK,SAAS;YACnB,uBAAuB,KAAK,SAAS;YACrC,kBAAkB,KAAK,SAAS;YAChC,YAAY,KAAK,SAAS,CAAC,CAAA;IAC/B,IAAI,cAAc,EAAE,CAAC;QACnB,yEAAyE;QACzE,wEAAwE;QACxE,oEAAoE;QACpE,qEAAqE;QACrE,OAAO,CAAC,IAAI,CACV,2EAA2E;YACzE,2EAA2E;YAC3E,2EAA2E;YAC3E,iEAAiE,CACpE,CAAA;IACH,CAAC;IAED,kCAAkC;IAClC,OAAO;QACL,QAAQ,EAAE;YACR,MAAM;YACN,OAAO;YACP,MAAM;YACN,KAAK,EAAE;gBACL,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC;aAC5B;SACF;QACD,sEAAsE;QACtE,gFAAgF;QAChF,gBAAgB,EAAE,YAAY,CAAC,gBAAgB;KAChD,CAAA;AACH,CAAC","sourcesContent":["/**\n * The access-token request body, shared by the x402 and MPP mints.\n *\n * Both routes take identical inputs — only the EIP-712 domain the backend signs\n * under differs — so the body is built in one place to keep them from drifting.\n */\n\nimport { PaymentsError } from '../common/payments.error.js'\nimport { X402TokenOptions, getDefaultNetwork } from '../common/types.js'\nimport type { EnvironmentName } from '../environments.js'\n\nexport function buildX402TokenRequestBody(params: {\n planId: string\n agentId?: string\n tokenOptions?: X402TokenOptions\n environmentName: EnvironmentName\n}): Record<string, any> {\n const { planId, agentId, tokenOptions, environmentName } = params\n const scheme = tokenOptions?.scheme ?? 'nvm:erc4337'\n const network = tokenOptions?.network ?? getDefaultNetwork(scheme, environmentName)\n\n // Validate delegationConfig is provided — the backend requires it for token generation\n if (!tokenOptions?.delegationConfig) {\n throw PaymentsError.validation(\n `delegationConfig is required for ${scheme} token generation. ` +\n 'Create a delegation first with payments.delegation.createDelegation(), ' +\n 'then request the token with delegationConfig.delegationId.',\n )\n }\n\n // Deprecation: the supported flow is create-first — create the delegation\n // with createDelegation(), then request the token with { delegationId }.\n // A delegationConfig that carries an inline-create signal instead of a\n // delegationId triggers inline create-on-the-fly, which the backend has\n // deprecated (auto-select and providerPaymentMethodId/cardId creation).\n // Warn once per call; the { delegationId } (± apiKeyId) path is silent.\n // Predicate mirrors the Python SDK (payments-py#224): no delegationId AND\n // at least one creation field present — a bare/invalid config is left to\n // fail downstream rather than warned.\n const { delegationId, cardId, providerPaymentMethodId, spendingLimitCents, durationSecs } =\n tokenOptions.delegationConfig\n // Reject an explicit empty/blank delegationId early — it is neither a valid\n // reuse id nor an inline-create signal, and forwarding `delegationId: ''`\n // would 4xx at the backend. (Symmetric with the Python SDK, payments-py#225.)\n if (delegationId !== undefined && delegationId.trim() === '') {\n throw PaymentsError.validation(\n 'delegationConfig.delegationId must not be an empty string. ' +\n 'Pass a valid delegation UUID or omit the field.',\n )\n }\n const isInlineCreate =\n !delegationId &&\n (cardId !== undefined ||\n providerPaymentMethodId !== undefined ||\n spendingLimitCents !== undefined ||\n durationSecs !== undefined)\n if (isInlineCreate) {\n // Neutral wording: this body builder is shared by getX402AccessToken and\n // the MPP mint (payments.mpp.fetch / getMppAccessToken), so the message\n // must not name one caller specifically — an MPP buyer grepping for\n // \"getX402AccessToken\" after seeing this warning would find nothing.\n console.warn(\n '[DEPRECATED] delegationConfig: inline create-on-the-fly delegationConfig ' +\n '(no delegationId) is deprecated and will be removed in a future release. ' +\n 'Create the delegation first with payments.delegation.createDelegation(), ' +\n 'then request the token with delegationConfig: { delegationId }.',\n )\n }\n\n // Build x402-aligned request body\n return {\n accepted: {\n scheme,\n network,\n planId,\n extra: {\n ...(agentId && { agentId }),\n },\n },\n // Add delegation config for both erc4337 and card-delegation schemes.\n // delegationConfig is guaranteed present here (the absence check above throws).\n delegationConfig: tokenOptions.delegationConfig,\n }\n}\n"]}
1
+ {"version":3,"file":"token-request.js","sourceRoot":"","sources":["../../src/x402/token-request.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The access-token request body, shared by the x402 and MPP mints.\n *\n * Both routes take identical inputs — only the EIP-712 domain the backend signs\n * under differs — so the body is built in one place to keep them from drifting.\n */\n\nimport { PaymentsError } from '../common/payments.error.js'\nimport { X402TokenOptions, getDefaultNetwork } from '../common/types.js'\nimport type { EnvironmentName } from '../environments.js'\n\nexport function buildX402TokenRequestBody(params: {\n planId: string\n agentId?: string\n tokenOptions?: X402TokenOptions\n environmentName: EnvironmentName\n /**\n * Which mint this body is for. MPP has no token version at all — it is not\n * \"x402 v2 by another name\": the two protocols stopped sharing a version\n * ladder (nvm-monorepo#3266), and `MppService.createPermission` refuses ANY\n * `tokenVersion` with `BCK.MPP.0007`, `2` included. Sending one would 400 the\n * mint, so this builder refuses it here, where the caller can be told why.\n */\n protocol?: 'x402' | 'mpp'\n}): Record<string, any> {\n const { planId, agentId, tokenOptions, environmentName, protocol = 'x402' } = params\n const scheme = tokenOptions?.scheme ?? 'nvm:erc4337'\n const network = tokenOptions?.network ?? getDefaultNetwork(scheme, environmentName)\n\n // Validate delegationConfig is provided — the backend requires it for token generation\n if (!tokenOptions?.delegationConfig) {\n throw PaymentsError.validation(\n `delegationConfig is required for ${scheme} token generation. ` +\n 'Create a delegation first with payments.delegation.createDelegation(), ' +\n 'then request the token with delegationConfig.delegationId.',\n )\n }\n\n // Deprecation: the supported flow is create-first — create the delegation\n // with createDelegation(), then request the token with { delegationId }.\n // A delegationConfig that carries an inline-create signal instead of a\n // delegationId triggers inline create-on-the-fly, which the backend has\n // deprecated (auto-select and providerPaymentMethodId/cardId creation).\n // Warn once per call; the { delegationId } (± apiKeyId) path is silent.\n // Predicate mirrors the Python SDK (payments-py#224): no delegationId AND\n // at least one creation field present — a bare/invalid config is left to\n // fail downstream rather than warned.\n const { delegationId, cardId, providerPaymentMethodId, spendingLimitCents, durationSecs } =\n tokenOptions.delegationConfig\n // Reject an explicit empty/blank delegationId early — it is neither a valid\n // reuse id nor an inline-create signal, and forwarding `delegationId: ''`\n // would 4xx at the backend. (Symmetric with the Python SDK, payments-py#225.)\n if (delegationId !== undefined && delegationId.trim() === '') {\n throw PaymentsError.validation(\n 'delegationConfig.delegationId must not be an empty string. ' +\n 'Pass a valid delegation UUID or omit the field.',\n )\n }\n const isInlineCreate =\n !delegationId &&\n (cardId !== undefined ||\n providerPaymentMethodId !== undefined ||\n spendingLimitCents !== undefined ||\n durationSecs !== undefined)\n if (isInlineCreate) {\n // Neutral wording: this body builder is shared by getX402AccessToken and\n // the MPP mint (payments.mpp.fetch / getMppAccessToken), so the message\n // must not name one caller specifically — an MPP buyer grepping for\n // \"getX402AccessToken\" after seeing this warning would find nothing.\n console.warn(\n '[DEPRECATED] delegationConfig: inline create-on-the-fly delegationConfig ' +\n '(no delegationId) is deprecated and will be removed in a future release. ' +\n 'Create the delegation first with payments.delegation.createDelegation(), ' +\n 'then request the token with delegationConfig: { delegationId }.',\n )\n }\n\n // `resource` and `httpVerb` are what a v3 token is bound to: they go inside\n // the EIP-712 signature next to `agentId` and the one-time nonce, so a v3\n // token minted for one seller endpoint cannot be presented to another.\n //\n // On a v1/v2 token they are outside the signature — but NOT inert. The\n // backend compares a token's `resource.url` against the seller's\n // `paymentRequired.resource.url` (origin + path, exact string when it cannot\n // parse either as a URL) for ANY token that carries one, so a v2 token bound\n // to a URL the seller does not advertise verbatim fails verification with\n // `BCK.X402.0013`. Measured against staging, not inferred.\n //\n // They are still forwarded when the caller supplies them — this is the\n // low-level builder, and a caller that names a resource means it (binding a\n // v2 token to the URL the seller really advertises is valid, and once the\n // backend default flips to v3 the binding is needed without anyone asking for\n // a version). What the builder will not do is let that happen silently: see\n // the warning below.\n //\n // Omitted rather than sent empty when the caller did not supply them: a field\n // absent at mint is signed as the empty string AND must stay absent from the\n // unsigned envelope, so sending `{ url: '' }` and sending nothing must not\n // diverge — which is why the guard below tests `resource?.url`, not the\n // object.\n const { resource, tokenVersion } = tokenOptions\n // Normalized here, once, rather than in each consumer: sellers advertise\n // `req.method`, i.e. upper case, and `httpVerb: 'post'` mints a token that\n // can never match. Both in-tree consumers already upper-cased it themselves;\n // a direct SDK caller had no such defence.\n const httpVerb = tokenOptions.httpVerb?.toUpperCase()\n const boundUrl = resource?.url\n\n // A binding on a token that is not v3 is legal but rarely intended, and its\n // failure mode (verify rejecting with BCK.X402.0013 at the seller) points\n // nowhere near this call. Say so once, here, where the cause is visible.\n //\n // MPP gets the same warning for the same reason: the MPP mint accepts\n // `resource`/`httpVerb` (its DTO is `OmitType(GenerateX402TokenDto,\n // ['tokenVersion'])`, so only the version is refused) and forwards them into\n // the same comparison — but MPP has no v3 to opt into, so a binding there is\n // never signed and can only narrow what the credential verifies against.\n if (boundUrl || httpVerb) {\n // A binding on anything but a v3 mint has no good outcome, so it is refused\n // rather than warned about — the same answer payments-py gives\n // (`token_request.py`). The presence of `resource.url` is what arms the\n // backend's endpoint allowlist (`erc4337-scheme.handler.ts`; without it the\n // backend logs \"resource.url not provided in token … skipping endpoint\n // validation\"), while a v2 signature binds nothing — so the caller either\n // arms a check they did not configure (`BCK.PROTOCOL.0031` for an agent\n // registered with `endpoints`) or fails verification later with\n // `BCK.X402.0013`. Neither is worth a log line nobody reads at mint time.\n //\n // This covers an OMITTED `tokenVersion` as well as an explicit `2`, because\n // today they mint the same thing: `core-kit`'s minter is\n // `(options.tokenVersion ?? 2) === 3`, the handler adds no default, and no\n // versioning gate fills the field in — verified against a live mint, which\n // comes back v2. Once nvm-monorepo#3292 makes v3 the default (it is open at\n // the time of writing), the omitted case becomes the shape the backend is\n // moving to and this guard should narrow to `tokenVersion === 2`.\n if (protocol === 'x402' && tokenVersion !== 3) {\n throw PaymentsError.validation(\n `resource/httpVerb were supplied with tokenVersion ${tokenVersion ?? '(omitted, defaults to 2)'}. ` +\n 'Only a v3 token signs them, so the binding cannot hold — but the backend still ' +\n 'compares the resource against what the seller advertises, and arms its endpoint ' +\n 'allowlist on it. Pass tokenVersion: 3 to bind the token, or drop resource/httpVerb.',\n )\n }\n if (protocol === 'mpp') {\n // MPP is warned, not refused: it has no v3 to opt into, so there is no\n // \"correct\" version to point the caller at, and the binding still narrows\n // what the credential verifies against rather than arming a signature\n // that cannot hold.\n console.warn(\n '[mpp] resource/httpVerb were supplied on an MPP mint. MPP tokens carry no version and ' +\n 'nothing is signed over them, but the backend still compares the resource against what ' +\n 'the seller advertises — a mismatch fails the credential. Omit them unless the string ' +\n 'matches the seller exactly.',\n )\n }\n }\n\n // MPP's single-use unit is the CHALLENGE, not the token: one MPP access token\n // is presented across many challenges by design, so the x402 v3 per-token\n // nonce would kill every buyer's second challenge. The backend enforces this\n // by refusing the field outright; refuse it here too rather than let the\n // caller discover it as a 400 whose cause is a field they set two layers up.\n if (protocol === 'mpp' && tokenVersion !== undefined) {\n throw PaymentsError.validation(\n 'tokenVersion is not supported on MPP access tokens: MPP and x402 no longer share a ' +\n 'token version ladder, and the backend refuses any tokenVersion on an MPP mint ' +\n '(BCK.MPP.0007). Omit the field — an MPP token is reusable across challenges, and the ' +\n 'challenge is what is single-use.',\n )\n }\n\n // Build x402-aligned request body\n return {\n ...(boundUrl && { resource }),\n accepted: {\n scheme,\n network,\n planId,\n extra: {\n ...(agentId && { agentId }),\n ...(httpVerb && { httpVerb }),\n },\n },\n // Add delegation config for both erc4337 and card-delegation schemes.\n // delegationConfig is guaranteed present here (the absence check above throws).\n delegationConfig: tokenOptions.delegationConfig,\n // Opt-in only, and x402-only (the MPP guard above has already thrown).\n // Left out entirely when unset so a v2 mint stays byte-identical to what it\n // was before v3 existed. A backend that predates the v3 struct drops this\n // field silently (ValidationPipe whitelists without forbidNonWhitelisted)\n // and returns v2 — which is why no caller may infer the version from what\n // it asked for. See detectAccessTokenVersion().\n //\n // `tokenVersion: 3` WITHOUT a resource is deliberately allowed, not an\n // oversight: the backend signs an absent binding member as the empty\n // string, so the result is a single-use token bound to nothing. That is a\n // supported mode — single-use without seller binding — and the only one\n // available when the caller does not know the seller's advertised URL.\n ...(tokenVersion !== undefined && { tokenVersion }),\n }\n}\n"]}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Detecting the EIP-712 version of a minted x402 access token.
3
+ *
4
+ * The version is a property of the token you were handed, never of the one you
5
+ * asked for. The backend's `ValidationPipe` runs with `whitelist: true` and
6
+ * without `forbidNonWhitelisted`, so a `tokenVersion: 3` sent to a deployment
7
+ * that predates the v3 struct is dropped **without an error** and a v2 token
8
+ * comes back. Branching on the requested version would then silently treat a
9
+ * reusable v2 token as single-use (harmless) or — far worse, once the default
10
+ * flips — a single-use v3 token as reusable, replaying a spent nonce on every
11
+ * call after the first.
12
+ *
13
+ * The discriminator is `payload.authorization.nonce`: v3 is the first version
14
+ * to carry one, and it is exactly what makes the token single-use.
15
+ */
16
+ import type { X402TokenVersion } from '../common/types.js';
17
+ /**
18
+ * The EIP-712 version the given access token was actually signed under.
19
+ *
20
+ * `3` when `payload.authorization.nonce` is a non-empty string, `2` otherwise.
21
+ *
22
+ * A token that cannot be decoded also reads as `2`. That is the conservative
23
+ * answer for a *version* question — v2 is what every pre-v3 token and every
24
+ * non-Nevermined blob already is — but note it is the permissive answer for the
25
+ * *reuse* question a caller asks next: `2` means "safe to cache and replay".
26
+ * The gap is unreachable today, since an undecodable token is unusable anyway
27
+ * (the backend rejects it on the first presentation, so there is nothing to
28
+ * replay), but a future change to the envelope format would turn every v3 token
29
+ * into a cached one here. Any such change must revisit this function first.
30
+ *
31
+ * Note this is deliberately NOT the same as trusting the token: nothing here
32
+ * is verified. It answers "which settle semantics does this token have", which
33
+ * only the backend's signature check ultimately enforces.
34
+ *
35
+ * @param accessToken - The base64-encoded x402 access token
36
+ * @returns `3` for a single-use, seller/resource-bound token, else `2`
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId, {
41
+ * delegationConfig: { delegationId },
42
+ * // The string the seller advertises, not the URL you fetch.
43
+ * resource: { url: '/api/v1/tasks' },
44
+ * httpVerb: 'POST',
45
+ * tokenVersion: 3,
46
+ * })
47
+ * if (detectAccessTokenVersion(accessToken) === 3) {
48
+ * // Single-use: mint a new one for the next paid request.
49
+ * }
50
+ * ```
51
+ */
52
+ export declare function detectAccessTokenVersion(accessToken: string): X402TokenVersion;
53
+ /**
54
+ * Whether the token is consumed by its first successful settlement.
55
+ *
56
+ * True exactly for v3 tokens. A caller that caches an access token must test
57
+ * this before reusing one: settling a spent v3 token fails with
58
+ * `BCK.X402.0059` (see {@link isAccessTokenAlreadyUsed}).
59
+ *
60
+ * @param accessToken - The base64-encoded x402 access token
61
+ */
62
+ export declare function isSingleUseAccessToken(accessToken: string): boolean;
63
+ /**
64
+ * Backend code for "this access token was already used".
65
+ *
66
+ * Returned by `POST /x402/settle` when a v3 token is settled a second time.
67
+ * Deliberately not forgery-gated on the backend: it says plainly that the
68
+ * token was spent, so a client can tell a **spent** token apart from a
69
+ * declined one (insufficient credits, wrong plan) and from a **forged** one
70
+ * (`BCK.X402.0005`, an envelope edited to disagree with the signature).
71
+ */
72
+ export declare const X402_TOKEN_ALREADY_USED_CODE = "BCK.X402.0059";
73
+ /**
74
+ * Whether an error is the backend refusing a second settlement of the same
75
+ * single-use token.
76
+ *
77
+ * The one correct response is to **mint a new token** — a retry that replays
78
+ * the same token fails identically forever. Never treat this as a transient
79
+ * settlement failure.
80
+ *
81
+ * @param error - Any thrown value; typically a `PaymentsError` from
82
+ * `payments.facilitator.settlePermissions`
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * try {
87
+ * await payments.facilitator.settlePermissions({ paymentRequired, x402AccessToken })
88
+ * } catch (error) {
89
+ * if (isAccessTokenAlreadyUsed(error)) {
90
+ * // Re-mint, do not replay.
91
+ * }
92
+ * throw error
93
+ * }
94
+ * ```
95
+ */
96
+ export declare function isAccessTokenAlreadyUsed(error: unknown): boolean;
97
+ //# sourceMappingURL=token-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-version.d.ts","sourceRoot":"","sources":["../../src/x402/token-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAgB1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAU9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,kBAAkB,CAAA;AAE3D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGhE"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Detecting the EIP-712 version of a minted x402 access token.
3
+ *
4
+ * The version is a property of the token you were handed, never of the one you
5
+ * asked for. The backend's `ValidationPipe` runs with `whitelist: true` and
6
+ * without `forbidNonWhitelisted`, so a `tokenVersion: 3` sent to a deployment
7
+ * that predates the v3 struct is dropped **without an error** and a v2 token
8
+ * comes back. Branching on the requested version would then silently treat a
9
+ * reusable v2 token as single-use (harmless) or — far worse, once the default
10
+ * flips — a single-use v3 token as reusable, replaying a spent nonce on every
11
+ * call after the first.
12
+ *
13
+ * The discriminator is `payload.authorization.nonce`: v3 is the first version
14
+ * to carry one, and it is exactly what makes the token single-use.
15
+ */
16
+ import { decodeAccessToken } from '../utils.js';
17
+ /**
18
+ * Read the `authorization` object out of a base64 x402 access token.
19
+ *
20
+ * Returns `undefined` for anything that is not a decodable token with that
21
+ * shape, so callers can treat "not a Nevermined token" and "no nonce" the same
22
+ * way: as not-v3.
23
+ */
24
+ function readAuthorization(accessToken) {
25
+ if (typeof accessToken !== 'string' || accessToken.length === 0)
26
+ return undefined;
27
+ const decoded = decodeAccessToken(accessToken);
28
+ const authorization = decoded?.payload?.authorization;
29
+ return typeof authorization === 'object' && authorization !== null ? authorization : undefined;
30
+ }
31
+ /**
32
+ * The EIP-712 version the given access token was actually signed under.
33
+ *
34
+ * `3` when `payload.authorization.nonce` is a non-empty string, `2` otherwise.
35
+ *
36
+ * A token that cannot be decoded also reads as `2`. That is the conservative
37
+ * answer for a *version* question — v2 is what every pre-v3 token and every
38
+ * non-Nevermined blob already is — but note it is the permissive answer for the
39
+ * *reuse* question a caller asks next: `2` means "safe to cache and replay".
40
+ * The gap is unreachable today, since an undecodable token is unusable anyway
41
+ * (the backend rejects it on the first presentation, so there is nothing to
42
+ * replay), but a future change to the envelope format would turn every v3 token
43
+ * into a cached one here. Any such change must revisit this function first.
44
+ *
45
+ * Note this is deliberately NOT the same as trusting the token: nothing here
46
+ * is verified. It answers "which settle semantics does this token have", which
47
+ * only the backend's signature check ultimately enforces.
48
+ *
49
+ * @param accessToken - The base64-encoded x402 access token
50
+ * @returns `3` for a single-use, seller/resource-bound token, else `2`
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId, {
55
+ * delegationConfig: { delegationId },
56
+ * // The string the seller advertises, not the URL you fetch.
57
+ * resource: { url: '/api/v1/tasks' },
58
+ * httpVerb: 'POST',
59
+ * tokenVersion: 3,
60
+ * })
61
+ * if (detectAccessTokenVersion(accessToken) === 3) {
62
+ * // Single-use: mint a new one for the next paid request.
63
+ * }
64
+ * ```
65
+ */
66
+ export function detectAccessTokenVersion(accessToken) {
67
+ const nonce = readAuthorization(accessToken)?.nonce;
68
+ // The string check mirrors the backend's own discriminator verbatim
69
+ // (`AgentX402AccessToken.ts`: `typeof authorization?.nonce === 'string' &&
70
+ // authorization.nonce.trim() !== ''`), and `generateTokenNonce()` emits 32
71
+ // bytes of CSPRNG output hex-encoded — a string on the wire, always.
72
+ // Accepting a non-string nonce as v3 would make this SDK disagree with the
73
+ // server about what a v3 token is, which is worse than either error it could
74
+ // prevent.
75
+ return typeof nonce === 'string' && nonce.trim() !== '' ? 3 : 2;
76
+ }
77
+ /**
78
+ * Whether the token is consumed by its first successful settlement.
79
+ *
80
+ * True exactly for v3 tokens. A caller that caches an access token must test
81
+ * this before reusing one: settling a spent v3 token fails with
82
+ * `BCK.X402.0059` (see {@link isAccessTokenAlreadyUsed}).
83
+ *
84
+ * @param accessToken - The base64-encoded x402 access token
85
+ */
86
+ export function isSingleUseAccessToken(accessToken) {
87
+ return detectAccessTokenVersion(accessToken) === 3;
88
+ }
89
+ /**
90
+ * Backend code for "this access token was already used".
91
+ *
92
+ * Returned by `POST /x402/settle` when a v3 token is settled a second time.
93
+ * Deliberately not forgery-gated on the backend: it says plainly that the
94
+ * token was spent, so a client can tell a **spent** token apart from a
95
+ * declined one (insufficient credits, wrong plan) and from a **forged** one
96
+ * (`BCK.X402.0005`, an envelope edited to disagree with the signature).
97
+ */
98
+ export const X402_TOKEN_ALREADY_USED_CODE = 'BCK.X402.0059';
99
+ /**
100
+ * Whether an error is the backend refusing a second settlement of the same
101
+ * single-use token.
102
+ *
103
+ * The one correct response is to **mint a new token** — a retry that replays
104
+ * the same token fails identically forever. Never treat this as a transient
105
+ * settlement failure.
106
+ *
107
+ * @param error - Any thrown value; typically a `PaymentsError` from
108
+ * `payments.facilitator.settlePermissions`
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * try {
113
+ * await payments.facilitator.settlePermissions({ paymentRequired, x402AccessToken })
114
+ * } catch (error) {
115
+ * if (isAccessTokenAlreadyUsed(error)) {
116
+ * // Re-mint, do not replay.
117
+ * }
118
+ * throw error
119
+ * }
120
+ * ```
121
+ */
122
+ export function isAccessTokenAlreadyUsed(error) {
123
+ if (typeof error !== 'object' || error === null)
124
+ return false;
125
+ return error.code === X402_TOKEN_ALREADY_USED_CODE;
126
+ }
127
+ //# sourceMappingURL=token-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-version.js","sourceRoot":"","sources":["../../src/x402/token-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAG/C;;;;;;GAMG;AACH,SAAS,iBAAiB,CAAC,WAAmB;IAC5C,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IACjF,MAAM,OAAO,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAA;IAC9C,MAAM,aAAa,GAAG,OAAO,EAAE,OAAO,EAAE,aAAa,CAAA;IACrD,OAAO,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAA;AAChG,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,wBAAwB,CAAC,WAAmB;IAC1D,MAAM,KAAK,GAAG,iBAAiB,CAAC,WAAW,CAAC,EAAE,KAAK,CAAA;IACnD,oEAAoE;IACpE,2EAA2E;IAC3E,2EAA2E;IAC3E,qEAAqE;IACrE,2EAA2E;IAC3E,6EAA6E;IAC7E,WAAW;IACX,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACjE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,WAAmB;IACxD,OAAO,wBAAwB,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;AACpD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,eAAe,CAAA;AAE3D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAC7D,OAAQ,KAA4B,CAAC,IAAI,KAAK,4BAA4B,CAAA;AAC5E,CAAC","sourcesContent":["/**\n * Detecting the EIP-712 version of a minted x402 access token.\n *\n * The version is a property of the token you were handed, never of the one you\n * asked for. The backend's `ValidationPipe` runs with `whitelist: true` and\n * without `forbidNonWhitelisted`, so a `tokenVersion: 3` sent to a deployment\n * that predates the v3 struct is dropped **without an error** and a v2 token\n * comes back. Branching on the requested version would then silently treat a\n * reusable v2 token as single-use (harmless) or — far worse, once the default\n * flips — a single-use v3 token as reusable, replaying a spent nonce on every\n * call after the first.\n *\n * The discriminator is `payload.authorization.nonce`: v3 is the first version\n * to carry one, and it is exactly what makes the token single-use.\n */\n\nimport { decodeAccessToken } from '../utils.js'\nimport type { X402TokenVersion } from '../common/types.js'\n\n/**\n * Read the `authorization` object out of a base64 x402 access token.\n *\n * Returns `undefined` for anything that is not a decodable token with that\n * shape, so callers can treat \"not a Nevermined token\" and \"no nonce\" the same\n * way: as not-v3.\n */\nfunction readAuthorization(accessToken: string): Record<string, any> | undefined {\n if (typeof accessToken !== 'string' || accessToken.length === 0) return undefined\n const decoded = decodeAccessToken(accessToken)\n const authorization = decoded?.payload?.authorization\n return typeof authorization === 'object' && authorization !== null ? authorization : undefined\n}\n\n/**\n * The EIP-712 version the given access token was actually signed under.\n *\n * `3` when `payload.authorization.nonce` is a non-empty string, `2` otherwise.\n *\n * A token that cannot be decoded also reads as `2`. That is the conservative\n * answer for a *version* question — v2 is what every pre-v3 token and every\n * non-Nevermined blob already is — but note it is the permissive answer for the\n * *reuse* question a caller asks next: `2` means \"safe to cache and replay\".\n * The gap is unreachable today, since an undecodable token is unusable anyway\n * (the backend rejects it on the first presentation, so there is nothing to\n * replay), but a future change to the envelope format would turn every v3 token\n * into a cached one here. Any such change must revisit this function first.\n *\n * Note this is deliberately NOT the same as trusting the token: nothing here\n * is verified. It answers \"which settle semantics does this token have\", which\n * only the backend's signature check ultimately enforces.\n *\n * @param accessToken - The base64-encoded x402 access token\n * @returns `3` for a single-use, seller/resource-bound token, else `2`\n *\n * @example\n * ```typescript\n * const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId, {\n * delegationConfig: { delegationId },\n * // The string the seller advertises, not the URL you fetch.\n * resource: { url: '/api/v1/tasks' },\n * httpVerb: 'POST',\n * tokenVersion: 3,\n * })\n * if (detectAccessTokenVersion(accessToken) === 3) {\n * // Single-use: mint a new one for the next paid request.\n * }\n * ```\n */\nexport function detectAccessTokenVersion(accessToken: string): X402TokenVersion {\n const nonce = readAuthorization(accessToken)?.nonce\n // The string check mirrors the backend's own discriminator verbatim\n // (`AgentX402AccessToken.ts`: `typeof authorization?.nonce === 'string' &&\n // authorization.nonce.trim() !== ''`), and `generateTokenNonce()` emits 32\n // bytes of CSPRNG output hex-encoded — a string on the wire, always.\n // Accepting a non-string nonce as v3 would make this SDK disagree with the\n // server about what a v3 token is, which is worse than either error it could\n // prevent.\n return typeof nonce === 'string' && nonce.trim() !== '' ? 3 : 2\n}\n\n/**\n * Whether the token is consumed by its first successful settlement.\n *\n * True exactly for v3 tokens. A caller that caches an access token must test\n * this before reusing one: settling a spent v3 token fails with\n * `BCK.X402.0059` (see {@link isAccessTokenAlreadyUsed}).\n *\n * @param accessToken - The base64-encoded x402 access token\n */\nexport function isSingleUseAccessToken(accessToken: string): boolean {\n return detectAccessTokenVersion(accessToken) === 3\n}\n\n/**\n * Backend code for \"this access token was already used\".\n *\n * Returned by `POST /x402/settle` when a v3 token is settled a second time.\n * Deliberately not forgery-gated on the backend: it says plainly that the\n * token was spent, so a client can tell a **spent** token apart from a\n * declined one (insufficient credits, wrong plan) and from a **forged** one\n * (`BCK.X402.0005`, an envelope edited to disagree with the signature).\n */\nexport const X402_TOKEN_ALREADY_USED_CODE = 'BCK.X402.0059'\n\n/**\n * Whether an error is the backend refusing a second settlement of the same\n * single-use token.\n *\n * The one correct response is to **mint a new token** — a retry that replays\n * the same token fails identically forever. Never treat this as a transient\n * settlement failure.\n *\n * @param error - Any thrown value; typically a `PaymentsError` from\n * `payments.facilitator.settlePermissions`\n *\n * @example\n * ```typescript\n * try {\n * await payments.facilitator.settlePermissions({ paymentRequired, x402AccessToken })\n * } catch (error) {\n * if (isAccessTokenAlreadyUsed(error)) {\n * // Re-mint, do not replay.\n * }\n * throw error\n * }\n * ```\n */\nexport function isAccessTokenAlreadyUsed(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) return false\n return (error as { code?: unknown }).code === X402_TOKEN_ALREADY_USED_CODE\n}\n"]}
@@ -5,7 +5,7 @@
5
5
  * Tokens are used to authorize payment verification and settlement.
6
6
  */
7
7
  import { BasePaymentsAPI } from '../api/base-payments.js';
8
- import { PaymentOptions, X402TokenOptions } from '../common/types.js';
8
+ import { PaymentOptions, X402TokenOptions, X402TokenVersion } from '../common/types.js';
9
9
  /**
10
10
  * X402 Token API for generating access tokens.
11
11
  *
@@ -32,11 +32,20 @@ export declare class X402TokenAPI extends BasePaymentsAPI {
32
32
  * (spending limits / `providerPaymentMethodId` / `cardId`, i.e. no
33
33
  * `delegationId`) is **deprecated** and emits a runtime warning.
34
34
  *
35
+ * Pass `resource` / `httpVerb` to bind the token to one seller endpoint, and
36
+ * `tokenVersion: 3` to ask for the single-use, seller/resource-bound token.
37
+ * v3 is opt-in and the request may be silently downgraded by a backend that
38
+ * predates it, so the returned `tokenVersion` is read off the minted token
39
+ * rather than echoed back from the request — branch on that, never on what
40
+ * you asked for.
41
+ *
35
42
  * @param planId - The unique identifier of the payment plan
36
43
  * @param agentId - The unique identifier of the AI agent (optional)
37
- * @param tokenOptions - Options controlling scheme and delegation behavior (optional)
44
+ * @param tokenOptions - Options controlling scheme, resource binding, token version and delegation behavior (optional)
38
45
  * @returns A promise that resolves to an object containing:
39
46
  * - accessToken: The X402 access token string
47
+ * - tokenVersion: The EIP-712 version the returned token was actually signed
48
+ * under (`3` means single-use — mint a new token per paid request)
40
49
  *
41
50
  * @throws PaymentsError if the request fails
42
51
  *
@@ -53,9 +62,31 @@ export declare class X402TokenAPI extends BasePaymentsAPI {
53
62
  * delegationConfig: { delegationId },
54
63
  * })
55
64
  * ```
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * // Single-use, bound to one seller endpoint. Mint one per paid request.
69
+ * // `resource.url` must be the string the seller advertises in its 402
70
+ * // `paymentRequired` — sellers built on this SDK's `paymentMiddleware`
71
+ * // advertise `req.originalUrl`, i.e. a relative path.
72
+ * const { accessToken, tokenVersion } = await payments.x402.getX402AccessToken(
73
+ * planId,
74
+ * agentId,
75
+ * {
76
+ * delegationConfig: { delegationId },
77
+ * resource: { url: '/api/v1/tasks' },
78
+ * httpVerb: 'POST',
79
+ * tokenVersion: 3,
80
+ * },
81
+ * )
82
+ * if (tokenVersion === 2) {
83
+ * // The backend does not support v3 yet; this token is still reusable.
84
+ * }
85
+ * ```
56
86
  */
57
87
  getX402AccessToken(planId: string, agentId?: string, tokenOptions?: X402TokenOptions): Promise<{
58
88
  accessToken: string;
89
+ tokenVersion: X402TokenVersion;
59
90
  [key: string]: any;
60
91
  }>;
61
92
  }
@@ -1 +1 @@
1
- {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/x402/token.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAGzD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAGrE;;;;;GAKG;AACH,qBAAa,YAAa,SAAQ,eAAe;IAC/C;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,YAAY;IAIzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACG,kBAAkB,CACtB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,gBAAgB,GAC9B,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;CAmCxD"}
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/x402/token.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAGzD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAIvF;;;;;GAKG;AACH,qBAAa,YAAa,SAAQ,eAAe;IAC/C;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,YAAY;IAIzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+DG;IACG,kBAAkB,CACtB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,gBAAgB,GAC9B,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,gBAAgB,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;CAmDxF"}
@@ -8,6 +8,7 @@ import { BasePaymentsAPI } from '../api/base-payments.js';
8
8
  import { API_URL_CREATE_PERMISSION } from '../api/nvm-api.js';
9
9
  import { PaymentsError } from '../common/payments.error.js';
10
10
  import { buildX402TokenRequestBody } from './token-request.js';
11
+ import { detectAccessTokenVersion } from './token-version.js';
11
12
  /**
12
13
  * X402 Token API for generating access tokens.
13
14
  *
@@ -36,11 +37,20 @@ export class X402TokenAPI extends BasePaymentsAPI {
36
37
  * (spending limits / `providerPaymentMethodId` / `cardId`, i.e. no
37
38
  * `delegationId`) is **deprecated** and emits a runtime warning.
38
39
  *
40
+ * Pass `resource` / `httpVerb` to bind the token to one seller endpoint, and
41
+ * `tokenVersion: 3` to ask for the single-use, seller/resource-bound token.
42
+ * v3 is opt-in and the request may be silently downgraded by a backend that
43
+ * predates it, so the returned `tokenVersion` is read off the minted token
44
+ * rather than echoed back from the request — branch on that, never on what
45
+ * you asked for.
46
+ *
39
47
  * @param planId - The unique identifier of the payment plan
40
48
  * @param agentId - The unique identifier of the AI agent (optional)
41
- * @param tokenOptions - Options controlling scheme and delegation behavior (optional)
49
+ * @param tokenOptions - Options controlling scheme, resource binding, token version and delegation behavior (optional)
42
50
  * @returns A promise that resolves to an object containing:
43
51
  * - accessToken: The X402 access token string
52
+ * - tokenVersion: The EIP-712 version the returned token was actually signed
53
+ * under (`3` means single-use — mint a new token per paid request)
44
54
  *
45
55
  * @throws PaymentsError if the request fails
46
56
  *
@@ -57,6 +67,27 @@ export class X402TokenAPI extends BasePaymentsAPI {
57
67
  * delegationConfig: { delegationId },
58
68
  * })
59
69
  * ```
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * // Single-use, bound to one seller endpoint. Mint one per paid request.
74
+ * // `resource.url` must be the string the seller advertises in its 402
75
+ * // `paymentRequired` — sellers built on this SDK's `paymentMiddleware`
76
+ * // advertise `req.originalUrl`, i.e. a relative path.
77
+ * const { accessToken, tokenVersion } = await payments.x402.getX402AccessToken(
78
+ * planId,
79
+ * agentId,
80
+ * {
81
+ * delegationConfig: { delegationId },
82
+ * resource: { url: '/api/v1/tasks' },
83
+ * httpVerb: 'POST',
84
+ * tokenVersion: 3,
85
+ * },
86
+ * )
87
+ * if (tokenVersion === 2) {
88
+ * // The backend does not support v3 yet; this token is still reusable.
89
+ * }
90
+ * ```
60
91
  */
61
92
  async getX402AccessToken(planId, agentId, tokenOptions) {
62
93
  const urlPath = API_URL_CREATE_PERMISSION;
@@ -81,7 +112,21 @@ export class X402TokenAPI extends BasePaymentsAPI {
81
112
  }
82
113
  throw PaymentsError.internal(`${errorMessage} (HTTP ${response.status})`);
83
114
  }
84
- return await response.json();
115
+ const result = await response.json();
116
+ // A 2xx with no usable token is a broken response, not a token: without
117
+ // this guard the caller gets `{ accessToken: undefined }` typed as
118
+ // `string`, sends `undefined` as the `payment-signature`, and the failure
119
+ // surfaces at the seller as an unparseable token — three layers from the
120
+ // cause.
121
+ if (typeof result?.accessToken !== 'string' || result.accessToken.length === 0) {
122
+ throw PaymentsError.internal('X402 token request succeeded but the response carried no accessToken');
123
+ }
124
+ // Detected, never echoed: `tokenVersion: 3` is dropped without an error by
125
+ // a backend that predates the v3 struct, so the only trustworthy source of
126
+ // the version is the token itself. The override after the spread is
127
+ // deliberate — a `tokenVersion` echoed by the backend is discarded in
128
+ // favour of the one read off the token.
129
+ return { ...result, tokenVersion: detectAccessTokenVersion(result.accessToken) };
85
130
  }
86
131
  catch (error) {
87
132
  if (error instanceof PaymentsError) {
@@ -1 +1 @@
1
- {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/x402/token.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAE3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAA;AAE9D;;;;;GAKG;AACH,MAAM,OAAO,YAAa,SAAQ,eAAe;IAC/C;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,OAAuB;QACxC,OAAO,IAAI,YAAY,CAAC,OAAO,CAAC,CAAA;IAClC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,KAAK,CAAC,kBAAkB,CACtB,MAAc,EACd,OAAgB,EAChB,YAA+B;QAE/B,MAAM,OAAO,GAAG,yBAAyB,CAAA;QACzC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;QAEtD,MAAM,IAAI,GAAG,yBAAyB,CAAC;YACrC,MAAM;YACN,OAAO;YACP,YAAY;YACZ,eAAe,EAAE,IAAI,CAAC,eAAe;SACtC,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QAExD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;YAC1C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,IAAI,YAAY,GAAG,wCAAwC,CAAA;gBAC3D,IAAI,CAAC;oBACH,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;oBACvC,YAAY,GAAG,SAAS,CAAC,OAAO,IAAI,YAAY,CAAA;gBAClD,CAAC;gBAAC,MAAM,CAAC;oBACP,4BAA4B;gBAC9B,CAAC;gBACD,MAAM,aAAa,CAAC,QAAQ,CAAC,GAAG,YAAY,UAAU,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;YAC3E,CAAC;YACD,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;gBACnC,MAAM,KAAK,CAAA;YACb,CAAC;YACD,MAAM,aAAa,CAAC,QAAQ,CAC1B,uDAAuD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAChH,CAAA;QACH,CAAC;IACH,CAAC;CACF","sourcesContent":["/**\n * X402 Token Generation API.\n *\n * Provides X402 access token generation functionality for subscribers.\n * Tokens are used to authorize payment verification and settlement.\n */\n\nimport { BasePaymentsAPI } from '../api/base-payments.js'\nimport { API_URL_CREATE_PERMISSION } from '../api/nvm-api.js'\nimport { PaymentsError } from '../common/payments.error.js'\nimport { PaymentOptions, X402TokenOptions } from '../common/types.js'\nimport { buildX402TokenRequestBody } from './token-request.js'\n\n/**\n * X402 Token API for generating access tokens.\n *\n * Handles X402 access token generation for subscribers to authorize\n * payment operations with AI agents.\n */\nexport class X402TokenAPI extends BasePaymentsAPI {\n /**\n * Get a singleton instance of the X402TokenAPI class.\n *\n * @param options - The options to initialize the API\n * @returns The instance of the X402TokenAPI class\n */\n static getInstance(options: PaymentOptions): X402TokenAPI {\n return new X402TokenAPI(options)\n }\n\n /**\n * Get an X402 access token for the given plan, backed by a delegation.\n *\n * This token allows the agent to verify and settle delegations on behalf\n * of the subscriber.\n *\n * The supported flow is **create-first**: create the delegation once with\n * {@link DelegationAPI.createDelegation}, then pass its `delegationId` in\n * `tokenOptions.delegationConfig`. Passing inline create-on-the-fly fields\n * (spending limits / `providerPaymentMethodId` / `cardId`, i.e. no\n * `delegationId`) is **deprecated** and emits a runtime warning.\n *\n * @param planId - The unique identifier of the payment plan\n * @param agentId - The unique identifier of the AI agent (optional)\n * @param tokenOptions - Options controlling scheme and delegation behavior (optional)\n * @returns A promise that resolves to an object containing:\n * - accessToken: The X402 access token string\n *\n * @throws PaymentsError if the request fails\n *\n * @example\n * ```typescript\n * // Supported: create the delegation first, then request the token by id.\n * const { delegationId } = await payments.delegation.createDelegation({\n * provider: 'erc4337',\n * spendingLimitCents: 10000,\n * durationSecs: 604800,\n * currency: 'usdc',\n * })\n * const result = await payments.x402.getX402AccessToken(planId, agentId, {\n * delegationConfig: { delegationId },\n * })\n * ```\n */\n async getX402AccessToken(\n planId: string,\n agentId?: string,\n tokenOptions?: X402TokenOptions,\n ): Promise<{ accessToken: string; [key: string]: any }> {\n const urlPath = API_URL_CREATE_PERMISSION\n const url = new URL(urlPath, this.environment.backend)\n\n const body = buildX402TokenRequestBody({\n planId,\n agentId,\n tokenOptions,\n environmentName: this.environmentName,\n })\n\n const options = this.getBackendHTTPOptions('POST', body)\n\n try {\n const response = await fetch(url, options)\n if (!response.ok) {\n let errorMessage = 'Failed to create X402 delegation token'\n try {\n const errorData = await response.json()\n errorMessage = errorData.message || errorMessage\n } catch {\n // Use default error message\n }\n throw PaymentsError.internal(`${errorMessage} (HTTP ${response.status})`)\n }\n return await response.json()\n } catch (error) {\n if (error instanceof PaymentsError) {\n throw error\n }\n throw PaymentsError.internal(\n `Network error while creating X402 delegation token: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n}\n"]}
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/x402/token.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAE3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAA;AAC9D,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAA;AAE7D;;;;;GAKG;AACH,MAAM,OAAO,YAAa,SAAQ,eAAe;IAC/C;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,OAAuB;QACxC,OAAO,IAAI,YAAY,CAAC,OAAO,CAAC,CAAA;IAClC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+DG;IACH,KAAK,CAAC,kBAAkB,CACtB,MAAc,EACd,OAAgB,EAChB,YAA+B;QAE/B,MAAM,OAAO,GAAG,yBAAyB,CAAA;QACzC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;QAEtD,MAAM,IAAI,GAAG,yBAAyB,CAAC;YACrC,MAAM;YACN,OAAO;YACP,YAAY;YACZ,eAAe,EAAE,IAAI,CAAC,eAAe;SACtC,CAAC,CAAA;QAEF,MAAM,OAAO,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QAExD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;YAC1C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,IAAI,YAAY,GAAG,wCAAwC,CAAA;gBAC3D,IAAI,CAAC;oBACH,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;oBACvC,YAAY,GAAG,SAAS,CAAC,OAAO,IAAI,YAAY,CAAA;gBAClD,CAAC;gBAAC,MAAM,CAAC;oBACP,4BAA4B;gBAC9B,CAAC;gBACD,MAAM,aAAa,CAAC,QAAQ,CAAC,GAAG,YAAY,UAAU,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;YAC3E,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YACpC,wEAAwE;YACxE,mEAAmE;YACnE,0EAA0E;YAC1E,yEAAyE;YACzE,SAAS;YACT,IAAI,OAAO,MAAM,EAAE,WAAW,KAAK,QAAQ,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/E,MAAM,aAAa,CAAC,QAAQ,CAC1B,sEAAsE,CACvE,CAAA;YACH,CAAC;YACD,2EAA2E;YAC3E,2EAA2E;YAC3E,oEAAoE;YACpE,sEAAsE;YACtE,wCAAwC;YACxC,OAAO,EAAE,GAAG,MAAM,EAAE,YAAY,EAAE,wBAAwB,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAA;QAClF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;gBACnC,MAAM,KAAK,CAAA;YACb,CAAC;YACD,MAAM,aAAa,CAAC,QAAQ,CAC1B,uDAAuD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAChH,CAAA;QACH,CAAC;IACH,CAAC;CACF","sourcesContent":["/**\n * X402 Token Generation API.\n *\n * Provides X402 access token generation functionality for subscribers.\n * Tokens are used to authorize payment verification and settlement.\n */\n\nimport { BasePaymentsAPI } from '../api/base-payments.js'\nimport { API_URL_CREATE_PERMISSION } from '../api/nvm-api.js'\nimport { PaymentsError } from '../common/payments.error.js'\nimport { PaymentOptions, X402TokenOptions, X402TokenVersion } from '../common/types.js'\nimport { buildX402TokenRequestBody } from './token-request.js'\nimport { detectAccessTokenVersion } from './token-version.js'\n\n/**\n * X402 Token API for generating access tokens.\n *\n * Handles X402 access token generation for subscribers to authorize\n * payment operations with AI agents.\n */\nexport class X402TokenAPI extends BasePaymentsAPI {\n /**\n * Get a singleton instance of the X402TokenAPI class.\n *\n * @param options - The options to initialize the API\n * @returns The instance of the X402TokenAPI class\n */\n static getInstance(options: PaymentOptions): X402TokenAPI {\n return new X402TokenAPI(options)\n }\n\n /**\n * Get an X402 access token for the given plan, backed by a delegation.\n *\n * This token allows the agent to verify and settle delegations on behalf\n * of the subscriber.\n *\n * The supported flow is **create-first**: create the delegation once with\n * {@link DelegationAPI.createDelegation}, then pass its `delegationId` in\n * `tokenOptions.delegationConfig`. Passing inline create-on-the-fly fields\n * (spending limits / `providerPaymentMethodId` / `cardId`, i.e. no\n * `delegationId`) is **deprecated** and emits a runtime warning.\n *\n * Pass `resource` / `httpVerb` to bind the token to one seller endpoint, and\n * `tokenVersion: 3` to ask for the single-use, seller/resource-bound token.\n * v3 is opt-in and the request may be silently downgraded by a backend that\n * predates it, so the returned `tokenVersion` is read off the minted token\n * rather than echoed back from the request — branch on that, never on what\n * you asked for.\n *\n * @param planId - The unique identifier of the payment plan\n * @param agentId - The unique identifier of the AI agent (optional)\n * @param tokenOptions - Options controlling scheme, resource binding, token version and delegation behavior (optional)\n * @returns A promise that resolves to an object containing:\n * - accessToken: The X402 access token string\n * - tokenVersion: The EIP-712 version the returned token was actually signed\n * under (`3` means single-use — mint a new token per paid request)\n *\n * @throws PaymentsError if the request fails\n *\n * @example\n * ```typescript\n * // Supported: create the delegation first, then request the token by id.\n * const { delegationId } = await payments.delegation.createDelegation({\n * provider: 'erc4337',\n * spendingLimitCents: 10000,\n * durationSecs: 604800,\n * currency: 'usdc',\n * })\n * const result = await payments.x402.getX402AccessToken(planId, agentId, {\n * delegationConfig: { delegationId },\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Single-use, bound to one seller endpoint. Mint one per paid request.\n * // `resource.url` must be the string the seller advertises in its 402\n * // `paymentRequired` — sellers built on this SDK's `paymentMiddleware`\n * // advertise `req.originalUrl`, i.e. a relative path.\n * const { accessToken, tokenVersion } = await payments.x402.getX402AccessToken(\n * planId,\n * agentId,\n * {\n * delegationConfig: { delegationId },\n * resource: { url: '/api/v1/tasks' },\n * httpVerb: 'POST',\n * tokenVersion: 3,\n * },\n * )\n * if (tokenVersion === 2) {\n * // The backend does not support v3 yet; this token is still reusable.\n * }\n * ```\n */\n async getX402AccessToken(\n planId: string,\n agentId?: string,\n tokenOptions?: X402TokenOptions,\n ): Promise<{ accessToken: string; tokenVersion: X402TokenVersion; [key: string]: any }> {\n const urlPath = API_URL_CREATE_PERMISSION\n const url = new URL(urlPath, this.environment.backend)\n\n const body = buildX402TokenRequestBody({\n planId,\n agentId,\n tokenOptions,\n environmentName: this.environmentName,\n })\n\n const options = this.getBackendHTTPOptions('POST', body)\n\n try {\n const response = await fetch(url, options)\n if (!response.ok) {\n let errorMessage = 'Failed to create X402 delegation token'\n try {\n const errorData = await response.json()\n errorMessage = errorData.message || errorMessage\n } catch {\n // Use default error message\n }\n throw PaymentsError.internal(`${errorMessage} (HTTP ${response.status})`)\n }\n const result = await response.json()\n // A 2xx with no usable token is a broken response, not a token: without\n // this guard the caller gets `{ accessToken: undefined }` typed as\n // `string`, sends `undefined` as the `payment-signature`, and the failure\n // surfaces at the seller as an unparseable token — three layers from the\n // cause.\n if (typeof result?.accessToken !== 'string' || result.accessToken.length === 0) {\n throw PaymentsError.internal(\n 'X402 token request succeeded but the response carried no accessToken',\n )\n }\n // Detected, never echoed: `tokenVersion: 3` is dropped without an error by\n // a backend that predates the v3 struct, so the only trustworthy source of\n // the version is the token itself. The override after the spread is\n // deliberate — a `tokenVersion` echoed by the backend is discarded in\n // favour of the one read off the token.\n return { ...result, tokenVersion: detectAccessTokenVersion(result.accessToken) }\n } catch (error) {\n if (error instanceof PaymentsError) {\n throw error\n }\n throw PaymentsError.internal(\n `Network error while creating X402 delegation token: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@nevermined-io/payments",
3
- "version": "1.12.0",
3
+ "version": "1.14.0",
4
4
  "description": "Typescript SDK to interact with the Nevermined Payments Protocol",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "type": "module",
8
+ "engines": {
9
+ "node": ">=20.0.0"
10
+ },
8
11
  "repository": {
9
12
  "type": "git",
10
13
  "url": "git+https://github.com/nevermined-io/payments"
@@ -44,6 +47,7 @@
44
47
  "scripts": {
45
48
  "clean": "rm -rf ./dist/ ./doc/ ./.nyc_output",
46
49
  "build": "tsc",
50
+ "typecheck:tests": "tsc --noEmit -p tests/tsconfig.json",
47
51
  "build:all": "pnpm build && pnpm cli:build",
48
52
  "lint": "eslint ./src",
49
53
  "test": "jest --verbose --config ./tests/jest.config.json",
@@ -63,26 +67,25 @@
63
67
  "devDependencies": {
64
68
  "@anthropic-ai/sdk": "^0.91.1",
65
69
  "@aws-sdk/client-bedrock-runtime": "^3.972.0",
66
- "@babel/core": "^7.27.4",
70
+ "@babel/core": "^7.29.6",
67
71
  "@babel/preset-env": "^7.27.2",
68
- "@google-cloud/aiplatform": "^6.1.0",
72
+ "@google-cloud/aiplatform": "^7.5.0",
69
73
  "@langchain/langgraph": "1.2.0",
70
74
  "@modelcontextprotocol/sdk": "^1.24.3",
71
75
  "@types/express": "4.17.23",
72
76
  "@types/jest": "^29.5.13",
73
77
  "@types/node": "^20.11.19",
74
78
  "@types/supertest": "^6.0.3",
75
- "@types/uuid": "^10.0.0",
76
79
  "@types/ws": "^8.0.3",
77
80
  "@typescript-eslint/eslint-plugin": "^5.62.0",
78
81
  "@typescript-eslint/parser": "^5.62.0",
79
82
  "babel-jest": "^30.0.2",
80
- "cohere-ai": "^7.19.0",
83
+ "cohere-ai": "^8.1.0",
81
84
  "dotenv": "^17.2.3",
82
85
  "eslint": "^8.56.0",
83
86
  "eslint-config-nevermined": "^0.3.0",
84
87
  "eslint-config-next": "^15.1.5",
85
- "eslint-config-prettier": "^9.1.0",
88
+ "eslint-config-prettier": "^10.1.8",
86
89
  "eslint-plugin-tsdoc": "^0.5.2",
87
90
  "jest": "^29.7.0",
88
91
  "langchain": "^0.3.37",
@@ -131,20 +134,26 @@
131
134
  "@traceloop/node-server-sdk": "^0.26.0",
132
135
  "axios": "^1.13.1",
133
136
  "express": "4.21.2",
134
- "jose": "^5.2.4",
137
+ "jose": "^6.2.12",
135
138
  "js-file-download": "^0.4.12",
136
- "uuid": "^10.0.0",
139
+ "uuid": "^11.1.1",
137
140
  "zod": "^4.0.17",
138
141
  "zod-to-json-schema": "^3.25.0"
139
142
  },
140
143
  "packageManager": "pnpm@10.33.0",
141
144
  "pnpm": {
142
145
  "overrides": {
143
- "qs": ">=6.14.1",
144
- "jws": ">=4.0.1",
145
- "js-yaml": ">=4.1.1",
146
- "@langchain/core": ">=0.3.80",
147
- "@smithy/config-resolver": ">=4.4.0"
146
+ "@opentelemetry/core@2": ">=2.8.0 <3",
147
+ "@opentelemetry/exporter-prometheus": ">=0.217.0 <1",
148
+ "@opentelemetry/propagator-jaeger": ">=2.9.0 <3",
149
+ "@opentelemetry/sdk-node": ">=0.217.0 <1",
150
+ "body-parser@1": ">=1.20.6 <2",
151
+ "express@4>path-to-regexp": ">=0.1.13 <0.2.0",
152
+ "ip-address": ">=10.3.1 <11",
153
+ "langsmith@<0.6.0": ">=0.6.0 <1",
154
+ "qs@6": ">=6.16.0 <7",
155
+ "uuid@10": ">=11.1.1 <12",
156
+ "uuid@9": ">=11.1.1 <12"
148
157
  }
149
158
  }
150
159
  }