@hashspan/core 0.5.0 → 0.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 CHANGED
@@ -12,7 +12,7 @@ Payments that another party settles on chain, such as x402 payments, become a **
12
12
  of a `send` span.
13
13
 
14
14
  The core is library-agnostic and read-only: it never signs, sends or fetches anything. Adapters such as
15
- [`@hashspan/viem`](https://github.com/selimaytac/hashspan/tree/@hashspan/core@0.5.0/packages/viem) call it for you. Use the core directly to instrument any other send path.
15
+ [`@hashspan/viem`](https://github.com/selimaytac/hashspan/tree/@hashspan/core@0.6.0/packages/viem) call it for you. Use the core directly to instrument any other send path.
16
16
 
17
17
  ## Install
18
18
 
@@ -69,14 +69,14 @@ stays the class name.
69
69
  `tracker.startPayment({ chainId, protocol, payer, recipient, asset, amount })` records a payment that another
70
70
  party settles on chain, such as an x402 facilitator, as a `payment {chainId}` span; end it with
71
71
  `end({ status, hash })` or `fail(error)`. A settlement with a hash links the transaction's confirm span to the payment
72
- span ([ADR 0013](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0013-x402-payments.md)).
72
+ span ([ADR 0013](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md)).
73
73
 
74
74
  All three calls accept an explicit parent `Context` as a second argument. An integration that learns about a call only
75
75
  after it started can record it after the fact: pass `startTime` in the input and `endTime` in the options of the
76
- handle method, e.g. `send.end({ hash }, { endTime })` ([ADR 0009](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0009-telemetry-off-the-call-path.md)). Every method is safe to call: failures inside
76
+ handle method, e.g. `send.end({ hash }, { endTime })` ([ADR 0009](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0009-telemetry-off-the-call-path.md)). Every method is safe to call: failures inside
77
77
  the instrumentation are reported through `diag` and never thrown into your code. The positional forms of earlier
78
78
  releases, `send.end(hash, endTime)` and `send.fail(error, endTime, { errorType })`, still work and are deprecated
79
- until 1.0 ([ADR 0014](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0014-core-api-boundary.md)).
79
+ until 1.0 ([ADR 0014](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md)).
80
80
 
81
81
  ## Options
82
82
 
@@ -84,7 +84,7 @@ until 1.0 ([ADR 0014](https://github.com/selimaytac/hashspan/blob/@hashspan/core
84
84
  |---|---|---|
85
85
  | `tracerProvider` | global provider | Tracer provider to use |
86
86
  | `address` | `'raw'` | `'raw'`, `'hashed'`, `'off'`, or `{ mode: 'hashed', hash: (address) => string }` |
87
- | `errorMessages` | `'off'` | What failed spans record about the error: `'off'` (type only), `'sanitized'` (first line, addresses per `address` mode, calldata removed; in `hashed` and `off` mode any hex longer than an address) or `'raw'` (full message and stack trace). `'raw'` can record RPC URLs that include API keys, as some libraries put the request URL in the message; `'sanitized'` keeps only the first line (viem puts the URL on a later line), which is best effort. See [ADR 0006](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0006-error-privacy.md) |
87
+ | `errorMessages` | `'off'` | What failed spans record about the error: `'off'` (type only), `'sanitized'` (first line, addresses per `address` mode, calldata removed; in `hashed` and `off` mode any hex longer than an address) or `'raw'` (full message and stack trace). `'raw'` can record RPC URLs that include API keys, as some libraries put the request URL in the message; `'sanitized'` keeps only the first line (viem puts the URL on a later line), which is best effort. See [ADR 0006](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0006-error-privacy.md) |
88
88
  | `paymentResource` | `'origin'` | How much of a paid resource's URL `x402.resource` records: `'origin'` (scheme, host and port; nothing for a resource that is not a URL), `'path'` (also the path, never the query string, fragment or user info) or `'off'`. Paths often carry user or account identifiers. At most 512 characters are recorded |
89
89
  | `recordFunctionArguments` | `false` | Record `functionArguments` as a JSON array in `blockchain.contract.function.arguments`: bigints as decimal strings, addresses per `address` mode (longer hex values become `<hex>` in `hashed` and `off` mode), at most 4096 characters. Reads only own enumerable data properties: `toJSON()` and getters are never called, so a `Date` records as `{}`; a Proxy's traps still run |
90
90
  | `agent` | none | Agent `{ id, name }`; a field set here always wins, unset fields come from the Baggage entries `gen_ai.agent.id` / `gen_ai.agent.name` |
@@ -99,7 +99,7 @@ Chain id, transaction hash, sender/recipient (per `address` mode), value, nonce,
99
99
  on confirmation, status, block number, gas used, effective gas price, L1 fee, total fee and revert reason. Decoded
100
100
  call arguments are recorded only with `recordFunctionArguments`, and error messages only with `errorMessages`.
101
101
  Attribute definitions:
102
- [docs/semconv.md](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/semconv.md).
102
+ [docs/semconv.md](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md).
103
103
 
104
104
  ## Privacy notes
105
105
 
@@ -114,7 +114,7 @@ Attribute definitions:
114
114
  propagated, or strip the entries before outbound calls.
115
115
  - **Inbound Baggage can claim an identity.** A caller can send Baggage entries with any agent id. A field set in the
116
116
  `agent` option cannot be overridden that way; to ignore identity from Baggage entirely, set `agentFromBaggage: false`
117
- ([ADR 0011](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0011-agent-identity-precedence.md)).
117
+ ([ADR 0011](https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0011-agent-identity-precedence.md)).
118
118
  - The redaction hook (`redact`) runs last on every attribute set and on exception attributes; use it for anything
119
119
  else your policy forbids.
120
120
  - **Your callbacks' errors go to the diagnostic logger.** If a custom `hash` function or the `redact` hook throws,
package/dist/index.cjs CHANGED
@@ -6,7 +6,7 @@ const ATTR_GEN_AI_AGENT_NAME = "gen_ai.agent.name";
6
6
  /**
7
7
  * Agent identity as GenAI attributes. A field set in the static identity always wins; Baggage, which a remote caller
8
8
  * can set, only fills fields it leaves unset, and is not read at all with `fromBaggage` false
9
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0011-agent-identity-precedence.md).
9
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0011-agent-identity-precedence.md).
10
10
  */
11
11
  function agentAttributes(ctx, identity, fromBaggage = true) {
12
12
  const baggage = fromBaggage ? _opentelemetry_api.propagation.getBaggage(ctx) : void 0;
@@ -22,7 +22,7 @@ function agentAttributes(ctx, identity, fromBaggage = true) {
22
22
  /**
23
23
  * Attribute keys emitted by hashspan.
24
24
  *
25
- * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/semconv.md for
25
+ * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md for
26
26
  * definitions and value types. These names are a public contract: changes follow the deprecation policy in AGENTS.md.
27
27
  */
28
28
  const ATTR_BLOCKCHAIN_SYSTEM = "blockchain.system";
@@ -46,12 +46,12 @@ const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME = "blockchain.contract.function.nam
46
46
  const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR = "blockchain.contract.function.selector";
47
47
  /**
48
48
  * Opt-in: decoded call arguments as a JSON array. See
49
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0004-privacy-defaults.md.
49
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
50
50
  */
51
51
  const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS = "blockchain.contract.function.arguments";
52
52
  /**
53
53
  * Payments settled on chain by a party other than the agent, e.g. an x402 facilitator. See
54
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0013-x402-payments.md.
54
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md.
55
55
  */
56
56
  const ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL = "blockchain.payment.protocol";
57
57
  const ATTR_BLOCKCHAIN_PAYMENT_PAYER = "blockchain.payment.payer";
@@ -61,6 +61,12 @@ const ATTR_BLOCKCHAIN_PAYMENT_AMOUNT = "blockchain.payment.amount";
61
61
  const ATTR_BLOCKCHAIN_PAYMENT_STATUS = "blockchain.payment.status";
62
62
  /** The amount the settling party reports it settled, e.g. less than the authorized maximum with x402 `upto`. */
63
63
  const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT = "blockchain.payment.settled_amount";
64
+ /**
65
+ * Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
66
+ * was possible. See
67
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0017-x402-payment-verification.md.
68
+ */
69
+ const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED = "blockchain.payment.verified";
64
70
  /** x402's own payment fields. */
65
71
  const ATTR_X402_SCHEME = "x402.scheme";
66
72
  const ATTR_X402_RESOURCE = "x402.resource";
@@ -82,7 +88,7 @@ const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED = "reverted";
82
88
  /**
83
89
  * @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
84
90
  * `blockchain.tx.status`. The constant is removed in 1.0. See
85
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
91
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
86
92
  */
87
93
  const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT = "timeout";
88
94
  const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED = "replaced";
@@ -380,7 +386,7 @@ function sanitizeResource(resource) {
380
386
  }
381
387
  //#endregion
382
388
  //#region src/version.ts
383
- const VERSION = "0.5.0";
389
+ const VERSION = "0.6.0";
384
390
  //#endregion
385
391
  //#region src/tracker.ts
386
392
  const INSTRUMENTATION_NAME = "@hashspan/core";
@@ -402,6 +408,7 @@ const NON_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
402
408
  ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON,
403
409
  ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL,
404
410
  ATTR_BLOCKCHAIN_PAYMENT_STATUS,
411
+ ATTR_BLOCKCHAIN_PAYMENT_VERIFIED,
405
412
  ATTR_ERROR_TYPE,
406
413
  ATTR_EXCEPTION_TYPE
407
414
  ]);
@@ -430,7 +437,8 @@ const noopSend = (parent) => ({
430
437
  const NOOP_PAYMENT = {
431
438
  end: () => {},
432
439
  fail: () => {},
433
- timeout: () => {}
440
+ timeout: () => {},
441
+ link: () => {}
434
442
  };
435
443
  const NOOP_CONFIRM = {
436
444
  end: () => {},
@@ -505,7 +513,7 @@ function reportedErrorType(error, options) {
505
513
  }
506
514
  /**
507
515
  * Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
508
- * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/semconv.md). It makes
516
+ * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md). It makes
509
517
  * no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
510
518
  * via `diag`, and a method that fails returns a handle that records nothing.
511
519
  */
@@ -545,7 +553,7 @@ function createTxTracker(options = {}) {
545
553
  /**
546
554
  * Exception event attributes for `error`, per the error message mode. The error object itself is never handed to
547
555
  * the SDK: its message and stack can carry addresses and calldata
548
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0006-error-privacy.md).
556
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0006-error-privacy.md).
549
557
  */
550
558
  const exceptionAttributes = (type, error) => {
551
559
  const attributes = { [ATTR_EXCEPTION_TYPE]: type };
@@ -725,7 +733,7 @@ function createTxTracker(options = {}) {
725
733
  };
726
734
  /**
727
735
  * Ends `shared` with `receipt`, attributing it to the transaction that was mined
728
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0008-replaced-transactions.md).
736
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0008-replaced-transactions.md).
729
737
  */
730
738
  const endWithReceipt = (chainId, hash, shared, receipt, endTime) => {
731
739
  const mined = receipt.transactionHash;
@@ -815,6 +823,15 @@ function createTxTracker(options = {}) {
815
823
  ...input.startTime !== void 0 ? { startTime: input.startTime } : {}
816
824
  }, parent);
817
825
  const finish = finisher(span);
826
+ /** Links the confirm span of `hash` to this payment span, unless the tracker already links that hash. */
827
+ const linkHash = (hash) => {
828
+ if (typeof hash !== "string" || !TX_HASH.test(hash)) return false;
829
+ if (!links.get(input.chainId, hash)) links.set(input.chainId, hash, {
830
+ spanContext: span.spanContext(),
831
+ parent
832
+ });
833
+ return true;
834
+ };
818
835
  const recordSettlement = (settlement) => {
819
836
  const status = settlement.status;
820
837
  if (!PAYMENT_STATUSES.has(status)) {
@@ -823,19 +840,15 @@ function createTxTracker(options = {}) {
823
840
  }
824
841
  const settled = { [ATTR_BLOCKCHAIN_PAYMENT_STATUS]: status };
825
842
  const hash = settlement.hash;
826
- if (typeof hash === "string" && TX_HASH.test(hash)) {
827
- if (!links.get(input.chainId, hash)) links.set(input.chainId, hash, {
828
- spanContext: span.spanContext(),
829
- parent
830
- });
831
- settled[ATTR_BLOCKCHAIN_TX_HASH] = hash;
832
- }
843
+ if (linkHash(hash)) settled[ATTR_BLOCKCHAIN_TX_HASH] = hash;
833
844
  if (!knownPayer) setPaymentAddress(settled, ATTR_BLOCKCHAIN_PAYMENT_PAYER, settlement.payer);
834
845
  const settledAmount = amount(settlement.amount);
835
846
  if (settledAmount !== void 0) {
836
847
  settled[ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT] = settledAmount;
837
848
  if (paid === void 0) settled[ATTR_BLOCKCHAIN_PAYMENT_AMOUNT] = settledAmount;
838
849
  }
850
+ const verified = settlement.verified;
851
+ if (typeof verified === "boolean") settled[ATTR_BLOCKCHAIN_PAYMENT_VERIFIED] = verified;
839
852
  span.setAttributes(redact(settled));
840
853
  if (status === "failed") markError(span, identifier(settlement.errorReason) ?? "_OTHER");
841
854
  };
@@ -845,7 +858,8 @@ function createTxTracker(options = {}) {
845
858
  const read = handleOptions(options);
846
859
  finish("record payment failure", () => markError(span, reportedErrorType(error, read), error, errorType(error)), read.endTime);
847
860
  },
848
- timeout: (options) => finish("record payment timeout", () => markError(span, OBSERVER_TIMEOUT), handleOptions(options).endTime)
861
+ timeout: (options) => finish("record payment timeout", () => markError(span, OBSERVER_TIMEOUT), handleOptions(options).endTime),
862
+ link: (hash) => safely("link the payment span", () => void linkHash(hash), void 0)
849
863
  };
850
864
  };
851
865
  return {
@@ -868,6 +882,7 @@ exports.ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL = ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL;
868
882
  exports.ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT = ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT;
869
883
  exports.ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT = ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT;
870
884
  exports.ATTR_BLOCKCHAIN_PAYMENT_STATUS = ATTR_BLOCKCHAIN_PAYMENT_STATUS;
885
+ exports.ATTR_BLOCKCHAIN_PAYMENT_VERIFIED = ATTR_BLOCKCHAIN_PAYMENT_VERIFIED;
871
886
  exports.ATTR_BLOCKCHAIN_SYSTEM = ATTR_BLOCKCHAIN_SYSTEM;
872
887
  exports.ATTR_BLOCKCHAIN_TX_EFFECTIVE_GAS_PRICE = ATTR_BLOCKCHAIN_TX_EFFECTIVE_GAS_PRICE;
873
888
  exports.ATTR_BLOCKCHAIN_TX_FEE = ATTR_BLOCKCHAIN_TX_FEE;
package/dist/index.d.cts CHANGED
@@ -2,12 +2,12 @@ import { Attributes, Context, TimeInput, TracerProvider } from "@opentelemetry/a
2
2
  //#region src/types.d.ts
3
3
  /**
4
4
  * How wallet addresses are recorded. See
5
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0004-privacy-defaults.md.
5
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
6
6
  */
7
7
  type AddressMode = "raw" | "hashed" | "off";
8
8
  /**
9
9
  * How error messages are recorded on exception events and span status. See
10
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0006-error-privacy.md.
10
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0006-error-privacy.md.
11
11
  * - `off`: error type only
12
12
  * - `sanitized`: first line, addresses per address mode, other long hex data removed
13
13
  * - `raw`: full message and stack trace, as thrown
@@ -61,7 +61,7 @@ interface TxTrackerOptions {
61
61
  /**
62
62
  * Agent identity. A field set here always wins; fields left unset are taken from the Baggage entries
63
63
  * `gen_ai.agent.id` / `gen_ai.agent.name` unless `agentFromBaggage` is false
64
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0011-agent-identity-precedence.md).
64
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0011-agent-identity-precedence.md).
65
65
  */
66
66
  agent?: AgentIdentity | undefined;
67
67
  /**
@@ -75,8 +75,8 @@ interface TxTrackerOptions {
75
75
  * If it throws or returns something other than an attributes object, the tracker fails closed and records only
76
76
  * `blockchain.system`, `blockchain.chain.id`, `blockchain.operation.name`, `blockchain.tx.hash`,
77
77
  * `blockchain.tx.status`, `blockchain.tx.replacement.hash`, `blockchain.tx.replacement.reason`,
78
- * `blockchain.payment.protocol`, `blockchain.payment.status`, `error.type` and `exception.type`, and logs the
79
- * failure via `diag`.
78
+ * `blockchain.payment.protocol`, `blockchain.payment.status`, `blockchain.payment.verified`, `error.type` and
79
+ * `exception.type`, and logs the failure via `diag`.
80
80
  */
81
81
  redact?: ((attributes: Attributes) => Attributes) | undefined;
82
82
  /** How long a sent transaction can be linked from its confirmation. Default: 10 minutes. */
@@ -103,7 +103,7 @@ interface SendInput {
103
103
  functionArguments?: readonly unknown[] | undefined;
104
104
  /**
105
105
  * When the send started, for adapters that record it after the fact
106
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0009-telemetry-off-the-call-path.md).
106
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0009-telemetry-off-the-call-path.md).
107
107
  * Omit it otherwise: with an explicit start time, the SDK measures the span by the wall clock, so pass the end time
108
108
  * to the handle too.
109
109
  */
@@ -112,14 +112,14 @@ interface SendInput {
112
112
  /**
113
113
  * Ends a send span. Only the first call counts; methods never throw.
114
114
  * Produced by the tracker only; methods may be added in minor releases
115
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0014-core-api-boundary.md).
115
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
116
116
  */
117
117
  interface SendHandle {
118
118
  /**
119
119
  * The parent context with the send span set. Run the call that sends the transaction in it, e.g.
120
120
  * `await context.with(send.context, () => sendSomehow())`, so that spans of wallet, RPC or HTTP instrumentation
121
121
  * nest under the send span
122
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0015-send-span-as-active-context.md).
122
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0015-send-span-as-active-context.md).
123
123
  * Run only that call in it: a confirm span started in it becomes a child of the send span.
124
124
  */
125
125
  readonly context: Context;
@@ -182,7 +182,7 @@ interface ReceiptLike {
182
182
  /**
183
183
  * Hash of the mined transaction. When it differs from the awaited hash, the awaited transaction was replaced: its
184
184
  * confirm span ends as `replaced` and the receipt is recorded for this hash
185
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0008-replaced-transactions.md).
185
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0008-replaced-transactions.md).
186
186
  */
187
187
  transactionHash?: string | undefined;
188
188
  /** Replacement reason reported by the library, when {@link transactionHash} differs from the awaited hash. */
@@ -192,7 +192,7 @@ interface ReceiptLike {
192
192
  * One wait for a transaction's receipt, joined to the transaction's shared confirm span. Only the first call counts;
193
193
  * methods never throw.
194
194
  * Produced by the tracker only; methods may be added in minor releases
195
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0014-core-api-boundary.md).
195
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
196
196
  */
197
197
  interface ConfirmHandle {
198
198
  /** Ends the shared confirm span with the receipt, for every handle of the transaction. */
@@ -216,7 +216,7 @@ interface ConfirmHandle {
216
216
  }
217
217
  /**
218
218
  * A payment the agent authorizes and another party settles on chain, e.g. an x402 facilitator
219
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0013-x402-payments.md).
219
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md).
220
220
  * Values often come from a remote server: addresses, amounts and identifiers that are malformed are not recorded.
221
221
  */
222
222
  interface PaymentInput {
@@ -257,13 +257,18 @@ interface PaymentSettlement {
257
257
  * `blockchain.payment.amount` when the input had none.
258
258
  */
259
259
  amount?: bigint | string | undefined;
260
+ /**
261
+ * Whether the settlement transaction's receipt carries this payment, as the adapter checked it from the payer's own
262
+ * data; recorded as `blockchain.payment.verified`. Leave it unset when no check was possible.
263
+ */
264
+ verified?: boolean | undefined;
260
265
  /** Why a `failed` settlement failed, recorded as `error.type` if it is a short identifier, else `_OTHER`. */
261
266
  errorReason?: string | undefined;
262
267
  }
263
268
  /**
264
269
  * Ends a payment span. Only the first call counts; methods never throw.
265
270
  * Produced by the tracker only; methods may be added in minor releases
266
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0014-core-api-boundary.md).
271
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
267
272
  */
268
273
  interface PaymentHandle {
269
274
  /** Ends the payment span with its settlement. */
@@ -279,6 +284,12 @@ interface PaymentHandle {
279
284
  * learned, e.g. no response arrived before the authorization expired.
280
285
  */
281
286
  timeout(options?: EndOptions): void;
287
+ /**
288
+ * Makes the confirm span of the settling transaction `hash` link to this payment span before it ends, for an
289
+ * adapter that ends it only after checking that transaction's receipt (ADR 0017). A hash this tracker already links,
290
+ * such as one of its own sends, keeps its link; `end` with a hash links it as well.
291
+ */
292
+ link(hash: string): void;
282
293
  }
283
294
  //#endregion
284
295
  //#region src/agent.d.ts
@@ -289,7 +300,7 @@ export declare const ATTR_GEN_AI_AGENT_NAME: "gen_ai.agent.name";
289
300
  /**
290
301
  * Attribute keys emitted by hashspan.
291
302
  *
292
- * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/semconv.md for
303
+ * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md for
293
304
  * definitions and value types. These names are a public contract: changes follow the deprecation policy in AGENTS.md.
294
305
  */
295
306
  export declare const ATTR_BLOCKCHAIN_SYSTEM: "blockchain.system";
@@ -313,12 +324,12 @@ export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME: "blockchain.contrac
313
324
  export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR: "blockchain.contract.function.selector";
314
325
  /**
315
326
  * Opt-in: decoded call arguments as a JSON array. See
316
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0004-privacy-defaults.md.
327
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
317
328
  */
318
329
  export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS: "blockchain.contract.function.arguments";
319
330
  /**
320
331
  * Payments settled on chain by a party other than the agent, e.g. an x402 facilitator. See
321
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0013-x402-payments.md.
332
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md.
322
333
  */
323
334
  export declare const ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL: "blockchain.payment.protocol";
324
335
  export declare const ATTR_BLOCKCHAIN_PAYMENT_PAYER: "blockchain.payment.payer";
@@ -328,6 +339,12 @@ export declare const ATTR_BLOCKCHAIN_PAYMENT_AMOUNT: "blockchain.payment.amount"
328
339
  export declare const ATTR_BLOCKCHAIN_PAYMENT_STATUS: "blockchain.payment.status";
329
340
  /** The amount the settling party reports it settled, e.g. less than the authorized maximum with x402 `upto`. */
330
341
  export declare const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT: "blockchain.payment.settled_amount";
342
+ /**
343
+ * Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
344
+ * was possible. See
345
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0017-x402-payment-verification.md.
346
+ */
347
+ export declare const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED: "blockchain.payment.verified";
331
348
  /** x402's own payment fields. */
332
349
  export declare const ATTR_X402_SCHEME: "x402.scheme";
333
350
  export declare const ATTR_X402_RESOURCE: "x402.resource";
@@ -349,7 +366,7 @@ export declare const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED: "reverted";
349
366
  /**
350
367
  * @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
351
368
  * `blockchain.tx.status`. The constant is removed in 1.0. See
352
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
369
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
353
370
  */
354
371
  export declare const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT: "timeout";
355
372
  export declare const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED: "replaced";
@@ -366,7 +383,7 @@ export declare const ERROR_TYPE_VALUE_OTHER: "_OTHER";
366
383
  /**
367
384
  * Records transactions and payments as spans. Obtain one from {@link createTxTracker}: it is not meant to be
368
385
  * implemented, and members may be added to it and to its handles in minor releases
369
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0014-core-api-boundary.md).
386
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
370
387
  */
371
388
  interface TxTracker {
372
389
  /**
@@ -384,7 +401,7 @@ interface TxTracker {
384
401
  /**
385
402
  * Starts a `payment` span as a child of `parent` (default: the active context), for a payment that another party
386
403
  * settles on chain
387
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0013-x402-payments.md). Call
404
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md). Call
388
405
  * `end(settlement)` with the settlement, or `fail(error)`. A settlement with a hash links the transaction's confirm
389
406
  * span to this span, as a send span would.
390
407
  */
@@ -392,7 +409,7 @@ interface TxTracker {
392
409
  }
393
410
  /**
394
411
  * Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
395
- * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/semconv.md). It makes
412
+ * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md). It makes
396
413
  * no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
397
414
  * via `diag`, and a method that fails returns a handle that records nothing.
398
415
  */
package/dist/index.d.mts CHANGED
@@ -2,12 +2,12 @@ import { Attributes, Context, TimeInput, TracerProvider } from "@opentelemetry/a
2
2
  //#region src/types.d.ts
3
3
  /**
4
4
  * How wallet addresses are recorded. See
5
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0004-privacy-defaults.md.
5
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
6
6
  */
7
7
  type AddressMode = "raw" | "hashed" | "off";
8
8
  /**
9
9
  * How error messages are recorded on exception events and span status. See
10
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0006-error-privacy.md.
10
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0006-error-privacy.md.
11
11
  * - `off`: error type only
12
12
  * - `sanitized`: first line, addresses per address mode, other long hex data removed
13
13
  * - `raw`: full message and stack trace, as thrown
@@ -61,7 +61,7 @@ interface TxTrackerOptions {
61
61
  /**
62
62
  * Agent identity. A field set here always wins; fields left unset are taken from the Baggage entries
63
63
  * `gen_ai.agent.id` / `gen_ai.agent.name` unless `agentFromBaggage` is false
64
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0011-agent-identity-precedence.md).
64
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0011-agent-identity-precedence.md).
65
65
  */
66
66
  agent?: AgentIdentity | undefined;
67
67
  /**
@@ -75,8 +75,8 @@ interface TxTrackerOptions {
75
75
  * If it throws or returns something other than an attributes object, the tracker fails closed and records only
76
76
  * `blockchain.system`, `blockchain.chain.id`, `blockchain.operation.name`, `blockchain.tx.hash`,
77
77
  * `blockchain.tx.status`, `blockchain.tx.replacement.hash`, `blockchain.tx.replacement.reason`,
78
- * `blockchain.payment.protocol`, `blockchain.payment.status`, `error.type` and `exception.type`, and logs the
79
- * failure via `diag`.
78
+ * `blockchain.payment.protocol`, `blockchain.payment.status`, `blockchain.payment.verified`, `error.type` and
79
+ * `exception.type`, and logs the failure via `diag`.
80
80
  */
81
81
  redact?: ((attributes: Attributes) => Attributes) | undefined;
82
82
  /** How long a sent transaction can be linked from its confirmation. Default: 10 minutes. */
@@ -103,7 +103,7 @@ interface SendInput {
103
103
  functionArguments?: readonly unknown[] | undefined;
104
104
  /**
105
105
  * When the send started, for adapters that record it after the fact
106
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0009-telemetry-off-the-call-path.md).
106
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0009-telemetry-off-the-call-path.md).
107
107
  * Omit it otherwise: with an explicit start time, the SDK measures the span by the wall clock, so pass the end time
108
108
  * to the handle too.
109
109
  */
@@ -112,14 +112,14 @@ interface SendInput {
112
112
  /**
113
113
  * Ends a send span. Only the first call counts; methods never throw.
114
114
  * Produced by the tracker only; methods may be added in minor releases
115
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0014-core-api-boundary.md).
115
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
116
116
  */
117
117
  interface SendHandle {
118
118
  /**
119
119
  * The parent context with the send span set. Run the call that sends the transaction in it, e.g.
120
120
  * `await context.with(send.context, () => sendSomehow())`, so that spans of wallet, RPC or HTTP instrumentation
121
121
  * nest under the send span
122
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0015-send-span-as-active-context.md).
122
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0015-send-span-as-active-context.md).
123
123
  * Run only that call in it: a confirm span started in it becomes a child of the send span.
124
124
  */
125
125
  readonly context: Context;
@@ -182,7 +182,7 @@ interface ReceiptLike {
182
182
  /**
183
183
  * Hash of the mined transaction. When it differs from the awaited hash, the awaited transaction was replaced: its
184
184
  * confirm span ends as `replaced` and the receipt is recorded for this hash
185
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0008-replaced-transactions.md).
185
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0008-replaced-transactions.md).
186
186
  */
187
187
  transactionHash?: string | undefined;
188
188
  /** Replacement reason reported by the library, when {@link transactionHash} differs from the awaited hash. */
@@ -192,7 +192,7 @@ interface ReceiptLike {
192
192
  * One wait for a transaction's receipt, joined to the transaction's shared confirm span. Only the first call counts;
193
193
  * methods never throw.
194
194
  * Produced by the tracker only; methods may be added in minor releases
195
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0014-core-api-boundary.md).
195
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
196
196
  */
197
197
  interface ConfirmHandle {
198
198
  /** Ends the shared confirm span with the receipt, for every handle of the transaction. */
@@ -216,7 +216,7 @@ interface ConfirmHandle {
216
216
  }
217
217
  /**
218
218
  * A payment the agent authorizes and another party settles on chain, e.g. an x402 facilitator
219
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0013-x402-payments.md).
219
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md).
220
220
  * Values often come from a remote server: addresses, amounts and identifiers that are malformed are not recorded.
221
221
  */
222
222
  interface PaymentInput {
@@ -257,13 +257,18 @@ interface PaymentSettlement {
257
257
  * `blockchain.payment.amount` when the input had none.
258
258
  */
259
259
  amount?: bigint | string | undefined;
260
+ /**
261
+ * Whether the settlement transaction's receipt carries this payment, as the adapter checked it from the payer's own
262
+ * data; recorded as `blockchain.payment.verified`. Leave it unset when no check was possible.
263
+ */
264
+ verified?: boolean | undefined;
260
265
  /** Why a `failed` settlement failed, recorded as `error.type` if it is a short identifier, else `_OTHER`. */
261
266
  errorReason?: string | undefined;
262
267
  }
263
268
  /**
264
269
  * Ends a payment span. Only the first call counts; methods never throw.
265
270
  * Produced by the tracker only; methods may be added in minor releases
266
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0014-core-api-boundary.md).
271
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
267
272
  */
268
273
  interface PaymentHandle {
269
274
  /** Ends the payment span with its settlement. */
@@ -279,6 +284,12 @@ interface PaymentHandle {
279
284
  * learned, e.g. no response arrived before the authorization expired.
280
285
  */
281
286
  timeout(options?: EndOptions): void;
287
+ /**
288
+ * Makes the confirm span of the settling transaction `hash` link to this payment span before it ends, for an
289
+ * adapter that ends it only after checking that transaction's receipt (ADR 0017). A hash this tracker already links,
290
+ * such as one of its own sends, keeps its link; `end` with a hash links it as well.
291
+ */
292
+ link(hash: string): void;
282
293
  }
283
294
  //#endregion
284
295
  //#region src/agent.d.ts
@@ -289,7 +300,7 @@ export declare const ATTR_GEN_AI_AGENT_NAME: "gen_ai.agent.name";
289
300
  /**
290
301
  * Attribute keys emitted by hashspan.
291
302
  *
292
- * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/semconv.md for
303
+ * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md for
293
304
  * definitions and value types. These names are a public contract: changes follow the deprecation policy in AGENTS.md.
294
305
  */
295
306
  export declare const ATTR_BLOCKCHAIN_SYSTEM: "blockchain.system";
@@ -313,12 +324,12 @@ export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME: "blockchain.contrac
313
324
  export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR: "blockchain.contract.function.selector";
314
325
  /**
315
326
  * Opt-in: decoded call arguments as a JSON array. See
316
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0004-privacy-defaults.md.
327
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
317
328
  */
318
329
  export declare const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS: "blockchain.contract.function.arguments";
319
330
  /**
320
331
  * Payments settled on chain by a party other than the agent, e.g. an x402 facilitator. See
321
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0013-x402-payments.md.
332
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md.
322
333
  */
323
334
  export declare const ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL: "blockchain.payment.protocol";
324
335
  export declare const ATTR_BLOCKCHAIN_PAYMENT_PAYER: "blockchain.payment.payer";
@@ -328,6 +339,12 @@ export declare const ATTR_BLOCKCHAIN_PAYMENT_AMOUNT: "blockchain.payment.amount"
328
339
  export declare const ATTR_BLOCKCHAIN_PAYMENT_STATUS: "blockchain.payment.status";
329
340
  /** The amount the settling party reports it settled, e.g. less than the authorized maximum with x402 `upto`. */
330
341
  export declare const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT: "blockchain.payment.settled_amount";
342
+ /**
343
+ * Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
344
+ * was possible. See
345
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0017-x402-payment-verification.md.
346
+ */
347
+ export declare const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED: "blockchain.payment.verified";
331
348
  /** x402's own payment fields. */
332
349
  export declare const ATTR_X402_SCHEME: "x402.scheme";
333
350
  export declare const ATTR_X402_RESOURCE: "x402.resource";
@@ -349,7 +366,7 @@ export declare const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED: "reverted";
349
366
  /**
350
367
  * @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
351
368
  * `blockchain.tx.status`. The constant is removed in 1.0. See
352
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
369
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
353
370
  */
354
371
  export declare const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT: "timeout";
355
372
  export declare const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED: "replaced";
@@ -366,7 +383,7 @@ export declare const ERROR_TYPE_VALUE_OTHER: "_OTHER";
366
383
  /**
367
384
  * Records transactions and payments as spans. Obtain one from {@link createTxTracker}: it is not meant to be
368
385
  * implemented, and members may be added to it and to its handles in minor releases
369
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0014-core-api-boundary.md).
386
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0014-core-api-boundary.md).
370
387
  */
371
388
  interface TxTracker {
372
389
  /**
@@ -384,7 +401,7 @@ interface TxTracker {
384
401
  /**
385
402
  * Starts a `payment` span as a child of `parent` (default: the active context), for a payment that another party
386
403
  * settles on chain
387
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0013-x402-payments.md). Call
404
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md). Call
388
405
  * `end(settlement)` with the settlement, or `fail(error)`. A settlement with a hash links the transaction's confirm
389
406
  * span to this span, as a send span would.
390
407
  */
@@ -392,7 +409,7 @@ interface TxTracker {
392
409
  }
393
410
  /**
394
411
  * Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
395
- * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/semconv.md). It makes
412
+ * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md). It makes
396
413
  * no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
397
414
  * via `diag`, and a method that fails returns a handle that records nothing.
398
415
  */
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ const ATTR_GEN_AI_AGENT_NAME = "gen_ai.agent.name";
5
5
  /**
6
6
  * Agent identity as GenAI attributes. A field set in the static identity always wins; Baggage, which a remote caller
7
7
  * can set, only fills fields it leaves unset, and is not read at all with `fromBaggage` false
8
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0011-agent-identity-precedence.md).
8
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0011-agent-identity-precedence.md).
9
9
  */
10
10
  function agentAttributes(ctx, identity, fromBaggage = true) {
11
11
  const baggage = fromBaggage ? propagation.getBaggage(ctx) : void 0;
@@ -21,7 +21,7 @@ function agentAttributes(ctx, identity, fromBaggage = true) {
21
21
  /**
22
22
  * Attribute keys emitted by hashspan.
23
23
  *
24
- * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/semconv.md for
24
+ * Stability: development. See https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md for
25
25
  * definitions and value types. These names are a public contract: changes follow the deprecation policy in AGENTS.md.
26
26
  */
27
27
  const ATTR_BLOCKCHAIN_SYSTEM = "blockchain.system";
@@ -45,12 +45,12 @@ const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME = "blockchain.contract.function.nam
45
45
  const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR = "blockchain.contract.function.selector";
46
46
  /**
47
47
  * Opt-in: decoded call arguments as a JSON array. See
48
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0004-privacy-defaults.md.
48
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0004-privacy-defaults.md.
49
49
  */
50
50
  const ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS = "blockchain.contract.function.arguments";
51
51
  /**
52
52
  * Payments settled on chain by a party other than the agent, e.g. an x402 facilitator. See
53
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0013-x402-payments.md.
53
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0013-x402-payments.md.
54
54
  */
55
55
  const ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL = "blockchain.payment.protocol";
56
56
  const ATTR_BLOCKCHAIN_PAYMENT_PAYER = "blockchain.payment.payer";
@@ -60,6 +60,12 @@ const ATTR_BLOCKCHAIN_PAYMENT_AMOUNT = "blockchain.payment.amount";
60
60
  const ATTR_BLOCKCHAIN_PAYMENT_STATUS = "blockchain.payment.status";
61
61
  /** The amount the settling party reports it settled, e.g. less than the authorized maximum with x402 `upto`. */
62
62
  const ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT = "blockchain.payment.settled_amount";
63
+ /**
64
+ * Whether the settlement transaction's receipt carries the payment, as checked by the adapter; absent when no check
65
+ * was possible. See
66
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0017-x402-payment-verification.md.
67
+ */
68
+ const ATTR_BLOCKCHAIN_PAYMENT_VERIFIED = "blockchain.payment.verified";
63
69
  /** x402's own payment fields. */
64
70
  const ATTR_X402_SCHEME = "x402.scheme";
65
71
  const ATTR_X402_RESOURCE = "x402.resource";
@@ -81,7 +87,7 @@ const BLOCKCHAIN_TX_STATUS_VALUE_REVERTED = "reverted";
81
87
  /**
82
88
  * @deprecated No longer recorded: a confirm span that gave up waiting records `error.type` `timeout` and no
83
89
  * `blockchain.tx.status`. The constant is removed in 1.0. See
84
- * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
90
+ * https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0016-timeout-is-an-observer-outcome.md.
85
91
  */
86
92
  const BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT = "timeout";
87
93
  const BLOCKCHAIN_TX_STATUS_VALUE_REPLACED = "replaced";
@@ -379,7 +385,7 @@ function sanitizeResource(resource) {
379
385
  }
380
386
  //#endregion
381
387
  //#region src/version.ts
382
- const VERSION = "0.5.0";
388
+ const VERSION = "0.6.0";
383
389
  //#endregion
384
390
  //#region src/tracker.ts
385
391
  const INSTRUMENTATION_NAME = "@hashspan/core";
@@ -401,6 +407,7 @@ const NON_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
401
407
  ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON,
402
408
  ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL,
403
409
  ATTR_BLOCKCHAIN_PAYMENT_STATUS,
410
+ ATTR_BLOCKCHAIN_PAYMENT_VERIFIED,
404
411
  ATTR_ERROR_TYPE,
405
412
  ATTR_EXCEPTION_TYPE
406
413
  ]);
@@ -429,7 +436,8 @@ const noopSend = (parent) => ({
429
436
  const NOOP_PAYMENT = {
430
437
  end: () => {},
431
438
  fail: () => {},
432
- timeout: () => {}
439
+ timeout: () => {},
440
+ link: () => {}
433
441
  };
434
442
  const NOOP_CONFIRM = {
435
443
  end: () => {},
@@ -504,7 +512,7 @@ function reportedErrorType(error, options) {
504
512
  }
505
513
  /**
506
514
  * Creates a tracker that records transactions as `send` and `confirm` spans, and payments as `payment` spans, with
507
- * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/semconv.md). It makes
515
+ * `@opentelemetry/api` (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/semconv.md). It makes
508
516
  * no network calls; the caller passes hashes and receipts. Its methods and handles never throw: failures are logged
509
517
  * via `diag`, and a method that fails returns a handle that records nothing.
510
518
  */
@@ -544,7 +552,7 @@ function createTxTracker(options = {}) {
544
552
  /**
545
553
  * Exception event attributes for `error`, per the error message mode. The error object itself is never handed to
546
554
  * the SDK: its message and stack can carry addresses and calldata
547
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0006-error-privacy.md).
555
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0006-error-privacy.md).
548
556
  */
549
557
  const exceptionAttributes = (type, error) => {
550
558
  const attributes = { [ATTR_EXCEPTION_TYPE]: type };
@@ -724,7 +732,7 @@ function createTxTracker(options = {}) {
724
732
  };
725
733
  /**
726
734
  * Ends `shared` with `receipt`, attributing it to the transaction that was mined
727
- * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.5.0/docs/adr/0008-replaced-transactions.md).
735
+ * (https://github.com/selimaytac/hashspan/blob/@hashspan/core@0.6.0/docs/adr/0008-replaced-transactions.md).
728
736
  */
729
737
  const endWithReceipt = (chainId, hash, shared, receipt, endTime) => {
730
738
  const mined = receipt.transactionHash;
@@ -814,6 +822,15 @@ function createTxTracker(options = {}) {
814
822
  ...input.startTime !== void 0 ? { startTime: input.startTime } : {}
815
823
  }, parent);
816
824
  const finish = finisher(span);
825
+ /** Links the confirm span of `hash` to this payment span, unless the tracker already links that hash. */
826
+ const linkHash = (hash) => {
827
+ if (typeof hash !== "string" || !TX_HASH.test(hash)) return false;
828
+ if (!links.get(input.chainId, hash)) links.set(input.chainId, hash, {
829
+ spanContext: span.spanContext(),
830
+ parent
831
+ });
832
+ return true;
833
+ };
817
834
  const recordSettlement = (settlement) => {
818
835
  const status = settlement.status;
819
836
  if (!PAYMENT_STATUSES.has(status)) {
@@ -822,19 +839,15 @@ function createTxTracker(options = {}) {
822
839
  }
823
840
  const settled = { [ATTR_BLOCKCHAIN_PAYMENT_STATUS]: status };
824
841
  const hash = settlement.hash;
825
- if (typeof hash === "string" && TX_HASH.test(hash)) {
826
- if (!links.get(input.chainId, hash)) links.set(input.chainId, hash, {
827
- spanContext: span.spanContext(),
828
- parent
829
- });
830
- settled[ATTR_BLOCKCHAIN_TX_HASH] = hash;
831
- }
842
+ if (linkHash(hash)) settled[ATTR_BLOCKCHAIN_TX_HASH] = hash;
832
843
  if (!knownPayer) setPaymentAddress(settled, ATTR_BLOCKCHAIN_PAYMENT_PAYER, settlement.payer);
833
844
  const settledAmount = amount(settlement.amount);
834
845
  if (settledAmount !== void 0) {
835
846
  settled[ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT] = settledAmount;
836
847
  if (paid === void 0) settled[ATTR_BLOCKCHAIN_PAYMENT_AMOUNT] = settledAmount;
837
848
  }
849
+ const verified = settlement.verified;
850
+ if (typeof verified === "boolean") settled[ATTR_BLOCKCHAIN_PAYMENT_VERIFIED] = verified;
838
851
  span.setAttributes(redact(settled));
839
852
  if (status === "failed") markError(span, identifier(settlement.errorReason) ?? "_OTHER");
840
853
  };
@@ -844,7 +857,8 @@ function createTxTracker(options = {}) {
844
857
  const read = handleOptions(options);
845
858
  finish("record payment failure", () => markError(span, reportedErrorType(error, read), error, errorType(error)), read.endTime);
846
859
  },
847
- timeout: (options) => finish("record payment timeout", () => markError(span, OBSERVER_TIMEOUT), handleOptions(options).endTime)
860
+ timeout: (options) => finish("record payment timeout", () => markError(span, OBSERVER_TIMEOUT), handleOptions(options).endTime),
861
+ link: (hash) => safely("link the payment span", () => void linkHash(hash), void 0)
848
862
  };
849
863
  };
850
864
  return {
@@ -854,4 +868,4 @@ function createTxTracker(options = {}) {
854
868
  };
855
869
  }
856
870
  //#endregion
857
- export { ATTR_BLOCKCHAIN_BLOCK_NUMBER, ATTR_BLOCKCHAIN_CHAIN_ID, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR, ATTR_BLOCKCHAIN_OPERATION_NAME, ATTR_BLOCKCHAIN_PAYMENT_AMOUNT, ATTR_BLOCKCHAIN_PAYMENT_ASSET, ATTR_BLOCKCHAIN_PAYMENT_PAYER, ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL, ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT, ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT, ATTR_BLOCKCHAIN_PAYMENT_STATUS, ATTR_BLOCKCHAIN_SYSTEM, ATTR_BLOCKCHAIN_TX_EFFECTIVE_GAS_PRICE, ATTR_BLOCKCHAIN_TX_FEE, ATTR_BLOCKCHAIN_TX_FROM, ATTR_BLOCKCHAIN_TX_GAS_USED, ATTR_BLOCKCHAIN_TX_HASH, ATTR_BLOCKCHAIN_TX_L1_FEE, ATTR_BLOCKCHAIN_TX_NONCE, ATTR_BLOCKCHAIN_TX_REPLACEMENT_HASH, ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON, ATTR_BLOCKCHAIN_TX_REVERT_REASON, ATTR_BLOCKCHAIN_TX_STATUS, ATTR_BLOCKCHAIN_TX_TO, ATTR_BLOCKCHAIN_TX_VALUE, ATTR_ERROR_TYPE, ATTR_GEN_AI_AGENT_ID, ATTR_GEN_AI_AGENT_NAME, ATTR_X402_RESOURCE, ATTR_X402_SCHEME, BLOCKCHAIN_OPERATION_NAME_VALUE_CONFIRM, BLOCKCHAIN_OPERATION_NAME_VALUE_PAYMENT, BLOCKCHAIN_OPERATION_NAME_VALUE_SEND, BLOCKCHAIN_PAYMENT_PROTOCOL_VALUE_X402, BLOCKCHAIN_PAYMENT_STATUS_VALUE_FAILED, BLOCKCHAIN_PAYMENT_STATUS_VALUE_PENDING, BLOCKCHAIN_PAYMENT_STATUS_VALUE_SETTLED, BLOCKCHAIN_SYSTEM_VALUE_EVM, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_CANCELLED, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_REPLACED, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_REPRICED, BLOCKCHAIN_TX_STATUS_VALUE_REPLACED, BLOCKCHAIN_TX_STATUS_VALUE_REVERTED, BLOCKCHAIN_TX_STATUS_VALUE_SUCCESS, BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT, ERROR_TYPE_VALUE_OTHER, VERSION, createTxTracker };
871
+ export { ATTR_BLOCKCHAIN_BLOCK_NUMBER, ATTR_BLOCKCHAIN_CHAIN_ID, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_ARGUMENTS, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_NAME, ATTR_BLOCKCHAIN_CONTRACT_FUNCTION_SELECTOR, ATTR_BLOCKCHAIN_OPERATION_NAME, ATTR_BLOCKCHAIN_PAYMENT_AMOUNT, ATTR_BLOCKCHAIN_PAYMENT_ASSET, ATTR_BLOCKCHAIN_PAYMENT_PAYER, ATTR_BLOCKCHAIN_PAYMENT_PROTOCOL, ATTR_BLOCKCHAIN_PAYMENT_RECIPIENT, ATTR_BLOCKCHAIN_PAYMENT_SETTLED_AMOUNT, ATTR_BLOCKCHAIN_PAYMENT_STATUS, ATTR_BLOCKCHAIN_PAYMENT_VERIFIED, ATTR_BLOCKCHAIN_SYSTEM, ATTR_BLOCKCHAIN_TX_EFFECTIVE_GAS_PRICE, ATTR_BLOCKCHAIN_TX_FEE, ATTR_BLOCKCHAIN_TX_FROM, ATTR_BLOCKCHAIN_TX_GAS_USED, ATTR_BLOCKCHAIN_TX_HASH, ATTR_BLOCKCHAIN_TX_L1_FEE, ATTR_BLOCKCHAIN_TX_NONCE, ATTR_BLOCKCHAIN_TX_REPLACEMENT_HASH, ATTR_BLOCKCHAIN_TX_REPLACEMENT_REASON, ATTR_BLOCKCHAIN_TX_REVERT_REASON, ATTR_BLOCKCHAIN_TX_STATUS, ATTR_BLOCKCHAIN_TX_TO, ATTR_BLOCKCHAIN_TX_VALUE, ATTR_ERROR_TYPE, ATTR_GEN_AI_AGENT_ID, ATTR_GEN_AI_AGENT_NAME, ATTR_X402_RESOURCE, ATTR_X402_SCHEME, BLOCKCHAIN_OPERATION_NAME_VALUE_CONFIRM, BLOCKCHAIN_OPERATION_NAME_VALUE_PAYMENT, BLOCKCHAIN_OPERATION_NAME_VALUE_SEND, BLOCKCHAIN_PAYMENT_PROTOCOL_VALUE_X402, BLOCKCHAIN_PAYMENT_STATUS_VALUE_FAILED, BLOCKCHAIN_PAYMENT_STATUS_VALUE_PENDING, BLOCKCHAIN_PAYMENT_STATUS_VALUE_SETTLED, BLOCKCHAIN_SYSTEM_VALUE_EVM, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_CANCELLED, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_REPLACED, BLOCKCHAIN_TX_REPLACEMENT_REASON_VALUE_REPRICED, BLOCKCHAIN_TX_STATUS_VALUE_REPLACED, BLOCKCHAIN_TX_STATUS_VALUE_REVERTED, BLOCKCHAIN_TX_STATUS_VALUE_SUCCESS, BLOCKCHAIN_TX_STATUS_VALUE_TIMEOUT, ERROR_TYPE_VALUE_OTHER, VERSION, createTxTracker };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hashspan/core",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Transaction lifecycle tracing for on-chain actions of AI agents, built on OpenTelemetry.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Selim Aytac",