/** * LangSmith span helpers for Nevermined payment events (TypeScript). * * TS parity port of `payments_py/langsmith/spans.py`. Emits the cross-SDK * `nvm:verify` / `nvm:settlement` spans defined by the * **observability-spans-v1** contract * (`docs/specs/observability-spans-v1.md` in nvm-monorepo), so a single * LangSmith trace can be correlated across the TypeScript and Python SDKs * (e.g. filtered on `nvm.tx_hash`). * * All helpers in this module silently no-op when: * - the optional `langsmith` JS SDK is not installed; or * - no LangSmith run tree is active in the current context (e.g. * `LANGSMITH_TRACING` is unset, or the call is not inside a traced run). * * Failures inside this module never propagate out — observability is * best-effort and must not interfere with the payment flow. * * `langsmith` is imported **lazily** (dynamic `import()`), so users who only * use the `requiresPayment` wrapper without tracing are not forced to install * it. Install it yourself (`pnpm add langsmith`) and set `LANGSMITH_TRACING=true` * to surface these spans. */ import type { RunTree } from 'langsmith'; import type { VerifyPermissionsResult, SettlePermissionsResult } from '../facilitator-api.js'; /** Plain JSON-ish metadata bag attached to a span / run tree. */ export type SpanMetadata = Record; /** Span names — must match observability-spans-v1 exactly (case-sensitive). */ export declare const NVM_VERIFY_SPAN = "nvm:verify"; export declare const NVM_SETTLEMENT_SPAN = "nvm:settlement"; /** * Return the current LangSmith `RunTree`, or `undefined` if none is active or * `langsmith` is not installed. Safe to call unconditionally. */ export declare function activeRunTree(): Promise; /** * Merge `metadata` into `runTree`, swallowing any error. No-op if `runTree` is * absent or `metadata` is empty. Matches Python's `run_tree.add_metadata`. * * The merge is performed on THIS side of the boundary — we read the existing * `extra.metadata`, spread the new keys over it, then assign — rather than * relying on the langsmith `RunTree.metadata` setter to merge. In `langsmith@0.7` * the setter does merge (`extra.metadata = { ...existing, ...new }`), but that is * an implementation detail of `run_trees.js`, not a documented contract: a future * release that flips it to plain assignment would silently drop the static * `nvm.*` keys attached on the pre-verify pass once the post-verify pass runs. * Reading+merging here keeps the double-pass accumulation correct regardless, and * mirrors how {@link redactMetadataKeys} already reaches into `extra.metadata`. */ export declare function addMetadata(runTree: RunTree | undefined, metadata: SpanMetadata): void; /** * Remove `keys` from `runTree`'s metadata in place. * * LangSmith inherits a parent run's metadata into child runs created via * `createChild`, so call this on the PARENT run BEFORE opening any child span * whose metadata should not carry the keys. The most common use is stripping * the full `payment_token` from the parent tool span's metadata, since the raw * access token grants access to the protected tool until it expires. * * No-op when `runTree` is absent or no keys are provided. Errors are swallowed * so observability never disrupts the payment flow — but, unlike the other * swallow paths, a failure here is **security-sensitive**: if the parent run * tree still holds the raw `payment_token`, that credential rides into the * parent tree and the inherited verify/settle child spans. So this path warns * (not just debug) to keep the leak diagnosable. */ export declare function redactMetadataKeys(runTree: RunTree | undefined, ...keys: string[]): void; /** * Return a short, non-functional reference to a payment token for span * metadata. Mirrors `payments_py/langsmith/spans.py::abbreviate_token` and the * redaction contract in nvm-monorepo#1746 §4 (short-token hardening from * nvm-monorepo#1747, tracked in payments-py PR #217). * * - `undefined`/empty input → `undefined` (and silent — no token at all is not * a "wrong token" mistake). * - Already-redacted input (a genuine `…(short)` marker) → returned * unchanged and silent, so the helper stays idempotent (the decorator path * abbreviates the same token more than once). A raw value that merely *ends* * in the marker is NOT treated as redacted (see {@link isRedactedMarker}). * - A token of length ≤ 20 is almost always a misconfiguration (a plan id, an * opaque handle, etc. passed where the JWT was expected). Because this helper * exists to keep credentials out of a durable trace store, such tokens are * **redacted, not exported**: at most the first 4 chars are revealed (to aid * debugging the misconfig), followed by the `…(short)` marker — and for a * token of 4 chars or fewer **nothing** is revealed (the whole value would * otherwise be the "prefix"), so it collapses to just `…(short)`. A warning * is emitted. The full short value never leaves this function. * - Otherwise (a normal JWT, more than 20 chars) → ``. */ export declare function abbreviateToken(token: string | undefined | null): string | undefined; /** Inputs accepted by {@link buildVerifyMetadata}. */ export interface VerifyMetadataInput { planIds: string[]; scheme?: string; network?: string; agentId?: string; verification?: VerifyPermissionsResult; durationMs?: number; token?: string; } /** * Build the `nvm.*` metadata for a verify span. Drops absent values (a key is * omitted, never set to null/undefined). Matches observability-spans-v1 §2. * * `token` is abbreviated/redacted via {@link abbreviateToken} before being * surfaced as `nvm.payment_token` so the full credential never lands in * metadata we control. */ export declare function buildVerifyMetadata(input: VerifyMetadataInput): SpanMetadata; /** Inputs accepted by {@link buildSettleMetadata}. */ export interface SettleMetadataInput { settlement: SettlePermissionsResult; planIds: string[]; agentId?: string; durationMs?: number; token?: string; } /** * Build the `nvm.*` metadata for a settlement span. Drops absent values. * Matches observability-spans-v1 §3. * * Note the types preserved exactly per the spec: `nvm.credits_redeemed` * (←`creditsRedeemed`) and `nvm.balance.after` (←`remainingBalance`) are * STRINGS — they are not coerced to numbers. `nvm.tx_hash` ← `transaction`. */ export declare function buildSettleMetadata(input: SettleMetadataInput): SpanMetadata; /** * An opened Nevermined span. `end()` records `outputs`/`error` and flushes the * child run to LangSmith. Always safe to call (no-op when `runTree` is absent). */ export interface NvmSpan { /** The underlying child `RunTree`, or `undefined` when tracing is inactive. */ readonly runTree: RunTree | undefined; /** Attach `nvm.*` metadata to this span. */ addMetadata(metadata: SpanMetadata): void; /** Close the span, flushing it to LangSmith. Optionally record an error. */ end(error?: unknown): Promise; } /** Inputs accepted by {@link verifySpan}. */ export interface VerifySpanInput { planIds: string[]; scheme?: string; network?: string; agentId?: string; } /** * Open an `nvm:verify` child span around a verify call. Returns an * {@link NvmSpan} whose `end()` must be called once the verify completes (or * throws). Always safe — a no-op span is returned when tracing is inactive or * `langsmith` is not installed. */ export declare function verifySpan(input: VerifySpanInput): Promise; /** Inputs accepted by {@link settlementSpan}. */ export interface SettlementSpanInput { planIds: string[]; agentId?: string; } /** * Open an `nvm:settlement` child span around a settle call. Same semantics as * {@link verifySpan}. */ export declare function settlementSpan(input: SettlementSpanInput): Promise; //# sourceMappingURL=spans.d.ts.map