@hashspan/core 0.1.0 → 0.2.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 +9 -3
- package/dist/index.cjs +10 -7
- package/dist/index.d.cts +10 -1
- package/dist/index.d.mts +10 -1
- package/dist/index.mjs +10 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -17,7 +17,9 @@ The core is library-agnostic and read-only: it never signs, sends or fetches any
|
|
|
17
17
|
npm install @hashspan/core @opentelemetry/api
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
`@opentelemetry/api` is the only peer dependency. Bring your own OpenTelemetry SDK and exporter.
|
|
20
|
+
`@opentelemetry/api` is the only peer dependency. Bring your own OpenTelemetry SDK and exporter. Requires Node.js 22.3
|
|
21
|
+
or later; in other runtimes, `hashed` address mode needs a custom `hash` function and otherwise records no addresses,
|
|
22
|
+
with a `diag` warning.
|
|
21
23
|
|
|
22
24
|
## Usage
|
|
23
25
|
|
|
@@ -66,9 +68,10 @@ the instrumentation are reported through `diag` and never thrown into your code.
|
|
|
66
68
|
|---|---|---|
|
|
67
69
|
| `tracerProvider` | global provider | Tracer provider to use |
|
|
68
70
|
| `address` | `'raw'` | `'raw'`, `'hashed'`, `'off'`, or `{ mode: 'hashed', hash: (address) => string }` |
|
|
69
|
-
| `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) |
|
|
71
|
+
| `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/main/docs/adr/0006-error-privacy.md) |
|
|
70
72
|
| `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 |
|
|
71
|
-
| `agent` | none |
|
|
73
|
+
| `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` |
|
|
74
|
+
| `agentFromBaggage` | `true` | Read agent identity fields that `agent` leaves unset from Baggage; set to `false` in services that accept requests from outside their trust boundary |
|
|
72
75
|
| `redact` | none | `(attributes) => attributes`, runs last on every attribute set, including exception event attributes; if it throws, only non-sensitive identifiers are kept |
|
|
73
76
|
| `linkTtlMs` | `600000` | How long a sent transaction can be linked from its confirmation |
|
|
74
77
|
| `maxTrackedTransactions` | `10000` | Upper bound on transactions kept for linking |
|
|
@@ -92,6 +95,9 @@ Attribute definitions:
|
|
|
92
95
|
party APIs. Put only identifiers there that may leave your system, such as an opaque agent id. For identifiers
|
|
93
96
|
that must stay internal, use the tracker's static `agent` option instead, which is recorded on spans but never
|
|
94
97
|
propagated, or strip the entries before outbound calls.
|
|
98
|
+
- **Inbound Baggage can claim an identity.** A caller can send Baggage entries with any agent id. A field set in the
|
|
99
|
+
`agent` option cannot be overridden that way; to ignore identity from Baggage entirely, set `agentFromBaggage: false`
|
|
100
|
+
([ADR 0011](https://github.com/selimaytac/hashspan/blob/main/docs/adr/0011-agent-identity-precedence.md)).
|
|
95
101
|
- The redaction hook (`redact`) runs last on every attribute set and on exception attributes; use it for anything
|
|
96
102
|
else your policy forbids.
|
|
97
103
|
|
package/dist/index.cjs
CHANGED
|
@@ -3,11 +3,14 @@ let _opentelemetry_api = require("@opentelemetry/api");
|
|
|
3
3
|
//#region src/agent.ts
|
|
4
4
|
const ATTR_GEN_AI_AGENT_ID = "gen_ai.agent.id";
|
|
5
5
|
const ATTR_GEN_AI_AGENT_NAME = "gen_ai.agent.name";
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Agent identity as GenAI attributes. A field set in the static identity always wins; Baggage, which a remote caller
|
|
8
|
+
* can set, only fills fields it leaves unset, and is not read at all with `fromBaggage` false (docs/adr/0011).
|
|
9
|
+
*/
|
|
10
|
+
function agentAttributes(ctx, identity, fromBaggage = true) {
|
|
11
|
+
const baggage = fromBaggage ? _opentelemetry_api.propagation.getBaggage(ctx) : void 0;
|
|
12
|
+
const id = identity?.id ?? baggage?.getEntry("gen_ai.agent.id")?.value;
|
|
13
|
+
const name = identity?.name ?? baggage?.getEntry("gen_ai.agent.name")?.value;
|
|
11
14
|
const attributes = {};
|
|
12
15
|
if (id !== void 0) attributes[ATTR_GEN_AI_AGENT_ID] = id;
|
|
13
16
|
if (name !== void 0) attributes[ATTR_GEN_AI_AGENT_NAME] = name;
|
|
@@ -299,7 +302,7 @@ function resolveErrorMessageMode(mode) {
|
|
|
299
302
|
}
|
|
300
303
|
//#endregion
|
|
301
304
|
//#region src/version.ts
|
|
302
|
-
const VERSION = "0.
|
|
305
|
+
const VERSION = "0.2.0";
|
|
303
306
|
//#endregion
|
|
304
307
|
//#region src/tracker.ts
|
|
305
308
|
const INSTRUMENTATION_NAME = "@hashspan/core";
|
|
@@ -443,7 +446,7 @@ function createTxTracker(options = {}) {
|
|
|
443
446
|
[ATTR_BLOCKCHAIN_SYSTEM]: "evm",
|
|
444
447
|
[ATTR_BLOCKCHAIN_CHAIN_ID]: chainId,
|
|
445
448
|
[ATTR_BLOCKCHAIN_OPERATION_NAME]: operation,
|
|
446
|
-
...agentAttributes(ctx, options.agent)
|
|
449
|
+
...agentAttributes(ctx, options.agent, options.agentFromBaggage !== false)
|
|
447
450
|
});
|
|
448
451
|
const startSend = (input, parentCtx) => {
|
|
449
452
|
const parent = parentCtx ?? _opentelemetry_api.context.active();
|
package/dist/index.d.cts
CHANGED
|
@@ -38,8 +38,17 @@ interface TxTrackerOptions {
|
|
|
38
38
|
* text; addresses in them follow the address mode and the redaction hook runs on them.
|
|
39
39
|
*/
|
|
40
40
|
recordFunctionArguments?: boolean | undefined;
|
|
41
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Agent identity. A field set here always wins; fields left unset are taken from the Baggage entries
|
|
43
|
+
* `gen_ai.agent.id` / `gen_ai.agent.name` unless `agentFromBaggage` is false (docs/adr/0011).
|
|
44
|
+
*/
|
|
42
45
|
agent?: AgentIdentity | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Read agent identity fields that `agent` leaves unset from Baggage. Default: true. Baggage travels with requests
|
|
48
|
+
* between services, so a remote caller can set it; services that accept requests from outside their trust boundary
|
|
49
|
+
* should set this to false.
|
|
50
|
+
*/
|
|
51
|
+
agentFromBaggage?: boolean | undefined;
|
|
43
52
|
/**
|
|
44
53
|
* Runs last on every attribute set and returns the attributes to record.
|
|
45
54
|
* If it throws, only non-sensitive identifiers (system, chain id, operation, hash) are recorded.
|
package/dist/index.d.mts
CHANGED
|
@@ -38,8 +38,17 @@ interface TxTrackerOptions {
|
|
|
38
38
|
* text; addresses in them follow the address mode and the redaction hook runs on them.
|
|
39
39
|
*/
|
|
40
40
|
recordFunctionArguments?: boolean | undefined;
|
|
41
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Agent identity. A field set here always wins; fields left unset are taken from the Baggage entries
|
|
43
|
+
* `gen_ai.agent.id` / `gen_ai.agent.name` unless `agentFromBaggage` is false (docs/adr/0011).
|
|
44
|
+
*/
|
|
42
45
|
agent?: AgentIdentity | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Read agent identity fields that `agent` leaves unset from Baggage. Default: true. Baggage travels with requests
|
|
48
|
+
* between services, so a remote caller can set it; services that accept requests from outside their trust boundary
|
|
49
|
+
* should set this to false.
|
|
50
|
+
*/
|
|
51
|
+
agentFromBaggage?: boolean | undefined;
|
|
43
52
|
/**
|
|
44
53
|
* Runs last on every attribute set and returns the attributes to record.
|
|
45
54
|
* If it throws, only non-sensitive identifiers (system, chain id, operation, hash) are recorded.
|
package/dist/index.mjs
CHANGED
|
@@ -2,11 +2,14 @@ import { SpanKind, SpanStatusCode, context, diag, propagation, trace } from "@op
|
|
|
2
2
|
//#region src/agent.ts
|
|
3
3
|
const ATTR_GEN_AI_AGENT_ID = "gen_ai.agent.id";
|
|
4
4
|
const ATTR_GEN_AI_AGENT_NAME = "gen_ai.agent.name";
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Agent identity as GenAI attributes. A field set in the static identity always wins; Baggage, which a remote caller
|
|
7
|
+
* can set, only fills fields it leaves unset, and is not read at all with `fromBaggage` false (docs/adr/0011).
|
|
8
|
+
*/
|
|
9
|
+
function agentAttributes(ctx, identity, fromBaggage = true) {
|
|
10
|
+
const baggage = fromBaggage ? propagation.getBaggage(ctx) : void 0;
|
|
11
|
+
const id = identity?.id ?? baggage?.getEntry("gen_ai.agent.id")?.value;
|
|
12
|
+
const name = identity?.name ?? baggage?.getEntry("gen_ai.agent.name")?.value;
|
|
10
13
|
const attributes = {};
|
|
11
14
|
if (id !== void 0) attributes[ATTR_GEN_AI_AGENT_ID] = id;
|
|
12
15
|
if (name !== void 0) attributes[ATTR_GEN_AI_AGENT_NAME] = name;
|
|
@@ -298,7 +301,7 @@ function resolveErrorMessageMode(mode) {
|
|
|
298
301
|
}
|
|
299
302
|
//#endregion
|
|
300
303
|
//#region src/version.ts
|
|
301
|
-
const VERSION = "0.
|
|
304
|
+
const VERSION = "0.2.0";
|
|
302
305
|
//#endregion
|
|
303
306
|
//#region src/tracker.ts
|
|
304
307
|
const INSTRUMENTATION_NAME = "@hashspan/core";
|
|
@@ -442,7 +445,7 @@ function createTxTracker(options = {}) {
|
|
|
442
445
|
[ATTR_BLOCKCHAIN_SYSTEM]: "evm",
|
|
443
446
|
[ATTR_BLOCKCHAIN_CHAIN_ID]: chainId,
|
|
444
447
|
[ATTR_BLOCKCHAIN_OPERATION_NAME]: operation,
|
|
445
|
-
...agentAttributes(ctx, options.agent)
|
|
448
|
+
...agentAttributes(ctx, options.agent, options.agentFromBaggage !== false)
|
|
446
449
|
});
|
|
447
450
|
const startSend = (input, parentCtx) => {
|
|
448
451
|
const parent = parentCtx ?? context.active();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hashspan/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"@opentelemetry/sdk-trace-node": "^2.11.0"
|
|
58
58
|
},
|
|
59
59
|
"engines": {
|
|
60
|
-
"node": ">=
|
|
60
|
+
"node": ">=22.3.0"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "tsdown",
|