@nevermined-io/payments 1.5.0 → 1.6.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.
- package/README.md +121 -93
- package/dist/x402/langchain/agent.d.ts +96 -0
- package/dist/x402/langchain/agent.d.ts.map +1 -0
- package/dist/x402/langchain/agent.js +121 -0
- package/dist/x402/langchain/agent.js.map +1 -0
- package/dist/x402/langchain/decorator.d.ts +43 -4
- package/dist/x402/langchain/decorator.d.ts.map +1 -1
- package/dist/x402/langchain/decorator.js +173 -6
- package/dist/x402/langchain/decorator.js.map +1 -1
- package/dist/x402/langchain/index.d.ts +2 -1
- package/dist/x402/langchain/index.d.ts.map +1 -1
- package/dist/x402/langchain/index.js +2 -1
- package/dist/x402/langchain/index.js.map +1 -1
- package/dist/x402/langsmith/index.d.ts +15 -0
- package/dist/x402/langsmith/index.d.ts.map +1 -0
- package/dist/x402/langsmith/index.js +15 -0
- package/dist/x402/langsmith/index.js.map +1 -0
- package/dist/x402/langsmith/spans.d.ts +163 -0
- package/dist/x402/langsmith/spans.d.ts.map +1 -0
- package/dist/x402/langsmith/spans.js +341 -0
- package/dist/x402/langsmith/spans.js.map +1 -0
- package/package.json +16 -2
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LangSmith span helpers for Nevermined payment events (TypeScript).
|
|
3
|
+
*
|
|
4
|
+
* TS parity port of `payments_py/langsmith/spans.py`. Emits the cross-SDK
|
|
5
|
+
* `nvm:verify` / `nvm:settlement` spans defined by the
|
|
6
|
+
* **observability-spans-v1** contract
|
|
7
|
+
* (`docs/specs/observability-spans-v1.md` in nvm-monorepo), so a single
|
|
8
|
+
* LangSmith trace can be correlated across the TypeScript and Python SDKs
|
|
9
|
+
* (e.g. filtered on `nvm.tx_hash`).
|
|
10
|
+
*
|
|
11
|
+
* All helpers in this module silently no-op when:
|
|
12
|
+
* - the optional `langsmith` JS SDK is not installed; or
|
|
13
|
+
* - no LangSmith run tree is active in the current context (e.g.
|
|
14
|
+
* `LANGSMITH_TRACING` is unset, or the call is not inside a traced run).
|
|
15
|
+
*
|
|
16
|
+
* Failures inside this module never propagate out — observability is
|
|
17
|
+
* best-effort and must not interfere with the payment flow.
|
|
18
|
+
*
|
|
19
|
+
* `langsmith` is imported **lazily** (dynamic `import()`), so users who only
|
|
20
|
+
* use the `requiresPayment` wrapper without tracing are not forced to install
|
|
21
|
+
* it. Install it yourself (`pnpm add langsmith`) and set `LANGSMITH_TRACING=true`
|
|
22
|
+
* to surface these spans.
|
|
23
|
+
*/
|
|
24
|
+
import type { RunTree } from 'langsmith';
|
|
25
|
+
import type { VerifyPermissionsResult, SettlePermissionsResult } from '../facilitator-api.js';
|
|
26
|
+
/** Plain JSON-ish metadata bag attached to a span / run tree. */
|
|
27
|
+
export type SpanMetadata = Record<string, unknown>;
|
|
28
|
+
/** Span names — must match observability-spans-v1 exactly (case-sensitive). */
|
|
29
|
+
export declare const NVM_VERIFY_SPAN = "nvm:verify";
|
|
30
|
+
export declare const NVM_SETTLEMENT_SPAN = "nvm:settlement";
|
|
31
|
+
/**
|
|
32
|
+
* Return the current LangSmith `RunTree`, or `undefined` if none is active or
|
|
33
|
+
* `langsmith` is not installed. Safe to call unconditionally.
|
|
34
|
+
*/
|
|
35
|
+
export declare function activeRunTree(): Promise<RunTree | undefined>;
|
|
36
|
+
/**
|
|
37
|
+
* Merge `metadata` into `runTree`, swallowing any error. No-op if `runTree` is
|
|
38
|
+
* absent or `metadata` is empty. Matches Python's `run_tree.add_metadata`.
|
|
39
|
+
*
|
|
40
|
+
* The merge is performed on THIS side of the boundary — we read the existing
|
|
41
|
+
* `extra.metadata`, spread the new keys over it, then assign — rather than
|
|
42
|
+
* relying on the langsmith `RunTree.metadata` setter to merge. In `langsmith@0.7`
|
|
43
|
+
* the setter does merge (`extra.metadata = { ...existing, ...new }`), but that is
|
|
44
|
+
* an implementation detail of `run_trees.js`, not a documented contract: a future
|
|
45
|
+
* release that flips it to plain assignment would silently drop the static
|
|
46
|
+
* `nvm.*` keys attached on the pre-verify pass once the post-verify pass runs.
|
|
47
|
+
* Reading+merging here keeps the double-pass accumulation correct regardless, and
|
|
48
|
+
* mirrors how {@link redactMetadataKeys} already reaches into `extra.metadata`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function addMetadata(runTree: RunTree | undefined, metadata: SpanMetadata): void;
|
|
51
|
+
/**
|
|
52
|
+
* Remove `keys` from `runTree`'s metadata in place.
|
|
53
|
+
*
|
|
54
|
+
* LangSmith inherits a parent run's metadata into child runs created via
|
|
55
|
+
* `createChild`, so call this on the PARENT run BEFORE opening any child span
|
|
56
|
+
* whose metadata should not carry the keys. The most common use is stripping
|
|
57
|
+
* the full `payment_token` from the parent tool span's metadata, since the raw
|
|
58
|
+
* access token grants access to the protected tool until it expires.
|
|
59
|
+
*
|
|
60
|
+
* No-op when `runTree` is absent or no keys are provided. Errors are swallowed
|
|
61
|
+
* so observability never disrupts the payment flow — but, unlike the other
|
|
62
|
+
* swallow paths, a failure here is **security-sensitive**: if the parent run
|
|
63
|
+
* tree still holds the raw `payment_token`, that credential rides into the
|
|
64
|
+
* parent tree and the inherited verify/settle child spans. So this path warns
|
|
65
|
+
* (not just debug) to keep the leak diagnosable.
|
|
66
|
+
*/
|
|
67
|
+
export declare function redactMetadataKeys(runTree: RunTree | undefined, ...keys: string[]): void;
|
|
68
|
+
/**
|
|
69
|
+
* Return a short, non-functional reference to a payment token for span
|
|
70
|
+
* metadata. Mirrors `payments_py/langsmith/spans.py::abbreviate_token` and the
|
|
71
|
+
* redaction contract in nvm-monorepo#1746 §4 (short-token hardening from
|
|
72
|
+
* nvm-monorepo#1747, tracked in payments-py PR #217).
|
|
73
|
+
*
|
|
74
|
+
* - `undefined`/empty input → `undefined` (and silent — no token at all is not
|
|
75
|
+
* a "wrong token" mistake).
|
|
76
|
+
* - Already-redacted input (a genuine `<prefix>…(short)` marker) → returned
|
|
77
|
+
* unchanged and silent, so the helper stays idempotent (the decorator path
|
|
78
|
+
* abbreviates the same token more than once). A raw value that merely *ends*
|
|
79
|
+
* in the marker is NOT treated as redacted (see {@link isRedactedMarker}).
|
|
80
|
+
* - A token of length ≤ 20 is almost always a misconfiguration (a plan id, an
|
|
81
|
+
* opaque handle, etc. passed where the JWT was expected). Because this helper
|
|
82
|
+
* exists to keep credentials out of a durable trace store, such tokens are
|
|
83
|
+
* **redacted, not exported**: at most the first 4 chars are revealed (to aid
|
|
84
|
+
* debugging the misconfig), followed by the `…(short)` marker — and for a
|
|
85
|
+
* token of 4 chars or fewer **nothing** is revealed (the whole value would
|
|
86
|
+
* otherwise be the "prefix"), so it collapses to just `…(short)`. A warning
|
|
87
|
+
* is emitted. The full short value never leaves this function.
|
|
88
|
+
* - Otherwise (a normal JWT, more than 20 chars) → `<first 16>…<last 4>`.
|
|
89
|
+
*/
|
|
90
|
+
export declare function abbreviateToken(token: string | undefined | null): string | undefined;
|
|
91
|
+
/** Inputs accepted by {@link buildVerifyMetadata}. */
|
|
92
|
+
export interface VerifyMetadataInput {
|
|
93
|
+
planIds: string[];
|
|
94
|
+
scheme?: string;
|
|
95
|
+
network?: string;
|
|
96
|
+
agentId?: string;
|
|
97
|
+
verification?: VerifyPermissionsResult;
|
|
98
|
+
durationMs?: number;
|
|
99
|
+
token?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Build the `nvm.*` metadata for a verify span. Drops absent values (a key is
|
|
103
|
+
* omitted, never set to null/undefined). Matches observability-spans-v1 §2.
|
|
104
|
+
*
|
|
105
|
+
* `token` is abbreviated/redacted via {@link abbreviateToken} before being
|
|
106
|
+
* surfaced as `nvm.payment_token` so the full credential never lands in
|
|
107
|
+
* metadata we control.
|
|
108
|
+
*/
|
|
109
|
+
export declare function buildVerifyMetadata(input: VerifyMetadataInput): SpanMetadata;
|
|
110
|
+
/** Inputs accepted by {@link buildSettleMetadata}. */
|
|
111
|
+
export interface SettleMetadataInput {
|
|
112
|
+
settlement: SettlePermissionsResult;
|
|
113
|
+
planIds: string[];
|
|
114
|
+
agentId?: string;
|
|
115
|
+
durationMs?: number;
|
|
116
|
+
token?: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Build the `nvm.*` metadata for a settlement span. Drops absent values.
|
|
120
|
+
* Matches observability-spans-v1 §3.
|
|
121
|
+
*
|
|
122
|
+
* Note the types preserved exactly per the spec: `nvm.credits_redeemed`
|
|
123
|
+
* (←`creditsRedeemed`) and `nvm.balance.after` (←`remainingBalance`) are
|
|
124
|
+
* STRINGS — they are not coerced to numbers. `nvm.tx_hash` ← `transaction`.
|
|
125
|
+
*/
|
|
126
|
+
export declare function buildSettleMetadata(input: SettleMetadataInput): SpanMetadata;
|
|
127
|
+
/**
|
|
128
|
+
* An opened Nevermined span. `end()` records `outputs`/`error` and flushes the
|
|
129
|
+
* child run to LangSmith. Always safe to call (no-op when `runTree` is absent).
|
|
130
|
+
*/
|
|
131
|
+
export interface NvmSpan {
|
|
132
|
+
/** The underlying child `RunTree`, or `undefined` when tracing is inactive. */
|
|
133
|
+
readonly runTree: RunTree | undefined;
|
|
134
|
+
/** Attach `nvm.*` metadata to this span. */
|
|
135
|
+
addMetadata(metadata: SpanMetadata): void;
|
|
136
|
+
/** Close the span, flushing it to LangSmith. Optionally record an error. */
|
|
137
|
+
end(error?: unknown): Promise<void>;
|
|
138
|
+
}
|
|
139
|
+
/** Inputs accepted by {@link verifySpan}. */
|
|
140
|
+
export interface VerifySpanInput {
|
|
141
|
+
planIds: string[];
|
|
142
|
+
scheme?: string;
|
|
143
|
+
network?: string;
|
|
144
|
+
agentId?: string;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Open an `nvm:verify` child span around a verify call. Returns an
|
|
148
|
+
* {@link NvmSpan} whose `end()` must be called once the verify completes (or
|
|
149
|
+
* throws). Always safe — a no-op span is returned when tracing is inactive or
|
|
150
|
+
* `langsmith` is not installed.
|
|
151
|
+
*/
|
|
152
|
+
export declare function verifySpan(input: VerifySpanInput): Promise<NvmSpan>;
|
|
153
|
+
/** Inputs accepted by {@link settlementSpan}. */
|
|
154
|
+
export interface SettlementSpanInput {
|
|
155
|
+
planIds: string[];
|
|
156
|
+
agentId?: string;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Open an `nvm:settlement` child span around a settle call. Same semantics as
|
|
160
|
+
* {@link verifySpan}.
|
|
161
|
+
*/
|
|
162
|
+
export declare function settlementSpan(input: SettlementSpanInput): Promise<NvmSpan>;
|
|
163
|
+
//# sourceMappingURL=spans.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spans.d.ts","sourceRoot":"","sources":["../../../src/x402/langsmith/spans.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAE7F,iEAAiE;AACjE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAElD,+EAA+E;AAC/E,eAAO,MAAM,eAAe,eAAe,CAAA;AAC3C,eAAO,MAAM,mBAAmB,mBAAmB,CAAA;AA4EnD;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAYlE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI,CAUtF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAexF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAoBpF;AAED,sDAAsD;AACtD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,uBAAuB,CAAA;IACtC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,GAAG,YAAY,CAe5E;AAED,sDAAsD;AACtD,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,uBAAuB,CAAA;IACnC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,GAAG,YAAY,CAa5E;AAOD;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAA;IACrC,4CAA4C;IAC5C,WAAW,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IACzC,4EAA4E;IAC5E,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACpC;AA6CD,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAOzE;AAED,iDAAiD;AACjD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAKjF"}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LangSmith span helpers for Nevermined payment events (TypeScript).
|
|
3
|
+
*
|
|
4
|
+
* TS parity port of `payments_py/langsmith/spans.py`. Emits the cross-SDK
|
|
5
|
+
* `nvm:verify` / `nvm:settlement` spans defined by the
|
|
6
|
+
* **observability-spans-v1** contract
|
|
7
|
+
* (`docs/specs/observability-spans-v1.md` in nvm-monorepo), so a single
|
|
8
|
+
* LangSmith trace can be correlated across the TypeScript and Python SDKs
|
|
9
|
+
* (e.g. filtered on `nvm.tx_hash`).
|
|
10
|
+
*
|
|
11
|
+
* All helpers in this module silently no-op when:
|
|
12
|
+
* - the optional `langsmith` JS SDK is not installed; or
|
|
13
|
+
* - no LangSmith run tree is active in the current context (e.g.
|
|
14
|
+
* `LANGSMITH_TRACING` is unset, or the call is not inside a traced run).
|
|
15
|
+
*
|
|
16
|
+
* Failures inside this module never propagate out — observability is
|
|
17
|
+
* best-effort and must not interfere with the payment flow.
|
|
18
|
+
*
|
|
19
|
+
* `langsmith` is imported **lazily** (dynamic `import()`), so users who only
|
|
20
|
+
* use the `requiresPayment` wrapper without tracing are not forced to install
|
|
21
|
+
* it. Install it yourself (`pnpm add langsmith`) and set `LANGSMITH_TRACING=true`
|
|
22
|
+
* to surface these spans.
|
|
23
|
+
*/
|
|
24
|
+
/** Span names — must match observability-spans-v1 exactly (case-sensitive). */
|
|
25
|
+
export const NVM_VERIFY_SPAN = 'nvm:verify';
|
|
26
|
+
export const NVM_SETTLEMENT_SPAN = 'nvm:settlement';
|
|
27
|
+
/**
|
|
28
|
+
* Marker appended to a redacted short token. The joining character is the
|
|
29
|
+
* single Unicode ellipsis `…` (U+2026), matching the Python implementation
|
|
30
|
+
* and the spec, NOT three ASCII dots. (`…` is one UTF-16 code unit, so this
|
|
31
|
+
* string has length 8.)
|
|
32
|
+
*/
|
|
33
|
+
const SHORT_TOKEN_MARKER = '…(short)';
|
|
34
|
+
/**
|
|
35
|
+
* A redacted short token is exactly `<prefix><marker>` where `prefix` is either
|
|
36
|
+
* empty (raw length 4 or fewer) or the first 4 chars (raw length over 4) — so a genuine
|
|
37
|
+
* marker only ever has one of these two lengths. Recognising it by both suffix
|
|
38
|
+
* AND an exact length stops a raw ≤20-char value that merely *ends* in the
|
|
39
|
+
* marker (e.g. `"x…(short)"`) from slipping through verbatim.
|
|
40
|
+
*/
|
|
41
|
+
const REDACTED_MARKER_LENGTHS = new Set([SHORT_TOKEN_MARKER.length, 4 + SHORT_TOKEN_MARKER.length]);
|
|
42
|
+
/** True if `token` is already a value produced by the short-token branch. */
|
|
43
|
+
function isRedactedMarker(token) {
|
|
44
|
+
return token.endsWith(SHORT_TOKEN_MARKER) && REDACTED_MARKER_LENGTHS.has(token.length);
|
|
45
|
+
}
|
|
46
|
+
let cachedLangsmith;
|
|
47
|
+
/** True when the dynamic-import error means the package is simply not installed. */
|
|
48
|
+
function isModuleNotFound(err) {
|
|
49
|
+
const code = err?.code;
|
|
50
|
+
return code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND';
|
|
51
|
+
}
|
|
52
|
+
async function loadLangsmith() {
|
|
53
|
+
if (cachedLangsmith !== undefined)
|
|
54
|
+
return cachedLangsmith;
|
|
55
|
+
try {
|
|
56
|
+
// `getCurrentRunTree` is NOT exported from the `langsmith` root entry point —
|
|
57
|
+
// it lives exclusively at the `langsmith/singletons/traceable` sub-path (true
|
|
58
|
+
// across every published 0.x). Importing the root and reading
|
|
59
|
+
// `mod.getCurrentRunTree` always yields `undefined`, which would disable
|
|
60
|
+
// every span in production while the mocked unit tests stay green. Import the
|
|
61
|
+
// sub-path directly. (See the un-mocked smoke test in
|
|
62
|
+
// `langsmith-real-sdk.test.ts`, which fails loudly if this ever moves again.)
|
|
63
|
+
const mod = (await import('langsmith/singletons/traceable'));
|
|
64
|
+
if (typeof mod?.getCurrentRunTree === 'function') {
|
|
65
|
+
cachedLangsmith = mod;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Installed but missing the API we rely on — a real, diagnosable problem,
|
|
69
|
+
// distinct from "not installed". Warn once (cached null prevents repeats).
|
|
70
|
+
cachedLangsmith = null;
|
|
71
|
+
console.warn('nvm-langsmith: `langsmith` is installed but `langsmith/singletons/traceable` ' +
|
|
72
|
+
'does not export getCurrentRunTree; Nevermined spans are disabled.');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
cachedLangsmith = null;
|
|
77
|
+
// "Not installed" is the expected optional-peer path → stay silent. Any
|
|
78
|
+
// other failure (a broken install, a transitive load error) disables all
|
|
79
|
+
// spans with no other signal, so surface it once.
|
|
80
|
+
if (!isModuleNotFound(err)) {
|
|
81
|
+
console.warn('nvm-langsmith: failed to load `langsmith`; spans disabled', err);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return cachedLangsmith;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Return the current LangSmith `RunTree`, or `undefined` if none is active or
|
|
88
|
+
* `langsmith` is not installed. Safe to call unconditionally.
|
|
89
|
+
*/
|
|
90
|
+
export async function activeRunTree() {
|
|
91
|
+
const ls = await loadLangsmith();
|
|
92
|
+
if (ls === null)
|
|
93
|
+
return undefined;
|
|
94
|
+
try {
|
|
95
|
+
// `permitAbsentRunTree: true` returns undefined instead of throwing when
|
|
96
|
+
// there is no active run, mirroring Python's `get_current_run_tree()` used
|
|
97
|
+
// behind a try/except.
|
|
98
|
+
return ls.getCurrentRunTree(true);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
console.debug('nvm-langsmith: getCurrentRunTree failed (ignored)', err);
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Merge `metadata` into `runTree`, swallowing any error. No-op if `runTree` is
|
|
107
|
+
* absent or `metadata` is empty. Matches Python's `run_tree.add_metadata`.
|
|
108
|
+
*
|
|
109
|
+
* The merge is performed on THIS side of the boundary — we read the existing
|
|
110
|
+
* `extra.metadata`, spread the new keys over it, then assign — rather than
|
|
111
|
+
* relying on the langsmith `RunTree.metadata` setter to merge. In `langsmith@0.7`
|
|
112
|
+
* the setter does merge (`extra.metadata = { ...existing, ...new }`), but that is
|
|
113
|
+
* an implementation detail of `run_trees.js`, not a documented contract: a future
|
|
114
|
+
* release that flips it to plain assignment would silently drop the static
|
|
115
|
+
* `nvm.*` keys attached on the pre-verify pass once the post-verify pass runs.
|
|
116
|
+
* Reading+merging here keeps the double-pass accumulation correct regardless, and
|
|
117
|
+
* mirrors how {@link redactMetadataKeys} already reaches into `extra.metadata`.
|
|
118
|
+
*/
|
|
119
|
+
export function addMetadata(runTree, metadata) {
|
|
120
|
+
if (!runTree || !metadata || Object.keys(metadata).length === 0)
|
|
121
|
+
return;
|
|
122
|
+
try {
|
|
123
|
+
const rt = runTree;
|
|
124
|
+
const existing = rt.extra?.metadata ?? {};
|
|
125
|
+
runTree.metadata = { ...existing, ...metadata };
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
// observability hygiene must never disrupt the payment flow
|
|
129
|
+
console.debug('nvm-langsmith: addMetadata failed (ignored)', err);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Remove `keys` from `runTree`'s metadata in place.
|
|
134
|
+
*
|
|
135
|
+
* LangSmith inherits a parent run's metadata into child runs created via
|
|
136
|
+
* `createChild`, so call this on the PARENT run BEFORE opening any child span
|
|
137
|
+
* whose metadata should not carry the keys. The most common use is stripping
|
|
138
|
+
* the full `payment_token` from the parent tool span's metadata, since the raw
|
|
139
|
+
* access token grants access to the protected tool until it expires.
|
|
140
|
+
*
|
|
141
|
+
* No-op when `runTree` is absent or no keys are provided. Errors are swallowed
|
|
142
|
+
* so observability never disrupts the payment flow — but, unlike the other
|
|
143
|
+
* swallow paths, a failure here is **security-sensitive**: if the parent run
|
|
144
|
+
* tree still holds the raw `payment_token`, that credential rides into the
|
|
145
|
+
* parent tree and the inherited verify/settle child spans. So this path warns
|
|
146
|
+
* (not just debug) to keep the leak diagnosable.
|
|
147
|
+
*/
|
|
148
|
+
export function redactMetadataKeys(runTree, ...keys) {
|
|
149
|
+
if (!runTree || keys.length === 0)
|
|
150
|
+
return;
|
|
151
|
+
try {
|
|
152
|
+
const extra = runTree.extra;
|
|
153
|
+
const metadata = extra?.metadata;
|
|
154
|
+
if (metadata && typeof metadata === 'object') {
|
|
155
|
+
for (const key of keys)
|
|
156
|
+
delete metadata[key];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
console.warn(`nvm-langsmith: failed to redact metadata keys [${keys.join(', ')}] from ` +
|
|
161
|
+
'the parent run tree — a raw credential may remain in the trace', err);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Return a short, non-functional reference to a payment token for span
|
|
166
|
+
* metadata. Mirrors `payments_py/langsmith/spans.py::abbreviate_token` and the
|
|
167
|
+
* redaction contract in nvm-monorepo#1746 §4 (short-token hardening from
|
|
168
|
+
* nvm-monorepo#1747, tracked in payments-py PR #217).
|
|
169
|
+
*
|
|
170
|
+
* - `undefined`/empty input → `undefined` (and silent — no token at all is not
|
|
171
|
+
* a "wrong token" mistake).
|
|
172
|
+
* - Already-redacted input (a genuine `<prefix>…(short)` marker) → returned
|
|
173
|
+
* unchanged and silent, so the helper stays idempotent (the decorator path
|
|
174
|
+
* abbreviates the same token more than once). A raw value that merely *ends*
|
|
175
|
+
* in the marker is NOT treated as redacted (see {@link isRedactedMarker}).
|
|
176
|
+
* - A token of length ≤ 20 is almost always a misconfiguration (a plan id, an
|
|
177
|
+
* opaque handle, etc. passed where the JWT was expected). Because this helper
|
|
178
|
+
* exists to keep credentials out of a durable trace store, such tokens are
|
|
179
|
+
* **redacted, not exported**: at most the first 4 chars are revealed (to aid
|
|
180
|
+
* debugging the misconfig), followed by the `…(short)` marker — and for a
|
|
181
|
+
* token of 4 chars or fewer **nothing** is revealed (the whole value would
|
|
182
|
+
* otherwise be the "prefix"), so it collapses to just `…(short)`. A warning
|
|
183
|
+
* is emitted. The full short value never leaves this function.
|
|
184
|
+
* - Otherwise (a normal JWT, more than 20 chars) → `<first 16>…<last 4>`.
|
|
185
|
+
*/
|
|
186
|
+
export function abbreviateToken(token) {
|
|
187
|
+
if (!token)
|
|
188
|
+
return undefined;
|
|
189
|
+
if (isRedactedMarker(token)) {
|
|
190
|
+
// Already redacted (re-applied on the decorator path); re-slicing would let
|
|
191
|
+
// the marker drift, so return unchanged and stay silent — the original
|
|
192
|
+
// short value already triggered the warning.
|
|
193
|
+
return token;
|
|
194
|
+
}
|
|
195
|
+
if (token.length <= 20) {
|
|
196
|
+
console.warn('abbreviateToken: token is 20 characters or fewer — was the right x402 ' +
|
|
197
|
+
'access token passed? Short/non-JWT tokens are almost always a ' +
|
|
198
|
+
'misconfiguration and are redacted (not exported).');
|
|
199
|
+
// Reveal at most 4 chars; for a ≤4-char token reveal nothing, since
|
|
200
|
+
// token.slice(0, 4) would be the entire value — defeating the redaction.
|
|
201
|
+
const prefix = token.length > 4 ? token.slice(0, 4) : '';
|
|
202
|
+
return `${prefix}${SHORT_TOKEN_MARKER}`;
|
|
203
|
+
}
|
|
204
|
+
return `${token.slice(0, 16)}…${token.slice(-4)}`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Build the `nvm.*` metadata for a verify span. Drops absent values (a key is
|
|
208
|
+
* omitted, never set to null/undefined). Matches observability-spans-v1 §2.
|
|
209
|
+
*
|
|
210
|
+
* `token` is abbreviated/redacted via {@link abbreviateToken} before being
|
|
211
|
+
* surfaced as `nvm.payment_token` so the full credential never lands in
|
|
212
|
+
* metadata we control.
|
|
213
|
+
*/
|
|
214
|
+
export function buildVerifyMetadata(input) {
|
|
215
|
+
const { planIds, scheme, network, agentId, verification, durationMs, token } = input;
|
|
216
|
+
const md = { 'nvm.plan_ids': [...planIds] };
|
|
217
|
+
if (scheme)
|
|
218
|
+
md['nvm.scheme'] = scheme;
|
|
219
|
+
if (network)
|
|
220
|
+
md['nvm.network'] = network;
|
|
221
|
+
if (agentId)
|
|
222
|
+
md['nvm.agent_id'] = agentId;
|
|
223
|
+
if (durationMs !== undefined)
|
|
224
|
+
md['nvm.verify.duration_ms'] = round2(durationMs);
|
|
225
|
+
const abbreviated = abbreviateToken(token);
|
|
226
|
+
if (abbreviated)
|
|
227
|
+
md['nvm.payment_token'] = abbreviated;
|
|
228
|
+
if (verification) {
|
|
229
|
+
if (verification.payer)
|
|
230
|
+
md['nvm.payer'] = verification.payer;
|
|
231
|
+
if (verification.network && !('nvm.network' in md))
|
|
232
|
+
md['nvm.network'] = verification.network;
|
|
233
|
+
if (verification.agentRequestId)
|
|
234
|
+
md['nvm.agent_request_id'] = verification.agentRequestId;
|
|
235
|
+
}
|
|
236
|
+
return md;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Build the `nvm.*` metadata for a settlement span. Drops absent values.
|
|
240
|
+
* Matches observability-spans-v1 §3.
|
|
241
|
+
*
|
|
242
|
+
* Note the types preserved exactly per the spec: `nvm.credits_redeemed`
|
|
243
|
+
* (←`creditsRedeemed`) and `nvm.balance.after` (←`remainingBalance`) are
|
|
244
|
+
* STRINGS — they are not coerced to numbers. `nvm.tx_hash` ← `transaction`.
|
|
245
|
+
*/
|
|
246
|
+
export function buildSettleMetadata(input) {
|
|
247
|
+
const { settlement, planIds, agentId, durationMs, token } = input;
|
|
248
|
+
const md = { 'nvm.plan_ids': [...planIds] };
|
|
249
|
+
if (agentId)
|
|
250
|
+
md['nvm.agent_id'] = agentId;
|
|
251
|
+
if (durationMs !== undefined)
|
|
252
|
+
md['nvm.settle.duration_ms'] = round2(durationMs);
|
|
253
|
+
const abbreviated = abbreviateToken(token);
|
|
254
|
+
if (abbreviated)
|
|
255
|
+
md['nvm.payment_token'] = abbreviated;
|
|
256
|
+
if (settlement.creditsRedeemed != null)
|
|
257
|
+
md['nvm.credits_redeemed'] = settlement.creditsRedeemed;
|
|
258
|
+
if (settlement.remainingBalance != null)
|
|
259
|
+
md['nvm.balance.after'] = settlement.remainingBalance;
|
|
260
|
+
if (settlement.transaction)
|
|
261
|
+
md['nvm.tx_hash'] = settlement.transaction;
|
|
262
|
+
if (settlement.network)
|
|
263
|
+
md['nvm.network'] = settlement.network;
|
|
264
|
+
if (settlement.payer)
|
|
265
|
+
md['nvm.payer'] = settlement.payer;
|
|
266
|
+
return md;
|
|
267
|
+
}
|
|
268
|
+
/** Round to 2 decimals, matching Python's `round(value, 2)`. */
|
|
269
|
+
function round2(value) {
|
|
270
|
+
return Math.round(value * 100) / 100;
|
|
271
|
+
}
|
|
272
|
+
async function openNvmSpan(name, inputs) {
|
|
273
|
+
const parent = await activeRunTree();
|
|
274
|
+
if (!parent)
|
|
275
|
+
return inactiveSpan();
|
|
276
|
+
let child;
|
|
277
|
+
try {
|
|
278
|
+
child = parent.createChild({ name, run_type: 'tool', inputs });
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
// span setup is pure observability — never let it disrupt the payment flow
|
|
282
|
+
console.debug(`nvm-langsmith: createChild(${name}) failed (ignored)`, err);
|
|
283
|
+
return inactiveSpan();
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
runTree: child,
|
|
287
|
+
addMetadata(metadata) {
|
|
288
|
+
addMetadata(child, metadata);
|
|
289
|
+
},
|
|
290
|
+
async end(error) {
|
|
291
|
+
try {
|
|
292
|
+
await child.end(undefined, error === undefined ? undefined : error instanceof Error ? error.message : String(error));
|
|
293
|
+
await child.postRun();
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
// best-effort flush
|
|
297
|
+
console.debug(`nvm-langsmith: ${name} span flush failed (ignored)`, err);
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function inactiveSpan() {
|
|
303
|
+
return {
|
|
304
|
+
runTree: undefined,
|
|
305
|
+
addMetadata() {
|
|
306
|
+
/* no-op */
|
|
307
|
+
},
|
|
308
|
+
async end() {
|
|
309
|
+
/* no-op */
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Open an `nvm:verify` child span around a verify call. Returns an
|
|
315
|
+
* {@link NvmSpan} whose `end()` must be called once the verify completes (or
|
|
316
|
+
* throws). Always safe — a no-op span is returned when tracing is inactive or
|
|
317
|
+
* `langsmith` is not installed.
|
|
318
|
+
*/
|
|
319
|
+
export async function verifySpan(input) {
|
|
320
|
+
const { planIds, scheme, network, agentId } = input;
|
|
321
|
+
const inputs = { plan_ids: [...planIds] };
|
|
322
|
+
if (scheme)
|
|
323
|
+
inputs.scheme = scheme;
|
|
324
|
+
if (network)
|
|
325
|
+
inputs.network = network;
|
|
326
|
+
if (agentId)
|
|
327
|
+
inputs.agent_id = agentId;
|
|
328
|
+
return openNvmSpan(NVM_VERIFY_SPAN, inputs);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Open an `nvm:settlement` child span around a settle call. Same semantics as
|
|
332
|
+
* {@link verifySpan}.
|
|
333
|
+
*/
|
|
334
|
+
export async function settlementSpan(input) {
|
|
335
|
+
const { planIds, agentId } = input;
|
|
336
|
+
const inputs = { plan_ids: [...planIds] };
|
|
337
|
+
if (agentId)
|
|
338
|
+
inputs.agent_id = agentId;
|
|
339
|
+
return openNvmSpan(NVM_SETTLEMENT_SPAN, inputs);
|
|
340
|
+
}
|
|
341
|
+
//# sourceMappingURL=spans.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spans.js","sourceRoot":"","sources":["../../../src/x402/langsmith/spans.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * LangSmith span helpers for Nevermined payment events (TypeScript).\n *\n * TS parity port of `payments_py/langsmith/spans.py`. Emits the cross-SDK\n * `nvm:verify` / `nvm:settlement` spans defined by the\n * **observability-spans-v1** contract\n * (`docs/specs/observability-spans-v1.md` in nvm-monorepo), so a single\n * LangSmith trace can be correlated across the TypeScript and Python SDKs\n * (e.g. filtered on `nvm.tx_hash`).\n *\n * All helpers in this module silently no-op when:\n * - the optional `langsmith` JS SDK is not installed; or\n * - no LangSmith run tree is active in the current context (e.g.\n * `LANGSMITH_TRACING` is unset, or the call is not inside a traced run).\n *\n * Failures inside this module never propagate out — observability is\n * best-effort and must not interfere with the payment flow.\n *\n * `langsmith` is imported **lazily** (dynamic `import()`), so users who only\n * use the `requiresPayment` wrapper without tracing are not forced to install\n * it. Install it yourself (`pnpm add langsmith`) and set `LANGSMITH_TRACING=true`\n * to surface these spans.\n */\n\nimport type { RunTree } from 'langsmith'\nimport type { VerifyPermissionsResult, SettlePermissionsResult } from '../facilitator-api.js'\n\n/** Plain JSON-ish metadata bag attached to a span / run tree. */\nexport type SpanMetadata = Record<string, unknown>\n\n/** Span names — must match observability-spans-v1 exactly (case-sensitive). */\nexport const NVM_VERIFY_SPAN = 'nvm:verify'\nexport const NVM_SETTLEMENT_SPAN = 'nvm:settlement'\n\n/**\n * Marker appended to a redacted short token. The joining character is the\n * single Unicode ellipsis `…` (U+2026), matching the Python implementation\n * and the spec, NOT three ASCII dots. (`…` is one UTF-16 code unit, so this\n * string has length 8.)\n */\nconst SHORT_TOKEN_MARKER = '…(short)'\n\n/**\n * A redacted short token is exactly `<prefix><marker>` where `prefix` is either\n * empty (raw length 4 or fewer) or the first 4 chars (raw length over 4) — so a genuine\n * marker only ever has one of these two lengths. Recognising it by both suffix\n * AND an exact length stops a raw ≤20-char value that merely *ends* in the\n * marker (e.g. `\"x…(short)\"`) from slipping through verbatim.\n */\nconst REDACTED_MARKER_LENGTHS = new Set([SHORT_TOKEN_MARKER.length, 4 + SHORT_TOKEN_MARKER.length])\n\n/** True if `token` is already a value produced by the short-token branch. */\nfunction isRedactedMarker(token: string): boolean {\n return token.endsWith(SHORT_TOKEN_MARKER) && REDACTED_MARKER_LENGTHS.has(token.length)\n}\n\n/**\n * Cached result of the lazy `langsmith` import.\n *\n * `undefined` = not yet attempted; `null` = attempted and unavailable (so we\n * never retry the dynamic import on every call); otherwise the resolved module\n * surface we use (`getCurrentRunTree`).\n */\ntype LangsmithModule = {\n getCurrentRunTree: (permitAbsentRunTree: boolean) => RunTree | undefined\n}\nlet cachedLangsmith: LangsmithModule | null | undefined\n\n/** True when the dynamic-import error means the package is simply not installed. */\nfunction isModuleNotFound(err: unknown): boolean {\n const code = (err as { code?: string } | null)?.code\n return code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND'\n}\n\nasync function loadLangsmith(): Promise<LangsmithModule | null> {\n if (cachedLangsmith !== undefined) return cachedLangsmith\n try {\n // `getCurrentRunTree` is NOT exported from the `langsmith` root entry point —\n // it lives exclusively at the `langsmith/singletons/traceable` sub-path (true\n // across every published 0.x). Importing the root and reading\n // `mod.getCurrentRunTree` always yields `undefined`, which would disable\n // every span in production while the mocked unit tests stay green. Import the\n // sub-path directly. (See the un-mocked smoke test in\n // `langsmith-real-sdk.test.ts`, which fails loudly if this ever moves again.)\n const mod = (await import('langsmith/singletons/traceable')) as unknown as LangsmithModule\n if (typeof mod?.getCurrentRunTree === 'function') {\n cachedLangsmith = mod\n } else {\n // Installed but missing the API we rely on — a real, diagnosable problem,\n // distinct from \"not installed\". Warn once (cached null prevents repeats).\n cachedLangsmith = null\n console.warn(\n 'nvm-langsmith: `langsmith` is installed but `langsmith/singletons/traceable` ' +\n 'does not export getCurrentRunTree; Nevermined spans are disabled.',\n )\n }\n } catch (err) {\n cachedLangsmith = null\n // \"Not installed\" is the expected optional-peer path → stay silent. Any\n // other failure (a broken install, a transitive load error) disables all\n // spans with no other signal, so surface it once.\n if (!isModuleNotFound(err)) {\n console.warn('nvm-langsmith: failed to load `langsmith`; spans disabled', err)\n }\n }\n return cachedLangsmith\n}\n\n/**\n * Return the current LangSmith `RunTree`, or `undefined` if none is active or\n * `langsmith` is not installed. Safe to call unconditionally.\n */\nexport async function activeRunTree(): Promise<RunTree | undefined> {\n const ls = await loadLangsmith()\n if (ls === null) return undefined\n try {\n // `permitAbsentRunTree: true` returns undefined instead of throwing when\n // there is no active run, mirroring Python's `get_current_run_tree()` used\n // behind a try/except.\n return ls.getCurrentRunTree(true)\n } catch (err) {\n console.debug('nvm-langsmith: getCurrentRunTree failed (ignored)', err)\n return undefined\n }\n}\n\n/**\n * Merge `metadata` into `runTree`, swallowing any error. No-op if `runTree` is\n * absent or `metadata` is empty. Matches Python's `run_tree.add_metadata`.\n *\n * The merge is performed on THIS side of the boundary — we read the existing\n * `extra.metadata`, spread the new keys over it, then assign — rather than\n * relying on the langsmith `RunTree.metadata` setter to merge. In `langsmith@0.7`\n * the setter does merge (`extra.metadata = { ...existing, ...new }`), but that is\n * an implementation detail of `run_trees.js`, not a documented contract: a future\n * release that flips it to plain assignment would silently drop the static\n * `nvm.*` keys attached on the pre-verify pass once the post-verify pass runs.\n * Reading+merging here keeps the double-pass accumulation correct regardless, and\n * mirrors how {@link redactMetadataKeys} already reaches into `extra.metadata`.\n */\nexport function addMetadata(runTree: RunTree | undefined, metadata: SpanMetadata): void {\n if (!runTree || !metadata || Object.keys(metadata).length === 0) return\n try {\n const rt = runTree as unknown as { extra?: Record<string, unknown> }\n const existing = (rt.extra?.metadata as Record<string, unknown> | undefined) ?? {}\n runTree.metadata = { ...existing, ...metadata }\n } catch (err) {\n // observability hygiene must never disrupt the payment flow\n console.debug('nvm-langsmith: addMetadata failed (ignored)', err)\n }\n}\n\n/**\n * Remove `keys` from `runTree`'s metadata in place.\n *\n * LangSmith inherits a parent run's metadata into child runs created via\n * `createChild`, so call this on the PARENT run BEFORE opening any child span\n * whose metadata should not carry the keys. The most common use is stripping\n * the full `payment_token` from the parent tool span's metadata, since the raw\n * access token grants access to the protected tool until it expires.\n *\n * No-op when `runTree` is absent or no keys are provided. Errors are swallowed\n * so observability never disrupts the payment flow — but, unlike the other\n * swallow paths, a failure here is **security-sensitive**: if the parent run\n * tree still holds the raw `payment_token`, that credential rides into the\n * parent tree and the inherited verify/settle child spans. So this path warns\n * (not just debug) to keep the leak diagnosable.\n */\nexport function redactMetadataKeys(runTree: RunTree | undefined, ...keys: string[]): void {\n if (!runTree || keys.length === 0) return\n try {\n const extra = (runTree as unknown as { extra?: Record<string, unknown> }).extra\n const metadata = extra?.metadata as Record<string, unknown> | undefined\n if (metadata && typeof metadata === 'object') {\n for (const key of keys) delete metadata[key]\n }\n } catch (err) {\n console.warn(\n `nvm-langsmith: failed to redact metadata keys [${keys.join(', ')}] from ` +\n 'the parent run tree — a raw credential may remain in the trace',\n err,\n )\n }\n}\n\n/**\n * Return a short, non-functional reference to a payment token for span\n * metadata. Mirrors `payments_py/langsmith/spans.py::abbreviate_token` and the\n * redaction contract in nvm-monorepo#1746 §4 (short-token hardening from\n * nvm-monorepo#1747, tracked in payments-py PR #217).\n *\n * - `undefined`/empty input → `undefined` (and silent — no token at all is not\n * a \"wrong token\" mistake).\n * - Already-redacted input (a genuine `<prefix>…(short)` marker) → returned\n * unchanged and silent, so the helper stays idempotent (the decorator path\n * abbreviates the same token more than once). A raw value that merely *ends*\n * in the marker is NOT treated as redacted (see {@link isRedactedMarker}).\n * - A token of length ≤ 20 is almost always a misconfiguration (a plan id, an\n * opaque handle, etc. passed where the JWT was expected). Because this helper\n * exists to keep credentials out of a durable trace store, such tokens are\n * **redacted, not exported**: at most the first 4 chars are revealed (to aid\n * debugging the misconfig), followed by the `…(short)` marker — and for a\n * token of 4 chars or fewer **nothing** is revealed (the whole value would\n * otherwise be the \"prefix\"), so it collapses to just `…(short)`. A warning\n * is emitted. The full short value never leaves this function.\n * - Otherwise (a normal JWT, more than 20 chars) → `<first 16>…<last 4>`.\n */\nexport function abbreviateToken(token: string | undefined | null): string | undefined {\n if (!token) return undefined\n if (isRedactedMarker(token)) {\n // Already redacted (re-applied on the decorator path); re-slicing would let\n // the marker drift, so return unchanged and stay silent — the original\n // short value already triggered the warning.\n return token\n }\n if (token.length <= 20) {\n console.warn(\n 'abbreviateToken: token is 20 characters or fewer — was the right x402 ' +\n 'access token passed? Short/non-JWT tokens are almost always a ' +\n 'misconfiguration and are redacted (not exported).',\n )\n // Reveal at most 4 chars; for a ≤4-char token reveal nothing, since\n // token.slice(0, 4) would be the entire value — defeating the redaction.\n const prefix = token.length > 4 ? token.slice(0, 4) : ''\n return `${prefix}${SHORT_TOKEN_MARKER}`\n }\n return `${token.slice(0, 16)}…${token.slice(-4)}`\n}\n\n/** Inputs accepted by {@link buildVerifyMetadata}. */\nexport interface VerifyMetadataInput {\n planIds: string[]\n scheme?: string\n network?: string\n agentId?: string\n verification?: VerifyPermissionsResult\n durationMs?: number\n token?: string\n}\n\n/**\n * Build the `nvm.*` metadata for a verify span. Drops absent values (a key is\n * omitted, never set to null/undefined). Matches observability-spans-v1 §2.\n *\n * `token` is abbreviated/redacted via {@link abbreviateToken} before being\n * surfaced as `nvm.payment_token` so the full credential never lands in\n * metadata we control.\n */\nexport function buildVerifyMetadata(input: VerifyMetadataInput): SpanMetadata {\n const { planIds, scheme, network, agentId, verification, durationMs, token } = input\n const md: SpanMetadata = { 'nvm.plan_ids': [...planIds] }\n if (scheme) md['nvm.scheme'] = scheme\n if (network) md['nvm.network'] = network\n if (agentId) md['nvm.agent_id'] = agentId\n if (durationMs !== undefined) md['nvm.verify.duration_ms'] = round2(durationMs)\n const abbreviated = abbreviateToken(token)\n if (abbreviated) md['nvm.payment_token'] = abbreviated\n if (verification) {\n if (verification.payer) md['nvm.payer'] = verification.payer\n if (verification.network && !('nvm.network' in md)) md['nvm.network'] = verification.network\n if (verification.agentRequestId) md['nvm.agent_request_id'] = verification.agentRequestId\n }\n return md\n}\n\n/** Inputs accepted by {@link buildSettleMetadata}. */\nexport interface SettleMetadataInput {\n settlement: SettlePermissionsResult\n planIds: string[]\n agentId?: string\n durationMs?: number\n token?: string\n}\n\n/**\n * Build the `nvm.*` metadata for a settlement span. Drops absent values.\n * Matches observability-spans-v1 §3.\n *\n * Note the types preserved exactly per the spec: `nvm.credits_redeemed`\n * (←`creditsRedeemed`) and `nvm.balance.after` (←`remainingBalance`) are\n * STRINGS — they are not coerced to numbers. `nvm.tx_hash` ← `transaction`.\n */\nexport function buildSettleMetadata(input: SettleMetadataInput): SpanMetadata {\n const { settlement, planIds, agentId, durationMs, token } = input\n const md: SpanMetadata = { 'nvm.plan_ids': [...planIds] }\n if (agentId) md['nvm.agent_id'] = agentId\n if (durationMs !== undefined) md['nvm.settle.duration_ms'] = round2(durationMs)\n const abbreviated = abbreviateToken(token)\n if (abbreviated) md['nvm.payment_token'] = abbreviated\n if (settlement.creditsRedeemed != null) md['nvm.credits_redeemed'] = settlement.creditsRedeemed\n if (settlement.remainingBalance != null) md['nvm.balance.after'] = settlement.remainingBalance\n if (settlement.transaction) md['nvm.tx_hash'] = settlement.transaction\n if (settlement.network) md['nvm.network'] = settlement.network\n if (settlement.payer) md['nvm.payer'] = settlement.payer\n return md\n}\n\n/** Round to 2 decimals, matching Python's `round(value, 2)`. */\nfunction round2(value: number): number {\n return Math.round(value * 100) / 100\n}\n\n/**\n * An opened Nevermined span. `end()` records `outputs`/`error` and flushes the\n * child run to LangSmith. Always safe to call (no-op when `runTree` is absent).\n */\nexport interface NvmSpan {\n /** The underlying child `RunTree`, or `undefined` when tracing is inactive. */\n readonly runTree: RunTree | undefined\n /** Attach `nvm.*` metadata to this span. */\n addMetadata(metadata: SpanMetadata): void\n /** Close the span, flushing it to LangSmith. Optionally record an error. */\n end(error?: unknown): Promise<void>\n}\n\nasync function openNvmSpan(name: string, inputs: SpanMetadata): Promise<NvmSpan> {\n const parent = await activeRunTree()\n if (!parent) return inactiveSpan()\n let child: RunTree\n try {\n child = parent.createChild({ name, run_type: 'tool', inputs })\n } catch (err) {\n // span setup is pure observability — never let it disrupt the payment flow\n console.debug(`nvm-langsmith: createChild(${name}) failed (ignored)`, err)\n return inactiveSpan()\n }\n return {\n runTree: child,\n addMetadata(metadata: SpanMetadata) {\n addMetadata(child, metadata)\n },\n async end(error?: unknown) {\n try {\n await child.end(\n undefined,\n error === undefined ? undefined : error instanceof Error ? error.message : String(error),\n )\n await child.postRun()\n } catch (err) {\n // best-effort flush\n console.debug(`nvm-langsmith: ${name} span flush failed (ignored)`, err)\n }\n },\n }\n}\n\nfunction inactiveSpan(): NvmSpan {\n return {\n runTree: undefined,\n addMetadata() {\n /* no-op */\n },\n async end() {\n /* no-op */\n },\n }\n}\n\n/** Inputs accepted by {@link verifySpan}. */\nexport interface VerifySpanInput {\n planIds: string[]\n scheme?: string\n network?: string\n agentId?: string\n}\n\n/**\n * Open an `nvm:verify` child span around a verify call. Returns an\n * {@link NvmSpan} whose `end()` must be called once the verify completes (or\n * throws). Always safe — a no-op span is returned when tracing is inactive or\n * `langsmith` is not installed.\n */\nexport async function verifySpan(input: VerifySpanInput): Promise<NvmSpan> {\n const { planIds, scheme, network, agentId } = input\n const inputs: SpanMetadata = { plan_ids: [...planIds] }\n if (scheme) inputs.scheme = scheme\n if (network) inputs.network = network\n if (agentId) inputs.agent_id = agentId\n return openNvmSpan(NVM_VERIFY_SPAN, inputs)\n}\n\n/** Inputs accepted by {@link settlementSpan}. */\nexport interface SettlementSpanInput {\n planIds: string[]\n agentId?: string\n}\n\n/**\n * Open an `nvm:settlement` child span around a settle call. Same semantics as\n * {@link verifySpan}.\n */\nexport async function settlementSpan(input: SettlementSpanInput): Promise<NvmSpan> {\n const { planIds, agentId } = input\n const inputs: SpanMetadata = { plan_ids: [...planIds] }\n if (agentId) inputs.agent_id = agentId\n return openNvmSpan(NVM_SETTLEMENT_SPAN, inputs)\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nevermined-io/payments",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.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",
|
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
"./langchain": {
|
|
36
36
|
"types": "./dist/x402/langchain/index.d.ts",
|
|
37
37
|
"import": "./dist/x402/langchain/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./langsmith": {
|
|
40
|
+
"types": "./dist/x402/langsmith/index.d.ts",
|
|
41
|
+
"import": "./dist/x402/langsmith/index.js"
|
|
38
42
|
}
|
|
39
43
|
},
|
|
40
44
|
"scripts": {
|
|
@@ -60,6 +64,7 @@
|
|
|
60
64
|
"@babel/core": "^7.27.4",
|
|
61
65
|
"@babel/preset-env": "^7.27.2",
|
|
62
66
|
"@google-cloud/aiplatform": "^6.1.0",
|
|
67
|
+
"@langchain/langgraph": "1.2.0",
|
|
63
68
|
"@modelcontextprotocol/sdk": "^1.24.3",
|
|
64
69
|
"@types/express": "4.17.23",
|
|
65
70
|
"@types/jest": "^29.5.13",
|
|
@@ -79,6 +84,7 @@
|
|
|
79
84
|
"eslint-plugin-tsdoc": "^0.5.2",
|
|
80
85
|
"jest": "^29.7.0",
|
|
81
86
|
"langchain": "^0.3.37",
|
|
87
|
+
"langsmith": "^0.7.4",
|
|
82
88
|
"openai": "^6.2.0",
|
|
83
89
|
"prettier": "^3.2.5",
|
|
84
90
|
"source-map-support": "^0.5.21",
|
|
@@ -92,9 +98,11 @@
|
|
|
92
98
|
"typescript": "^5.3.3"
|
|
93
99
|
},
|
|
94
100
|
"peerDependencies": {
|
|
101
|
+
"@langchain/core": ">=0.3.0",
|
|
102
|
+
"@langchain/langgraph": ">=1.0.0 <2",
|
|
95
103
|
"@modelcontextprotocol/sdk": ">=1.25.0",
|
|
96
104
|
"express": ">=4.0.0",
|
|
97
|
-
"
|
|
105
|
+
"langsmith": ">=0.7.0"
|
|
98
106
|
},
|
|
99
107
|
"peerDependenciesMeta": {
|
|
100
108
|
"@modelcontextprotocol/sdk": {
|
|
@@ -105,6 +113,12 @@
|
|
|
105
113
|
},
|
|
106
114
|
"@langchain/core": {
|
|
107
115
|
"optional": true
|
|
116
|
+
},
|
|
117
|
+
"@langchain/langgraph": {
|
|
118
|
+
"optional": true
|
|
119
|
+
},
|
|
120
|
+
"langsmith": {
|
|
121
|
+
"optional": true
|
|
108
122
|
}
|
|
109
123
|
},
|
|
110
124
|
"dependencies": {
|