@nevermined-io/payments 1.5.0 → 1.7.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 (56) hide show
  1. package/README.md +121 -93
  2. package/dist/api/agents-api.d.ts.map +1 -1
  3. package/dist/api/agents-api.js +2 -2
  4. package/dist/api/agents-api.js.map +1 -1
  5. package/dist/api/base-payments.d.ts +6 -4
  6. package/dist/api/base-payments.d.ts.map +1 -1
  7. package/dist/api/base-payments.js +10 -0
  8. package/dist/api/base-payments.js.map +1 -1
  9. package/dist/api/contracts-api.js +1 -1
  10. package/dist/api/contracts-api.js.map +1 -1
  11. package/dist/api/plans-api.d.ts.map +1 -1
  12. package/dist/api/plans-api.js +3 -10
  13. package/dist/api/plans-api.js.map +1 -1
  14. package/dist/common/api-version.d.ts +24 -0
  15. package/dist/common/api-version.d.ts.map +1 -0
  16. package/dist/common/api-version.js +24 -0
  17. package/dist/common/api-version.js.map +1 -0
  18. package/dist/common/types.d.ts +73 -18
  19. package/dist/common/types.d.ts.map +1 -1
  20. package/dist/common/types.js.map +1 -1
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/payments.d.ts +4 -3
  26. package/dist/payments.d.ts.map +1 -1
  27. package/dist/payments.js +5 -3
  28. package/dist/payments.js.map +1 -1
  29. package/dist/x402/index.d.ts +1 -1
  30. package/dist/x402/index.d.ts.map +1 -1
  31. package/dist/x402/index.js.map +1 -1
  32. package/dist/x402/langchain/agent.d.ts +96 -0
  33. package/dist/x402/langchain/agent.d.ts.map +1 -0
  34. package/dist/x402/langchain/agent.js +121 -0
  35. package/dist/x402/langchain/agent.js.map +1 -0
  36. package/dist/x402/langchain/decorator.d.ts +43 -4
  37. package/dist/x402/langchain/decorator.d.ts.map +1 -1
  38. package/dist/x402/langchain/decorator.js +173 -6
  39. package/dist/x402/langchain/decorator.js.map +1 -1
  40. package/dist/x402/langchain/index.d.ts +2 -1
  41. package/dist/x402/langchain/index.d.ts.map +1 -1
  42. package/dist/x402/langchain/index.js +2 -1
  43. package/dist/x402/langchain/index.js.map +1 -1
  44. package/dist/x402/langsmith/index.d.ts +15 -0
  45. package/dist/x402/langsmith/index.d.ts.map +1 -0
  46. package/dist/x402/langsmith/index.js +15 -0
  47. package/dist/x402/langsmith/index.js.map +1 -0
  48. package/dist/x402/langsmith/spans.d.ts +163 -0
  49. package/dist/x402/langsmith/spans.d.ts.map +1 -0
  50. package/dist/x402/langsmith/spans.js +341 -0
  51. package/dist/x402/langsmith/spans.js.map +1 -0
  52. package/dist/x402/token.d.ts +13 -10
  53. package/dist/x402/token.d.ts.map +1 -1
  54. package/dist/x402/token.js +46 -16
  55. package/dist/x402/token.js.map +1 -1
  56. package/package.json +16 -2
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * The `credits` option accepts two forms:
16
16
  * - **Static number**: `credits: 1` — always charges 1 credit
17
- * - **Function**: `credits: (ctx) => Math.max(1, ctx.result.length / 100)` — dynamic
17
+ * - **Function**: `credits: (ctx) => Math.max(1, Math.floor(ctx.result.length / 100))` — dynamic
18
18
  *
19
19
  * When `credits` is a function, it receives `{ args, result }` after tool execution.
20
20
  *
@@ -43,7 +43,7 @@
43
43
  * ```
44
44
  */
45
45
  import type { Payments } from '../../payments.js';
46
- import { type X402PaymentRequired } from '../facilitator-api.js';
46
+ import { type X402PaymentRequired, type SettlePermissionsResult } from '../facilitator-api.js';
47
47
  /**
48
48
  * Context passed to a dynamic credits function after tool execution.
49
49
  */
@@ -66,7 +66,18 @@ export interface RequiresPaymentOptions {
66
66
  payments: Payments;
67
67
  /** Single plan ID to accept */
68
68
  planId: string;
69
- /** Number of credits to charge, or a function for dynamic pricing (default: 1) */
69
+ /**
70
+ * Number of credits to charge, or a function for dynamic pricing (default: 1).
71
+ *
72
+ * This value is sent as `maxAmount` to the facilitator. The amount actually
73
+ * redeemed depends on the plan's server-side credit configuration:
74
+ *
75
+ * - **Fixed plans** (`plan.credits.minAmount === plan.credits.maxAmount`)
76
+ * always burn `plan.credits.maxAmount` — this value is then effectively a
77
+ * no-op (see nevermined-io/nvm-monorepo#1568).
78
+ * - **Range plans** clamp this value into
79
+ * `[plan.credits.minAmount, plan.credits.maxAmount]`.
80
+ */
70
81
  credits?: number | CreditsCallable;
71
82
  /** Optional agent identifier */
72
83
  agentId?: string;
@@ -88,7 +99,17 @@ export declare class PaymentRequiredError extends Error {
88
99
  * Payment context stored in `config.configurable.payment_context` after verification.
89
100
  */
90
101
  export interface PaymentContext {
91
- /** The x402 access token */
102
+ /**
103
+ * Abbreviated, non-functional reference to the x402 access token — the SAME
104
+ * redacted form surfaced as `nvm.payment_token` (`<first 16>…<last 4>`, or a
105
+ * `…(short)` marker for a too-short token). The **full token is deliberately
106
+ * not persisted here**: this object is written into
107
+ * `config.configurable.payment_context`, and tracing frameworks (e.g.
108
+ * LangChain) can capture `config.configurable` into span metadata, so storing
109
+ * the raw credential would let it ride into any traced run opened during or
110
+ * after the tool body. Settlement uses the token read from
111
+ * `config.configurable.payment_token`, never this field.
112
+ */
92
113
  token: string;
93
114
  /** The payment required object */
94
115
  paymentRequired: X402PaymentRequired;
@@ -101,6 +122,24 @@ export interface PaymentContext {
101
122
  /** Agent request context for observability */
102
123
  agentRequest?: unknown;
103
124
  }
125
+ /**
126
+ * Return the most recent settlement receipt produced by {@link requiresPayment}.
127
+ *
128
+ * Use this after invoking a LangChain/LangGraph runnable whose tool is wrapped
129
+ * with {@link requiresPayment} to recover the settlement receipt
130
+ * (`creditsRedeemed`, `remainingBalance`, `transaction`, `network`, `payer`)
131
+ * without threading it back through the runnable config (which LangGraph copies
132
+ * per node, so the in-place write is invisible to the outer scope).
133
+ *
134
+ * Returns `undefined` if no settlement has happened yet in this process, or if
135
+ * the most recent invocation raised before reaching the settle phase.
136
+ *
137
+ * @remarks
138
+ * This accessor reads from a module-level slot. In multi-tenant processes (e.g.
139
+ * a server handling concurrent settlements), the value reflects whichever
140
+ * invocation settled most recently — there is no per-call isolation.
141
+ */
142
+ export declare function lastSettlement(): SettlePermissionsResult | undefined;
104
143
  /**
105
144
  * Wraps a LangChain.js tool implementation with x402 payment verification and settlement.
106
145
  *
@@ -1 +1 @@
1
- {"version":3,"file":"decorator.d.ts","sourceRoot":"","sources":["../../../src/x402/langchain/decorator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,uBAAuB,CAAA;AAE9B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,8BAA8B;IAC9B,MAAM,EAAE,OAAO,CAAA;CAChB;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAA;AAE7D;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,wDAAwD;IACxD,QAAQ,EAAE,QAAQ,CAAA;IAClB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,GAAG,eAAe,CAAA;IAClC,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,yEAAyE;IACzE,eAAe,EAAE,mBAAmB,GAAG,SAAS,CAAA;gBAEpC,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,mBAAmB;CAKnE;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,kCAAkC;IAClC,eAAe,EAAE,mBAAmB,CAAA;IACpC,kCAAkC;IAClC,eAAe,EAAE,MAAM,CAAA;IACvB,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,CAAA;IACjB,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AA8BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,eAAe,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAC5E,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EACjE,OAAO,EAAE,sBAAsB,GAC9B,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAiFrD"}
1
+ {"version":3,"file":"decorator.d.ts","sourceRoot":"","sources":["../../../src/x402/langchain/decorator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAEL,KAAK,mBAAmB,EAExB,KAAK,uBAAuB,EAC7B,MAAM,uBAAuB,CAAA;AAY9B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,8BAA8B;IAC9B,MAAM,EAAE,OAAO,CAAA;CAChB;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAA;AAE7D;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,wDAAwD;IACxD,QAAQ,EAAE,QAAQ,CAAA;IAClB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,eAAe,CAAA;IAClC,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,yEAAyE;IACzE,eAAe,EAAE,mBAAmB,GAAG,SAAS,CAAA;gBAEpC,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,mBAAmB;CAKnE;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;OAUG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,kCAAkC;IAClC,eAAe,EAAE,mBAAmB,CAAA;IACpC,kCAAkC;IAClC,eAAe,EAAE,MAAM,CAAA;IACvB,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,CAAA;IACjB,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAeD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,IAAI,uBAAuB,GAAG,SAAS,CAEpE;AAwCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,eAAe,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAC5E,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EACjE,OAAO,EAAE,sBAAsB,GAC9B,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAoNrD"}
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * The `credits` option accepts two forms:
16
16
  * - **Static number**: `credits: 1` — always charges 1 credit
17
- * - **Function**: `credits: (ctx) => Math.max(1, ctx.result.length / 100)` — dynamic
17
+ * - **Function**: `credits: (ctx) => Math.max(1, Math.floor(ctx.result.length / 100))` — dynamic
18
18
  *
19
19
  * When `credits` is a function, it receives `{ args, result }` after tool execution.
20
20
  *
@@ -43,6 +43,7 @@
43
43
  * ```
44
44
  */
45
45
  import { buildPaymentRequired, } from '../facilitator-api.js';
46
+ import { abbreviateToken, activeRunTree, addMetadata, buildSettleMetadata, buildVerifyMetadata, redactMetadataKeys, settlementSpan, verifySpan, } from '../langsmith/spans.js';
46
47
  /**
47
48
  * Thrown when payment verification fails or no token is provided.
48
49
  *
@@ -56,6 +57,38 @@ export class PaymentRequiredError extends Error {
56
57
  this.paymentRequired = paymentRequired;
57
58
  }
58
59
  }
60
+ // Module-level holder for the most recent settlement receipt. LangGraph copies
61
+ // `RunnableConfig.configurable` per node, so the in-place write to
62
+ // `config.configurable.payment_settlement` is not visible to the buyer's outer
63
+ // scope. A module-level slot is the simplest reliable signal. It is
64
+ // intentionally single-tenant — if the same process runs multiple concurrent
65
+ // settlements, the last writer wins. This race is not limited to multi-tenant
66
+ // servers: a single `createPaidReactAgent` run can also hit it, because a ReAct
67
+ // agent may execute several paid tools in parallel within one LLM turn via
68
+ // `ToolNode`, and this single slot only retains the last writer. For
69
+ // multi-tenant or parallel-tool use cases, surface the receipt via a callback
70
+ // or via observability (see Sprint 1 of the LangChain epic).
71
+ let lastSettlementReceipt;
72
+ /**
73
+ * Return the most recent settlement receipt produced by {@link requiresPayment}.
74
+ *
75
+ * Use this after invoking a LangChain/LangGraph runnable whose tool is wrapped
76
+ * with {@link requiresPayment} to recover the settlement receipt
77
+ * (`creditsRedeemed`, `remainingBalance`, `transaction`, `network`, `payer`)
78
+ * without threading it back through the runnable config (which LangGraph copies
79
+ * per node, so the in-place write is invisible to the outer scope).
80
+ *
81
+ * Returns `undefined` if no settlement has happened yet in this process, or if
82
+ * the most recent invocation raised before reaching the settle phase.
83
+ *
84
+ * @remarks
85
+ * This accessor reads from a module-level slot. In multi-tenant processes (e.g.
86
+ * a server handling concurrent settlements), the value reflects whichever
87
+ * invocation settled most recently — there is no per-call isolation.
88
+ */
89
+ export function lastSettlement() {
90
+ return lastSettlementReceipt;
91
+ }
59
92
  /**
60
93
  * Extract the payment token from a LangChain RunnableConfig.
61
94
  *
@@ -82,6 +115,17 @@ function storeInConfigurable(config, key, value) {
82
115
  return;
83
116
  configurable[key] = value;
84
117
  }
118
+ /**
119
+ * Remove a key from config.configurable if present (no-op otherwise).
120
+ */
121
+ function removeFromConfigurable(config, key) {
122
+ if (config == null || typeof config !== 'object')
123
+ return;
124
+ const configurable = config.configurable;
125
+ if (configurable == null || typeof configurable !== 'object')
126
+ return;
127
+ delete configurable[key];
128
+ }
85
129
  /**
86
130
  * Wraps a LangChain.js tool implementation with x402 payment verification and settlement.
87
131
  *
@@ -126,17 +170,82 @@ function storeInConfigurable(config, key, value) {
126
170
  export function requiresPayment(fn, options) {
127
171
  const { payments, planId, credits = 1, agentId, network } = options;
128
172
  return async (args, config) => {
173
+ // Reset the module-level slot at the START of every invocation, before
174
+ // verify. Any failure that does not reach the settle-success write (a
175
+ // verify failure / PaymentRequiredError, or a swallowed settle failure)
176
+ // then leaves lastSettlement() returning `undefined` rather than a stale
177
+ // receipt from a previous invocation — matching the JSDoc contract on
178
+ // lastSettlement().
179
+ lastSettlementReceipt = undefined;
129
180
  // Build payment required object
130
181
  const paymentRequired = buildPaymentRequired(planId, {
131
182
  endpoint: fn.name || 'tool',
132
183
  agentId,
133
184
  network,
134
185
  });
186
+ // The scheme/network the verify span advertises come from the resolved
187
+ // X402 scheme so the span metadata matches what the buyer paid against.
188
+ const accepted = paymentRequired.accepts[0];
189
+ const planIds = paymentRequired.accepts
190
+ .map((a) => a.planId)
191
+ .filter((p) => Boolean(p));
192
+ const resolvedScheme = accepted?.scheme;
193
+ const resolvedNetwork = network ?? accepted?.network;
194
+ // LangChain auto-captures every key in config.configurable into the parent
195
+ // tool span's metadata, and child spans inherit it at construction time.
196
+ // Strip the full x402 access token from the parent BEFORE opening the verify
197
+ // span so neither the parent nor the child carries the raw credential — only
198
+ // the abbreviated nvm.payment_token remains for correlation.
199
+ const parentRunTree = await activeRunTree();
200
+ redactMetadataKeys(parentRunTree, 'payment_token');
201
+ const verifyStarted = Date.now();
202
+ // Open the verify span BEFORE the token-presence check so failed probes
203
+ // (no payment_token in config) still produce a clearly-named span with the
204
+ // static nvm.* attrs attached to both the span and the parent tool span.
205
+ const vspan = await verifySpan({
206
+ planIds,
207
+ scheme: resolvedScheme,
208
+ network: resolvedNetwork,
209
+ agentId,
210
+ });
211
+ // Pre-verify metadata is best-effort and static-only.
212
+ const preVerifyMd = buildVerifyMetadata({
213
+ planIds,
214
+ scheme: resolvedScheme,
215
+ network: resolvedNetwork,
216
+ agentId,
217
+ });
218
+ vspan.addMetadata(preVerifyMd);
219
+ addMetadata(parentRunTree, preVerifyMd);
135
220
  // Extract token from config.configurable.payment_token
136
221
  const token = extractPaymentToken(config);
137
222
  if (!token) {
138
- throw new PaymentRequiredError("Payment required: missing payment_token in config.configurable", paymentRequired);
223
+ await vspan.end(new Error('missing payment_token in config.configurable'));
224
+ throw new PaymentRequiredError('Payment required: missing payment_token in config.configurable', paymentRequired);
139
225
  }
226
+ // Drop the raw token from configurable now that we hold it in `token`.
227
+ // LangChain's `ensureConfig` re-promotes every configurable scalar into a
228
+ // runnable's `metadata` on EACH invocation, so any traced runnable the tool
229
+ // body spawns (an LLM call, a sub-chain) sharing this config would otherwise
230
+ // re-leak the full credential into its own span metadata — and a child run
231
+ // opened *during* `fn()` would capture it before the settle-side
232
+ // `redactMetadataKeys` below could run. Removing the key here is the
233
+ // proactive complement to that reactive redaction. This MUST run after
234
+ // extraction (deleting earlier would make `extractPaymentToken` return null)
235
+ // and before `fn()` — guaranteed, since `fn()` is called further down.
236
+ // Settlement uses the local `token`, never configurable, so removal is
237
+ // non-functional. On the `tool()`/LangGraph path LangChain hands the wrapper
238
+ // a per-invocation config copy, so this does not mutate a config the caller
239
+ // reuses across sibling tools.
240
+ removeFromConfigurable(config, 'payment_token');
241
+ // Abbreviate/redact the token ONCE here (mirrors Python's
242
+ // attach_metadata_safely pre-abbreviation) and pass the result into the
243
+ // metadata builders. abbreviateToken is idempotent, so the builders leave
244
+ // it unchanged — this means the short-token warning fires at most once per
245
+ // call (not once per verify AND once per settle), and the raw token never
246
+ // reaches the builders' frame locals (defense against exception enrichers
247
+ // that capture locals).
248
+ const abbreviatedToken = abbreviateToken(token);
140
249
  // Resolve pre-execution credits (static only; callable deferred to post-execution)
141
250
  const creditsToVerify = typeof credits === 'number' ? credits : 1;
142
251
  // Verify permissions
@@ -149,14 +258,38 @@ export function requiresPayment(fn, options) {
149
258
  });
150
259
  }
151
260
  catch (error) {
261
+ await vspan.end(error);
152
262
  throw new PaymentRequiredError(`Payment verification failed: ${error instanceof Error ? error.message : String(error)}`, paymentRequired);
153
263
  }
264
+ // Augment span metadata with verification results + timing (both span and
265
+ // parent), then close the verify span. Best-effort: a metadata failure must
266
+ // not mask the PaymentRequiredError that may follow.
267
+ const verifyMd = buildVerifyMetadata({
268
+ planIds,
269
+ scheme: resolvedScheme,
270
+ network: resolvedNetwork,
271
+ agentId,
272
+ verification,
273
+ durationMs: Date.now() - verifyStarted,
274
+ token: abbreviatedToken,
275
+ });
276
+ vspan.addMetadata(verifyMd);
277
+ addMetadata(parentRunTree, verifyMd);
154
278
  if (!verification.isValid) {
279
+ await vspan.end(new Error(verification.invalidReason || 'verification failed'));
155
280
  throw new PaymentRequiredError(`Payment verification failed: ${verification.invalidReason || 'Insufficient credits or invalid token'}`, paymentRequired);
156
281
  }
157
- // Store payment context
282
+ await vspan.end();
283
+ // Store payment context. The `token` field carries the ABBREVIATED
284
+ // reference, never the raw credential: `payment_context` is written into
285
+ // `config.configurable`, which LangChain can capture into span metadata, so
286
+ // persisting the full token here would reopen the very leak the parent-tree
287
+ // `redactMetadataKeys('payment_token')` calls close — and a child run opened
288
+ // *during* the tool body would capture it before any post-hoc redaction
289
+ // could run. `abbreviatedToken` is always defined past the `!token` guard
290
+ // above; `?? ''` is a belt-and-suspenders that can never fall back to raw.
158
291
  const paymentContext = {
159
- token,
292
+ token: abbreviatedToken ?? '',
160
293
  paymentRequired,
161
294
  creditsToSettle: creditsToVerify,
162
295
  verified: true,
@@ -170,18 +303,52 @@ export function requiresPayment(fn, options) {
170
303
  const finalCredits = typeof credits === 'function'
171
304
  ? credits({ args: args, result })
172
305
  : credits;
173
- // Settle credits
306
+ // Settle credits, wrapped in an nvm:settlement span (mirrors Python's
307
+ // settlement_span around settle_permissions).
308
+ //
309
+ // Re-fetch the active run tree: `fn()` may have opened nested traced runs, so
310
+ // `activeRunTree()` can now return a different RunTree than the verify-side
311
+ // redaction at the top of this function scrubbed. We already removed
312
+ // `payment_token` from `config.configurable` before `fn()` ran, so newly
313
+ // opened child runs can no longer re-promote the raw credential — this
314
+ // re-redaction is now defense-in-depth, covering any RunTree whose metadata
315
+ // was populated before that removal took effect. Redact BEFORE opening the
316
+ // settlement span so the credential never rides into the settle child span or
317
+ // the (possibly new) parent tree.
318
+ const settleParentRunTree = await activeRunTree();
319
+ redactMetadataKeys(settleParentRunTree, 'payment_token');
320
+ const settleStarted = Date.now();
321
+ const sspan = await settlementSpan({ planIds, agentId });
174
322
  try {
175
323
  const settlement = await payments.facilitator.settlePermissions({
176
324
  paymentRequired,
177
325
  x402AccessToken: token,
178
- maxAmount: BigInt(finalCredits),
326
+ // A dynamic `credits` callable can return a float (e.g. length/100).
327
+ // `BigInt(1.5)` throws RangeError, which the surrounding try/catch
328
+ // swallows — credits are never burned and the caller is never told.
329
+ // Floor to keep the settle on the money path.
330
+ maxAmount: BigInt(Math.floor(finalCredits)),
179
331
  agentRequestId: paymentContext.agentRequestId,
180
332
  });
181
333
  storeInConfigurable(config, 'payment_settlement', settlement);
334
+ // Also publish to the module-level slot so lastSettlement() can recover
335
+ // the receipt — LangGraph copies config.configurable per node, hiding the
336
+ // line above from the buyer's outer scope.
337
+ lastSettlementReceipt = settlement;
338
+ const settleMd = buildSettleMetadata({
339
+ settlement,
340
+ planIds,
341
+ agentId,
342
+ durationMs: Date.now() - settleStarted,
343
+ token: abbreviatedToken,
344
+ });
345
+ sspan.addMetadata(settleMd);
346
+ addMetadata(settleParentRunTree, settleMd);
347
+ await sspan.end();
182
348
  }
183
349
  catch (settleError) {
184
350
  console.error('Payment settlement failed:', settleError);
351
+ await sspan.end(settleError);
185
352
  // Still return result even if settlement fails
186
353
  }
187
354
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"decorator.js","sourceRoot":"","sources":["../../../src/x402/langchain/decorator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * LangChain tool wrapper for Nevermined payment protection using the x402 protocol.\n *\n * Wraps a LangChain.js tool implementation function to:\n *\n * 1. Extract the x402 payment token from `config.configurable.payment_token`\n * 2. Verify the subscriber has sufficient credits\n * 3. Execute the wrapped tool function\n * 4. Settle (burn) credits after successful execution\n *\n * Payment errors throw `PaymentRequiredError` so LangChain agents can catch and\n * surface them to the user. The error carries the full `X402PaymentRequired`\n * object for programmatic token acquisition.\n *\n * The `credits` option accepts two forms:\n * - **Static number**: `credits: 1` — always charges 1 credit\n * - **Function**: `credits: (ctx) => Math.max(1, ctx.result.length / 100)` — dynamic\n *\n * When `credits` is a function, it receives `{ args, result }` after tool execution.\n *\n * @example\n * ```typescript\n * import { tool } from '@langchain/core/tools'\n * import { z } from 'zod'\n * import { Payments } from '@nevermined-io/payments'\n * import { requiresPayment } from '@nevermined-io/payments/langchain'\n *\n * const payments = Payments.getInstance({ nvmApiKey: '...', environment: 'testing' })\n *\n * const searchData = tool(\n * requiresPayment(\n * (args) => `Results for ${args.query}`,\n * { payments, planId: PLAN_ID, credits: 1 }\n * ),\n * { name: 'search_data', description: 'Search for data', schema: z.object({ query: z.string() }) }\n * )\n *\n * // Invoke with payment token\n * const result = await searchData.invoke(\n * { query: 'AI trends' },\n * { configurable: { payment_token: accessToken } }\n * )\n * ```\n */\n\nimport type { Payments } from '../../payments.js'\nimport {\n buildPaymentRequired,\n type X402PaymentRequired,\n type VerifyPermissionsResult,\n} from '../facilitator-api.js'\n\n/**\n * Context passed to a dynamic credits function after tool execution.\n */\nexport interface CreditsContext {\n /** The tool's input arguments */\n args: Record<string, unknown>\n /** The tool's return value */\n result: unknown\n}\n\n/**\n * Credits can be a static number or a function that receives\n * `{ args, result }` and returns the number of credits to charge.\n */\nexport type CreditsCallable = (ctx: CreditsContext) => number\n\n/**\n * Options for the `requiresPayment` wrapper.\n */\nexport interface RequiresPaymentOptions {\n /** The Payments instance (with payments.facilitator) */\n payments: Payments\n /** Single plan ID to accept */\n planId: string\n /** Number of credits to charge, or a function for dynamic pricing (default: 1) */\n credits?: number | CreditsCallable\n /** Optional agent identifier */\n agentId?: string\n /** Blockchain network in CAIP-2 format (default: 'eip155:84532' for Base Sepolia) */\n network?: string\n}\n\n/**\n * Thrown when payment verification fails or no token is provided.\n *\n * Carries the `X402PaymentRequired` object so callers can inspect\n * accepted plans and acquire the correct payment token.\n */\nexport class PaymentRequiredError extends Error {\n /** The x402 PaymentRequired object for programmatic token acquisition */\n paymentRequired: X402PaymentRequired | undefined\n\n constructor(message: string, paymentRequired?: X402PaymentRequired) {\n super(message)\n this.name = 'PaymentRequiredError'\n this.paymentRequired = paymentRequired\n }\n}\n\n/**\n * Payment context stored in `config.configurable.payment_context` after verification.\n */\nexport interface PaymentContext {\n /** The x402 access token */\n token: string\n /** The payment required object */\n paymentRequired: X402PaymentRequired\n /** Number of credits to settle */\n creditsToSettle: number\n /** Whether verification was successful */\n verified: boolean\n /** Agent request ID for observability tracking */\n agentRequestId?: string\n /** Agent request context for observability */\n agentRequest?: unknown\n}\n\n/**\n * Extract the payment token from a LangChain RunnableConfig.\n *\n * In LangChain.js, the config is the optional second parameter to tool functions:\n * `(args, config?) => ...` where config is a `RunnableConfig`.\n */\nfunction extractPaymentToken(config: unknown): string | null {\n if (config == null || typeof config !== 'object') return null\n\n const configurable = (config as Record<string, unknown>).configurable\n if (configurable == null || typeof configurable !== 'object') return null\n\n const token = (configurable as Record<string, unknown>).payment_token\n return typeof token === 'string' ? token : null\n}\n\n/**\n * Store a value in config.configurable if available.\n */\nfunction storeInConfigurable(config: unknown, key: string, value: unknown): void {\n if (config == null || typeof config !== 'object') return\n\n const configurable = (config as Record<string, unknown>).configurable\n if (configurable == null || typeof configurable !== 'object') return\n\n ;(configurable as Record<string, unknown>)[key] = value\n}\n\n/**\n * Wraps a LangChain.js tool implementation with x402 payment verification and settlement.\n *\n * This is a higher-order function that takes the tool's implementation function and\n * payment options, returning a new function with the same signature that:\n *\n * 1. Extracts the payment token from `config.configurable.payment_token`\n * 2. Verifies the subscriber has sufficient credits\n * 3. Calls the original tool function\n * 4. Settles (burns) credits\n * 5. Stores `payment_context` and `payment_settlement` in `config.configurable`\n *\n * @param fn - The tool implementation function: `(args, config?) => result`\n * @param options - Payment configuration\n * @returns Wrapped function with the same signature\n *\n * @example Static credits\n * ```typescript\n * const searchData = tool(\n * requiresPayment(\n * (args) => `Results for ${args.query}`,\n * { payments, planId: PLAN_ID, credits: 1 }\n * ),\n * { name: 'search_data', description: 'Search', schema: z.object({ query: z.string() }) }\n * )\n * ```\n *\n * @example Dynamic credits\n * ```typescript\n * const summarize = tool(\n * requiresPayment(\n * (args) => `Summary of ${args.text}`,\n * {\n * payments, planId: PLAN_ID,\n * credits: (ctx) => Math.max(1, Math.floor(String(ctx.result).length / 100)),\n * }\n * ),\n * { name: 'summarize', description: 'Summarize text', schema: z.object({ text: z.string() }) }\n * )\n * ```\n */\nexport function requiresPayment<TArgs extends Record<string, unknown>, TResult>(\n fn: (args: TArgs, config?: unknown) => TResult | Promise<TResult>,\n options: RequiresPaymentOptions,\n): (args: TArgs, config?: unknown) => Promise<TResult> {\n const { payments, planId, credits = 1, agentId, network } = options\n\n return async (args: TArgs, config?: unknown): Promise<TResult> => {\n // Build payment required object\n const paymentRequired = buildPaymentRequired(planId, {\n endpoint: fn.name || 'tool',\n agentId,\n network,\n })\n\n // Extract token from config.configurable.payment_token\n const token = extractPaymentToken(config)\n if (!token) {\n throw new PaymentRequiredError(\n \"Payment required: missing payment_token in config.configurable\",\n paymentRequired,\n )\n }\n\n // Resolve pre-execution credits (static only; callable deferred to post-execution)\n const creditsToVerify = typeof credits === 'number' ? credits : 1\n\n // Verify permissions\n let verification: VerifyPermissionsResult\n try {\n verification = await payments.facilitator.verifyPermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: BigInt(creditsToVerify),\n })\n } catch (error) {\n throw new PaymentRequiredError(\n `Payment verification failed: ${error instanceof Error ? error.message : String(error)}`,\n paymentRequired,\n )\n }\n\n if (!verification.isValid) {\n throw new PaymentRequiredError(\n `Payment verification failed: ${verification.invalidReason || 'Insufficient credits or invalid token'}`,\n paymentRequired,\n )\n }\n\n // Store payment context\n const paymentContext: PaymentContext = {\n token,\n paymentRequired,\n creditsToSettle: creditsToVerify,\n verified: true,\n agentRequestId: verification.agentRequest?.agentRequestId || verification.agentRequestId,\n agentRequest: verification.agentRequest,\n }\n storeInConfigurable(config, 'payment_context', paymentContext)\n\n // Execute the tool function\n const result = await fn(args, config)\n\n // Resolve final credits (may be dynamic based on result)\n const finalCredits =\n typeof credits === 'function'\n ? credits({ args: args as Record<string, unknown>, result })\n : credits\n\n // Settle credits\n try {\n const settlement = await payments.facilitator.settlePermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: BigInt(finalCredits),\n agentRequestId: paymentContext.agentRequestId,\n })\n storeInConfigurable(config, 'payment_settlement', settlement)\n } catch (settleError) {\n console.error('Payment settlement failed:', settleError)\n // Still return result even if settlement fails\n }\n\n return result\n }\n}\n"]}
1
+ {"version":3,"file":"decorator.js","sourceRoot":"","sources":["../../../src/x402/langchain/decorator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * LangChain tool wrapper for Nevermined payment protection using the x402 protocol.\n *\n * Wraps a LangChain.js tool implementation function to:\n *\n * 1. Extract the x402 payment token from `config.configurable.payment_token`\n * 2. Verify the subscriber has sufficient credits\n * 3. Execute the wrapped tool function\n * 4. Settle (burn) credits after successful execution\n *\n * Payment errors throw `PaymentRequiredError` so LangChain agents can catch and\n * surface them to the user. The error carries the full `X402PaymentRequired`\n * object for programmatic token acquisition.\n *\n * The `credits` option accepts two forms:\n * - **Static number**: `credits: 1` — always charges 1 credit\n * - **Function**: `credits: (ctx) => Math.max(1, Math.floor(ctx.result.length / 100))` — dynamic\n *\n * When `credits` is a function, it receives `{ args, result }` after tool execution.\n *\n * @example\n * ```typescript\n * import { tool } from '@langchain/core/tools'\n * import { z } from 'zod'\n * import { Payments } from '@nevermined-io/payments'\n * import { requiresPayment } from '@nevermined-io/payments/langchain'\n *\n * const payments = Payments.getInstance({ nvmApiKey: '...', environment: 'testing' })\n *\n * const searchData = tool(\n * requiresPayment(\n * (args) => `Results for ${args.query}`,\n * { payments, planId: PLAN_ID, credits: 1 }\n * ),\n * { name: 'search_data', description: 'Search for data', schema: z.object({ query: z.string() }) }\n * )\n *\n * // Invoke with payment token\n * const result = await searchData.invoke(\n * { query: 'AI trends' },\n * { configurable: { payment_token: accessToken } }\n * )\n * ```\n */\n\nimport type { Payments } from '../../payments.js'\nimport {\n buildPaymentRequired,\n type X402PaymentRequired,\n type VerifyPermissionsResult,\n type SettlePermissionsResult,\n} from '../facilitator-api.js'\nimport {\n abbreviateToken,\n activeRunTree,\n addMetadata,\n buildSettleMetadata,\n buildVerifyMetadata,\n redactMetadataKeys,\n settlementSpan,\n verifySpan,\n} from '../langsmith/spans.js'\n\n/**\n * Context passed to a dynamic credits function after tool execution.\n */\nexport interface CreditsContext {\n /** The tool's input arguments */\n args: Record<string, unknown>\n /** The tool's return value */\n result: unknown\n}\n\n/**\n * Credits can be a static number or a function that receives\n * `{ args, result }` and returns the number of credits to charge.\n */\nexport type CreditsCallable = (ctx: CreditsContext) => number\n\n/**\n * Options for the `requiresPayment` wrapper.\n */\nexport interface RequiresPaymentOptions {\n /** The Payments instance (with payments.facilitator) */\n payments: Payments\n /** Single plan ID to accept */\n planId: string\n /**\n * Number of credits to charge, or a function for dynamic pricing (default: 1).\n *\n * This value is sent as `maxAmount` to the facilitator. The amount actually\n * redeemed depends on the plan's server-side credit configuration:\n *\n * - **Fixed plans** (`plan.credits.minAmount === plan.credits.maxAmount`)\n * always burn `plan.credits.maxAmount` — this value is then effectively a\n * no-op (see nevermined-io/nvm-monorepo#1568).\n * - **Range plans** clamp this value into\n * `[plan.credits.minAmount, plan.credits.maxAmount]`.\n */\n credits?: number | CreditsCallable\n /** Optional agent identifier */\n agentId?: string\n /** Blockchain network in CAIP-2 format (default: 'eip155:84532' for Base Sepolia) */\n network?: string\n}\n\n/**\n * Thrown when payment verification fails or no token is provided.\n *\n * Carries the `X402PaymentRequired` object so callers can inspect\n * accepted plans and acquire the correct payment token.\n */\nexport class PaymentRequiredError extends Error {\n /** The x402 PaymentRequired object for programmatic token acquisition */\n paymentRequired: X402PaymentRequired | undefined\n\n constructor(message: string, paymentRequired?: X402PaymentRequired) {\n super(message)\n this.name = 'PaymentRequiredError'\n this.paymentRequired = paymentRequired\n }\n}\n\n/**\n * Payment context stored in `config.configurable.payment_context` after verification.\n */\nexport interface PaymentContext {\n /**\n * Abbreviated, non-functional reference to the x402 access token — the SAME\n * redacted form surfaced as `nvm.payment_token` (`<first 16>…<last 4>`, or a\n * `…(short)` marker for a too-short token). The **full token is deliberately\n * not persisted here**: this object is written into\n * `config.configurable.payment_context`, and tracing frameworks (e.g.\n * LangChain) can capture `config.configurable` into span metadata, so storing\n * the raw credential would let it ride into any traced run opened during or\n * after the tool body. Settlement uses the token read from\n * `config.configurable.payment_token`, never this field.\n */\n token: string\n /** The payment required object */\n paymentRequired: X402PaymentRequired\n /** Number of credits to settle */\n creditsToSettle: number\n /** Whether verification was successful */\n verified: boolean\n /** Agent request ID for observability tracking */\n agentRequestId?: string\n /** Agent request context for observability */\n agentRequest?: unknown\n}\n\n// Module-level holder for the most recent settlement receipt. LangGraph copies\n// `RunnableConfig.configurable` per node, so the in-place write to\n// `config.configurable.payment_settlement` is not visible to the buyer's outer\n// scope. A module-level slot is the simplest reliable signal. It is\n// intentionally single-tenant — if the same process runs multiple concurrent\n// settlements, the last writer wins. This race is not limited to multi-tenant\n// servers: a single `createPaidReactAgent` run can also hit it, because a ReAct\n// agent may execute several paid tools in parallel within one LLM turn via\n// `ToolNode`, and this single slot only retains the last writer. For\n// multi-tenant or parallel-tool use cases, surface the receipt via a callback\n// or via observability (see Sprint 1 of the LangChain epic).\nlet lastSettlementReceipt: SettlePermissionsResult | undefined\n\n/**\n * Return the most recent settlement receipt produced by {@link requiresPayment}.\n *\n * Use this after invoking a LangChain/LangGraph runnable whose tool is wrapped\n * with {@link requiresPayment} to recover the settlement receipt\n * (`creditsRedeemed`, `remainingBalance`, `transaction`, `network`, `payer`)\n * without threading it back through the runnable config (which LangGraph copies\n * per node, so the in-place write is invisible to the outer scope).\n *\n * Returns `undefined` if no settlement has happened yet in this process, or if\n * the most recent invocation raised before reaching the settle phase.\n *\n * @remarks\n * This accessor reads from a module-level slot. In multi-tenant processes (e.g.\n * a server handling concurrent settlements), the value reflects whichever\n * invocation settled most recently — there is no per-call isolation.\n */\nexport function lastSettlement(): SettlePermissionsResult | undefined {\n return lastSettlementReceipt\n}\n\n/**\n * Extract the payment token from a LangChain RunnableConfig.\n *\n * In LangChain.js, the config is the optional second parameter to tool functions:\n * `(args, config?) => ...` where config is a `RunnableConfig`.\n */\nfunction extractPaymentToken(config: unknown): string | null {\n if (config == null || typeof config !== 'object') return null\n\n const configurable = (config as Record<string, unknown>).configurable\n if (configurable == null || typeof configurable !== 'object') return null\n\n const token = (configurable as Record<string, unknown>).payment_token\n return typeof token === 'string' ? token : null\n}\n\n/**\n * Store a value in config.configurable if available.\n */\nfunction storeInConfigurable(config: unknown, key: string, value: unknown): void {\n if (config == null || typeof config !== 'object') return\n\n const configurable = (config as Record<string, unknown>).configurable\n if (configurable == null || typeof configurable !== 'object') return\n ;(configurable as Record<string, unknown>)[key] = value\n}\n\n/**\n * Remove a key from config.configurable if present (no-op otherwise).\n */\nfunction removeFromConfigurable(config: unknown, key: string): void {\n if (config == null || typeof config !== 'object') return\n\n const configurable = (config as Record<string, unknown>).configurable\n if (configurable == null || typeof configurable !== 'object') return\n delete (configurable as Record<string, unknown>)[key]\n}\n\n/**\n * Wraps a LangChain.js tool implementation with x402 payment verification and settlement.\n *\n * This is a higher-order function that takes the tool's implementation function and\n * payment options, returning a new function with the same signature that:\n *\n * 1. Extracts the payment token from `config.configurable.payment_token`\n * 2. Verifies the subscriber has sufficient credits\n * 3. Calls the original tool function\n * 4. Settles (burns) credits\n * 5. Stores `payment_context` and `payment_settlement` in `config.configurable`\n *\n * @param fn - The tool implementation function: `(args, config?) => result`\n * @param options - Payment configuration\n * @returns Wrapped function with the same signature\n *\n * @example Static credits\n * ```typescript\n * const searchData = tool(\n * requiresPayment(\n * (args) => `Results for ${args.query}`,\n * { payments, planId: PLAN_ID, credits: 1 }\n * ),\n * { name: 'search_data', description: 'Search', schema: z.object({ query: z.string() }) }\n * )\n * ```\n *\n * @example Dynamic credits\n * ```typescript\n * const summarize = tool(\n * requiresPayment(\n * (args) => `Summary of ${args.text}`,\n * {\n * payments, planId: PLAN_ID,\n * credits: (ctx) => Math.max(1, Math.floor(String(ctx.result).length / 100)),\n * }\n * ),\n * { name: 'summarize', description: 'Summarize text', schema: z.object({ text: z.string() }) }\n * )\n * ```\n */\nexport function requiresPayment<TArgs extends Record<string, unknown>, TResult>(\n fn: (args: TArgs, config?: unknown) => TResult | Promise<TResult>,\n options: RequiresPaymentOptions,\n): (args: TArgs, config?: unknown) => Promise<TResult> {\n const { payments, planId, credits = 1, agentId, network } = options\n\n return async (args: TArgs, config?: unknown): Promise<TResult> => {\n // Reset the module-level slot at the START of every invocation, before\n // verify. Any failure that does not reach the settle-success write (a\n // verify failure / PaymentRequiredError, or a swallowed settle failure)\n // then leaves lastSettlement() returning `undefined` rather than a stale\n // receipt from a previous invocation — matching the JSDoc contract on\n // lastSettlement().\n lastSettlementReceipt = undefined\n\n // Build payment required object\n const paymentRequired = buildPaymentRequired(planId, {\n endpoint: fn.name || 'tool',\n agentId,\n network,\n })\n // The scheme/network the verify span advertises come from the resolved\n // X402 scheme so the span metadata matches what the buyer paid against.\n const accepted = paymentRequired.accepts[0]\n const planIds = paymentRequired.accepts\n .map((a) => a.planId)\n .filter((p): p is string => Boolean(p))\n const resolvedScheme = accepted?.scheme\n const resolvedNetwork = network ?? accepted?.network\n\n // LangChain auto-captures every key in config.configurable into the parent\n // tool span's metadata, and child spans inherit it at construction time.\n // Strip the full x402 access token from the parent BEFORE opening the verify\n // span so neither the parent nor the child carries the raw credential — only\n // the abbreviated nvm.payment_token remains for correlation.\n const parentRunTree = await activeRunTree()\n redactMetadataKeys(parentRunTree, 'payment_token')\n\n const verifyStarted = Date.now()\n // Open the verify span BEFORE the token-presence check so failed probes\n // (no payment_token in config) still produce a clearly-named span with the\n // static nvm.* attrs attached to both the span and the parent tool span.\n const vspan = await verifySpan({\n planIds,\n scheme: resolvedScheme,\n network: resolvedNetwork,\n agentId,\n })\n // Pre-verify metadata is best-effort and static-only.\n const preVerifyMd = buildVerifyMetadata({\n planIds,\n scheme: resolvedScheme,\n network: resolvedNetwork,\n agentId,\n })\n vspan.addMetadata(preVerifyMd)\n addMetadata(parentRunTree, preVerifyMd)\n\n // Extract token from config.configurable.payment_token\n const token = extractPaymentToken(config)\n if (!token) {\n await vspan.end(new Error('missing payment_token in config.configurable'))\n throw new PaymentRequiredError(\n 'Payment required: missing payment_token in config.configurable',\n paymentRequired,\n )\n }\n\n // Drop the raw token from configurable now that we hold it in `token`.\n // LangChain's `ensureConfig` re-promotes every configurable scalar into a\n // runnable's `metadata` on EACH invocation, so any traced runnable the tool\n // body spawns (an LLM call, a sub-chain) sharing this config would otherwise\n // re-leak the full credential into its own span metadata — and a child run\n // opened *during* `fn()` would capture it before the settle-side\n // `redactMetadataKeys` below could run. Removing the key here is the\n // proactive complement to that reactive redaction. This MUST run after\n // extraction (deleting earlier would make `extractPaymentToken` return null)\n // and before `fn()` — guaranteed, since `fn()` is called further down.\n // Settlement uses the local `token`, never configurable, so removal is\n // non-functional. On the `tool()`/LangGraph path LangChain hands the wrapper\n // a per-invocation config copy, so this does not mutate a config the caller\n // reuses across sibling tools.\n removeFromConfigurable(config, 'payment_token')\n\n // Abbreviate/redact the token ONCE here (mirrors Python's\n // attach_metadata_safely pre-abbreviation) and pass the result into the\n // metadata builders. abbreviateToken is idempotent, so the builders leave\n // it unchanged — this means the short-token warning fires at most once per\n // call (not once per verify AND once per settle), and the raw token never\n // reaches the builders' frame locals (defense against exception enrichers\n // that capture locals).\n const abbreviatedToken = abbreviateToken(token)\n\n // Resolve pre-execution credits (static only; callable deferred to post-execution)\n const creditsToVerify = typeof credits === 'number' ? credits : 1\n\n // Verify permissions\n let verification: VerifyPermissionsResult\n try {\n verification = await payments.facilitator.verifyPermissions({\n paymentRequired,\n x402AccessToken: token,\n maxAmount: BigInt(creditsToVerify),\n })\n } catch (error) {\n await vspan.end(error)\n throw new PaymentRequiredError(\n `Payment verification failed: ${error instanceof Error ? error.message : String(error)}`,\n paymentRequired,\n )\n }\n\n // Augment span metadata with verification results + timing (both span and\n // parent), then close the verify span. Best-effort: a metadata failure must\n // not mask the PaymentRequiredError that may follow.\n const verifyMd = buildVerifyMetadata({\n planIds,\n scheme: resolvedScheme,\n network: resolvedNetwork,\n agentId,\n verification,\n durationMs: Date.now() - verifyStarted,\n token: abbreviatedToken,\n })\n vspan.addMetadata(verifyMd)\n addMetadata(parentRunTree, verifyMd)\n\n if (!verification.isValid) {\n await vspan.end(new Error(verification.invalidReason || 'verification failed'))\n throw new PaymentRequiredError(\n `Payment verification failed: ${verification.invalidReason || 'Insufficient credits or invalid token'}`,\n paymentRequired,\n )\n }\n\n await vspan.end()\n\n // Store payment context. The `token` field carries the ABBREVIATED\n // reference, never the raw credential: `payment_context` is written into\n // `config.configurable`, which LangChain can capture into span metadata, so\n // persisting the full token here would reopen the very leak the parent-tree\n // `redactMetadataKeys('payment_token')` calls close — and a child run opened\n // *during* the tool body would capture it before any post-hoc redaction\n // could run. `abbreviatedToken` is always defined past the `!token` guard\n // above; `?? ''` is a belt-and-suspenders that can never fall back to raw.\n const paymentContext: PaymentContext = {\n token: abbreviatedToken ?? '',\n paymentRequired,\n creditsToSettle: creditsToVerify,\n verified: true,\n agentRequestId: verification.agentRequest?.agentRequestId || verification.agentRequestId,\n agentRequest: verification.agentRequest,\n }\n storeInConfigurable(config, 'payment_context', paymentContext)\n\n // Execute the tool function\n const result = await fn(args, config)\n\n // Resolve final credits (may be dynamic based on result)\n const finalCredits =\n typeof credits === 'function'\n ? credits({ args: args as Record<string, unknown>, result })\n : credits\n\n // Settle credits, wrapped in an nvm:settlement span (mirrors Python's\n // settlement_span around settle_permissions).\n //\n // Re-fetch the active run tree: `fn()` may have opened nested traced runs, so\n // `activeRunTree()` can now return a different RunTree than the verify-side\n // redaction at the top of this function scrubbed. We already removed\n // `payment_token` from `config.configurable` before `fn()` ran, so newly\n // opened child runs can no longer re-promote the raw credential — this\n // re-redaction is now defense-in-depth, covering any RunTree whose metadata\n // was populated before that removal took effect. Redact BEFORE opening the\n // settlement span so the credential never rides into the settle child span or\n // the (possibly new) parent tree.\n const settleParentRunTree = await activeRunTree()\n redactMetadataKeys(settleParentRunTree, 'payment_token')\n const settleStarted = Date.now()\n const sspan = await settlementSpan({ planIds, agentId })\n try {\n const settlement = await payments.facilitator.settlePermissions({\n paymentRequired,\n x402AccessToken: token,\n // A dynamic `credits` callable can return a float (e.g. length/100).\n // `BigInt(1.5)` throws RangeError, which the surrounding try/catch\n // swallows — credits are never burned and the caller is never told.\n // Floor to keep the settle on the money path.\n maxAmount: BigInt(Math.floor(finalCredits)),\n agentRequestId: paymentContext.agentRequestId,\n })\n storeInConfigurable(config, 'payment_settlement', settlement)\n // Also publish to the module-level slot so lastSettlement() can recover\n // the receipt — LangGraph copies config.configurable per node, hiding the\n // line above from the buyer's outer scope.\n lastSettlementReceipt = settlement\n\n const settleMd = buildSettleMetadata({\n settlement,\n planIds,\n agentId,\n durationMs: Date.now() - settleStarted,\n token: abbreviatedToken,\n })\n sspan.addMetadata(settleMd)\n addMetadata(settleParentRunTree, settleMd)\n await sspan.end()\n } catch (settleError) {\n console.error('Payment settlement failed:', settleError)\n await sspan.end(settleError)\n // Still return result even if settlement fails\n }\n\n return result\n }\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * LangChain integration for Nevermined payment protection using the x402 protocol.
3
3
  */
4
- export { requiresPayment, PaymentRequiredError, type RequiresPaymentOptions, type CreditsCallable, type CreditsContext, type PaymentContext, } from './decorator.js';
4
+ export { requiresPayment, lastSettlement, PaymentRequiredError, type RequiresPaymentOptions, type CreditsCallable, type CreditsContext, type PaymentContext, } from './decorator.js';
5
+ export { createPaidReactAgent, type CreatePaidReactAgentOptions, } from './agent.js';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/x402/langchain/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/x402/langchain/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,GACjC,MAAM,YAAY,CAAA"}
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * LangChain integration for Nevermined payment protection using the x402 protocol.
3
3
  */
4
- export { requiresPayment, PaymentRequiredError, } from './decorator.js';
4
+ export { requiresPayment, lastSettlement, PaymentRequiredError, } from './decorator.js';
5
+ export { createPaidReactAgent, } from './agent.js';
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/x402/langchain/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,GAKrB,MAAM,gBAAgB,CAAA","sourcesContent":["/**\n * LangChain integration for Nevermined payment protection using the x402 protocol.\n */\n\nexport {\n requiresPayment,\n PaymentRequiredError,\n type RequiresPaymentOptions,\n type CreditsCallable,\n type CreditsContext,\n type PaymentContext,\n} from './decorator.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/x402/langchain/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,cAAc,EACd,oBAAoB,GAKrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,oBAAoB,GAErB,MAAM,YAAY,CAAA","sourcesContent":["/**\n * LangChain integration for Nevermined payment protection using the x402 protocol.\n */\n\nexport {\n requiresPayment,\n lastSettlement,\n PaymentRequiredError,\n type RequiresPaymentOptions,\n type CreditsCallable,\n type CreditsContext,\n type PaymentContext,\n} from './decorator.js'\nexport {\n createPaidReactAgent,\n type CreatePaidReactAgentOptions,\n} from './agent.js'\n"]}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * LangSmith observability bridge for Nevermined payments (TypeScript).
3
+ *
4
+ * Surfaces the cross-SDK `nvm:verify` / `nvm:settlement` spans defined by the
5
+ * observability-spans-v1 contract, matching the Python SDK attribute-for-
6
+ * attribute so a single LangSmith trace can be correlated across both SDKs.
7
+ *
8
+ * These helpers are wired into the `requiresPayment` decorator automatically;
9
+ * they are also exported here for manual use in non-LangChain code paths.
10
+ *
11
+ * Requires the optional `langsmith` peer dependency and `LANGSMITH_TRACING=true`;
12
+ * every helper no-ops when tracing is inactive or `langsmith` is not installed.
13
+ */
14
+ export { abbreviateToken, activeRunTree, addMetadata, buildSettleMetadata, buildVerifyMetadata, redactMetadataKeys, settlementSpan, verifySpan, NVM_VERIFY_SPAN, NVM_SETTLEMENT_SPAN, type NvmSpan, type SpanMetadata, type VerifyMetadataInput, type SettleMetadataInput, type VerifySpanInput, type SettlementSpanInput, } from './spans.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/x402/langsmith/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,eAAe,EACf,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,eAAe,EACf,mBAAmB,EACnB,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * LangSmith observability bridge for Nevermined payments (TypeScript).
3
+ *
4
+ * Surfaces the cross-SDK `nvm:verify` / `nvm:settlement` spans defined by the
5
+ * observability-spans-v1 contract, matching the Python SDK attribute-for-
6
+ * attribute so a single LangSmith trace can be correlated across both SDKs.
7
+ *
8
+ * These helpers are wired into the `requiresPayment` decorator automatically;
9
+ * they are also exported here for manual use in non-LangChain code paths.
10
+ *
11
+ * Requires the optional `langsmith` peer dependency and `LANGSMITH_TRACING=true`;
12
+ * every helper no-ops when tracing is inactive or `langsmith` is not installed.
13
+ */
14
+ export { abbreviateToken, activeRunTree, addMetadata, buildSettleMetadata, buildVerifyMetadata, redactMetadataKeys, settlementSpan, verifySpan, NVM_VERIFY_SPAN, NVM_SETTLEMENT_SPAN, } from './spans.js';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/x402/langsmith/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,eAAe,EACf,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,eAAe,EACf,mBAAmB,GAOpB,MAAM,YAAY,CAAA","sourcesContent":["/**\n * LangSmith observability bridge for Nevermined payments (TypeScript).\n *\n * Surfaces the cross-SDK `nvm:verify` / `nvm:settlement` spans defined by the\n * observability-spans-v1 contract, matching the Python SDK attribute-for-\n * attribute so a single LangSmith trace can be correlated across both SDKs.\n *\n * These helpers are wired into the `requiresPayment` decorator automatically;\n * they are also exported here for manual use in non-LangChain code paths.\n *\n * Requires the optional `langsmith` peer dependency and `LANGSMITH_TRACING=true`;\n * every helper no-ops when tracing is inactive or `langsmith` is not installed.\n */\n\nexport {\n abbreviateToken,\n activeRunTree,\n addMetadata,\n buildSettleMetadata,\n buildVerifyMetadata,\n redactMetadataKeys,\n settlementSpan,\n verifySpan,\n NVM_VERIFY_SPAN,\n NVM_SETTLEMENT_SPAN,\n type NvmSpan,\n type SpanMetadata,\n type VerifyMetadataInput,\n type SettleMetadataInput,\n type VerifySpanInput,\n type SettlementSpanInput,\n} from './spans.js'\n"]}