@tangle-network/agent-eval 0.48.0 → 0.49.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
@@ -215,6 +215,13 @@ const next = await analyzeOptimizationResult(campaign, { researcher })
215
215
 
216
216
  | Subpath | Use for |
217
217
  | --- | --- |
218
+ | `@tangle-network/agent-eval/contract` | **LAND-tier surface** — `selfImprove`, `runCampaign`, `runImprovementLoop`, `runEval`, `Dispatch`, `Mutator`, `Gate`, `defaultProductionGate`, `gepaDriver`, `diffRuns`, storage backends. New code starts here. |
219
+ | `@tangle-network/agent-eval/hosted` | **EXPAND-tier surface** — `createHostedClient`, wire-format types, `HOSTED_WIRE_VERSION`. Ships eval-run events + trace spans to any orchestrator that speaks the spec. |
220
+ | `@tangle-network/agent-eval/adapters/otel` | OTel→hosted bridge — `createOtelBridge` forwards OTel-shape spans (TraceAI, OpenLLMetry, OTel SDK) into the hosted-tier ingest. |
221
+ | `@tangle-network/agent-eval/adapters/langchain` | LangChain executor adapter — wrap a LangChain runnable as a `Dispatch`. |
222
+ | `@tangle-network/agent-eval/adapters/http` | Distributed driver — `httpDispatch` + `runDispatchServer` for cross-machine campaigns. |
223
+ | `@tangle-network/agent-eval/campaign` | Lower-level campaign primitives — `runCampaign`, driver implementations, storage. |
224
+ | `@tangle-network/agent-eval/multishot` | Multi-shot optimization primitives. |
218
225
  | `@tangle-network/agent-eval/control` | `observe → validate → decide → act`, action policy, propose/review loops |
219
226
  | `@tangle-network/agent-eval/traces` | trace stores, emitters, TraceAnalyst, replay |
220
227
  | `@tangle-network/agent-eval/optimization` | feedback trajectories, multi-shot, prompt evolution, GEPA, EvalCampaign |
@@ -2,39 +2,35 @@ import { TraceSpanEvent, HostedClient } from '../hosted/index.js';
2
2
  import '../types-8u72Gc76.js';
3
3
 
4
4
  /**
5
- * # `@tangle-network/agent-eval/adapters/traceai` — OTel→hosted bridge.
5
+ * # `@tangle-network/agent-eval/adapters/otel` — OTel→hosted bridge.
6
6
  *
7
- * Forwards OpenTelemetry-shaped spans (from `future-agi/traceai`, from the
8
- * OTel SDK directly, or from any library that emits OTel `ReadableSpan`s)
9
- * into the hosted-tier ingest endpoint via `createHostedClient`.
10
- *
11
- * **Why this exists:** future-agi ships the strongest OTel-native
12
- * instrumentation library in the TypeScript-agent ecosystem. Partners using
13
- * traceai for tracing should be able to plug it into Tangle Intelligence
14
- * with one config line — not rebuild OTel emission from scratch. Adapter
15
- * shape applies equally to any OTel SpanProcessor pipeline.
7
+ * Forwards OpenTelemetry-shaped spans into the hosted-tier ingest endpoint
8
+ * via `createHostedClient`. Works with anything that emits OTel
9
+ * `ReadableSpan`s `@opentelemetry/sdk-trace-base` directly, OpenLLMetry,
10
+ * Phoenix's OTel exporter, TraceAI from future-agi, any OTel collector
11
+ * pipeline.
16
12
  *
17
13
  * **Pattern:**
18
14
  *
19
15
  * ```ts
20
16
  * import { createHostedClient } from '@tangle-network/agent-eval/hosted'
21
- * import { createTraceAiBridge } from '@tangle-network/agent-eval/adapters/traceai'
17
+ * import { createOtelBridge } from '@tangle-network/agent-eval/adapters/otel'
22
18
  *
23
19
  * const client = createHostedClient({ endpoint, apiKey, tenantId })
24
- * const bridge = createTraceAiBridge({ client, defaultRunId: substrateRunId })
20
+ * const bridge = createOtelBridge({ client, defaultRunId: substrateRunId })
25
21
  *
26
22
  * // Wherever your OTel SpanProcessor hands you a finished span:
27
- * processor.onEnd = (span) => bridge.ingest([span])
23
+ * processor.onEnd = (span) => { void bridge.ingest([span]) }
28
24
  * // …or in a SpanProcessor.onShutdown / batch flush:
29
25
  * await bridge.ingest(batchedSpans)
30
26
  * ```
31
27
  *
32
28
  * No `@opentelemetry/*` dependency is declared here — the adapter accepts
33
29
  * a structurally-typed `OtelLikeSpan`. This keeps the substrate dep graph
34
- * lean while remaining compatible with OTel SDK `ReadableSpan` instances
35
- * and with traceai's emitted spans. If a consumer's span shape differs
36
- * (e.g. `parentSpanId` as a top-level field rather than via
37
- * `parentSpanContext()`), the adapter accepts both forms.
30
+ * lean while remaining compatible with any OTel SDK that produces
31
+ * `ReadableSpan`-shaped instances. If a consumer's span shape exposes
32
+ * `parentSpanId` as a top-level field rather than via `parentSpanContext()`,
33
+ * the adapter accepts both forms.
38
34
  */
39
35
 
40
36
  /**
@@ -47,11 +43,10 @@ type HrTime = [number, number];
47
43
  declare const OTEL_STATUS_UNSET = 0;
48
44
  declare const OTEL_STATUS_OK = 1;
49
45
  declare const OTEL_STATUS_ERROR = 2;
50
- type OtelAttributeValue = string | number | boolean | null | undefined;
46
+ type OtelAttributeValue = string | number | boolean | null | undefined | Array<string> | Array<number> | Array<boolean>;
51
47
  /**
52
48
  * Structural surface compatible with `@opentelemetry/sdk-trace-base`'s
53
- * `ReadableSpan`. Consumers pass instances they get from their OTel SDK
54
- * (or from `future-agi/traceai`, which produces spans of this shape).
49
+ * `ReadableSpan`. Consumers pass instances they get from their OTel SDK.
55
50
  */
56
51
  interface OtelLikeSpan {
57
52
  spanContext: () => {
@@ -59,9 +54,8 @@ interface OtelLikeSpan {
59
54
  spanId: string;
60
55
  traceFlags?: number;
61
56
  };
62
- /** Set on the span itself by some SDKs (legacy / OTLP-shape). Some SDKs
63
- * expose the parent via `parentSpanContext()` instead — the adapter
64
- * checks both. */
57
+ /** Set on the span itself by some SDKs (OTLP-shape). Other SDKs expose
58
+ * the parent via `parentSpanContext()` instead — the adapter checks both. */
65
59
  parentSpanId?: string;
66
60
  parentSpanContext?: () => {
67
61
  spanId: string;
@@ -82,7 +76,7 @@ interface OtelLikeSpan {
82
76
  }
83
77
  /** `[seconds, nanoseconds]` → unix-nano number. */
84
78
  declare function hrTimeToUnixNano(hr: HrTime): number;
85
- interface TraceAiBridgeOptions {
79
+ interface OtelBridgeOptions {
86
80
  /** Hosted client to forward spans to. */
87
81
  client: HostedClient;
88
82
  /** When set, spans missing a `tangle.runId` attribute receive this value
@@ -97,13 +91,13 @@ interface TraceAiBridgeOptions {
97
91
  * this when you need backpressure or to spill to a fallback. */
98
92
  onError?: (err: unknown, batch: TraceSpanEvent[]) => void | Promise<void>;
99
93
  }
100
- interface TraceAiBridge {
94
+ interface OtelBridge {
101
95
  /** Convert + ingest a batch of OTel-shape spans. */
102
96
  ingest(spans: OtelLikeSpan[]): Promise<void>;
103
97
  /** Convert one OTel span to the wire-format event. Useful for tests or
104
98
  * custom batching pipelines. */
105
99
  spanToEvent(span: OtelLikeSpan): TraceSpanEvent;
106
100
  }
107
- declare function createTraceAiBridge(opts: TraceAiBridgeOptions): TraceAiBridge;
101
+ declare function createOtelBridge(opts: OtelBridgeOptions): OtelBridge;
108
102
 
109
- export { type HrTime, OTEL_STATUS_ERROR, OTEL_STATUS_OK, OTEL_STATUS_UNSET, type OtelAttributeValue, type OtelLikeSpan, type TraceAiBridge, type TraceAiBridgeOptions, createTraceAiBridge, hrTimeToUnixNano };
103
+ export { type HrTime, OTEL_STATUS_ERROR, OTEL_STATUS_OK, OTEL_STATUS_UNSET, type OtelAttributeValue, type OtelBridge, type OtelBridgeOptions, type OtelLikeSpan, createOtelBridge, hrTimeToUnixNano };
@@ -1,6 +1,6 @@
1
1
  import "../chunk-NSBPE2FW.js";
2
2
 
3
- // src/adapters/traceai.ts
3
+ // src/adapters/otel.ts
4
4
  var OTEL_STATUS_UNSET = 0;
5
5
  var OTEL_STATUS_OK = 1;
6
6
  var OTEL_STATUS_ERROR = 2;
@@ -20,6 +20,10 @@ function cleanAttributes(attrs) {
20
20
  if (v === null || v === void 0) continue;
21
21
  if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
22
22
  out[k] = v;
23
+ continue;
24
+ }
25
+ if (Array.isArray(v)) {
26
+ out[k] = JSON.stringify(v);
23
27
  }
24
28
  }
25
29
  return out;
@@ -37,10 +41,10 @@ function resolveParentSpanId(span) {
37
41
  const ctx = span.parentSpanContext?.();
38
42
  return ctx?.spanId;
39
43
  }
40
- function createTraceAiBridge(opts) {
44
+ function createOtelBridge(opts) {
41
45
  const batchSize = opts.batchSize ?? 200;
42
46
  const onError = opts.onError ?? ((err) => {
43
- console.warn("[traceai-bridge] ingest batch failed:", err);
47
+ console.warn("[otel-bridge] ingest batch failed:", err);
44
48
  });
45
49
  function convert(span) {
46
50
  const ctx = span.spanContext();
@@ -100,7 +104,7 @@ export {
100
104
  OTEL_STATUS_ERROR,
101
105
  OTEL_STATUS_OK,
102
106
  OTEL_STATUS_UNSET,
103
- createTraceAiBridge,
107
+ createOtelBridge,
104
108
  hrTimeToUnixNano
105
109
  };
106
- //# sourceMappingURL=traceai.js.map
110
+ //# sourceMappingURL=otel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/otel.ts"],"sourcesContent":["/**\n * # `@tangle-network/agent-eval/adapters/otel` — OTel→hosted bridge.\n *\n * Forwards OpenTelemetry-shaped spans into the hosted-tier ingest endpoint\n * via `createHostedClient`. Works with anything that emits OTel\n * `ReadableSpan`s — `@opentelemetry/sdk-trace-base` directly, OpenLLMetry,\n * Phoenix's OTel exporter, TraceAI from future-agi, any OTel collector\n * pipeline.\n *\n * **Pattern:**\n *\n * ```ts\n * import { createHostedClient } from '@tangle-network/agent-eval/hosted'\n * import { createOtelBridge } from '@tangle-network/agent-eval/adapters/otel'\n *\n * const client = createHostedClient({ endpoint, apiKey, tenantId })\n * const bridge = createOtelBridge({ client, defaultRunId: substrateRunId })\n *\n * // Wherever your OTel SpanProcessor hands you a finished span:\n * processor.onEnd = (span) => { void bridge.ingest([span]) }\n * // …or in a SpanProcessor.onShutdown / batch flush:\n * await bridge.ingest(batchedSpans)\n * ```\n *\n * No `@opentelemetry/*` dependency is declared here — the adapter accepts\n * a structurally-typed `OtelLikeSpan`. This keeps the substrate dep graph\n * lean while remaining compatible with any OTel SDK that produces\n * `ReadableSpan`-shaped instances. If a consumer's span shape exposes\n * `parentSpanId` as a top-level field rather than via `parentSpanContext()`,\n * the adapter accepts both forms.\n */\n\nimport type { HostedClient } from '../hosted/client'\nimport type { TraceSpanEvent } from '../hosted/types'\n\n// ── OTel-compatible structural types ─────────────────────────────────\n\n/**\n * `[seconds, nanoseconds]` — the OTel SDK's `HrTime` shape. Spans emitted\n * by the OTel SDK carry timestamps in this representation; we convert to\n * a single unix-nano number for the wire format.\n */\nexport type HrTime = [number, number]\n\n/** Standard OTel `SpanStatusCode` numeric values: 0 = UNSET, 1 = OK, 2 = ERROR. */\nexport const OTEL_STATUS_UNSET = 0\nexport const OTEL_STATUS_OK = 1\nexport const OTEL_STATUS_ERROR = 2\n\nexport type OtelAttributeValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | Array<string>\n | Array<number>\n | Array<boolean>\n\n/**\n * Structural surface compatible with `@opentelemetry/sdk-trace-base`'s\n * `ReadableSpan`. Consumers pass instances they get from their OTel SDK.\n */\nexport interface OtelLikeSpan {\n spanContext: () => { traceId: string; spanId: string; traceFlags?: number }\n /** Set on the span itself by some SDKs (OTLP-shape). Other SDKs expose\n * the parent via `parentSpanContext()` instead — the adapter checks both. */\n parentSpanId?: string\n parentSpanContext?: () => { spanId: string } | undefined\n name: string\n startTime: HrTime\n endTime: HrTime\n attributes: Record<string, OtelAttributeValue>\n events?: Array<{\n name: string\n time: HrTime\n attributes?: Record<string, OtelAttributeValue>\n }>\n status?: { code: number; message?: string }\n}\n\n// ── Conversion ───────────────────────────────────────────────────────\n\n/** `[seconds, nanoseconds]` → unix-nano number. */\nexport function hrTimeToUnixNano(hr: HrTime): number {\n const [seconds, nanos] = hr\n return seconds * 1_000_000_000 + nanos\n}\n\nfunction statusCodeName(code: number | undefined): 'OK' | 'ERROR' | 'UNSET' {\n if (code === OTEL_STATUS_OK) return 'OK'\n if (code === OTEL_STATUS_ERROR) return 'ERROR'\n return 'UNSET'\n}\n\n/**\n * Normalise OTel attributes to the scalar shape the wire format accepts.\n *\n * - null / undefined → dropped (no representation in the wire format)\n * - string / number / boolean → passed through\n * - Array<scalar> → JSON-stringified (the wire format is scalar-only;\n * dropping arrays would silently lose information, so we serialise\n * them deterministically)\n *\n * OTel SDK / TraceAI emit array attributes routinely (e.g. `tool.names`,\n * `http.request.header.set-cookie`). Stringification keeps the data flowing\n * through to the dashboard; consumers parse with `JSON.parse` if they need\n * the structured form back.\n */\nfunction cleanAttributes(\n attrs: Record<string, OtelAttributeValue> | undefined,\n): Record<string, string | number | boolean> {\n const out: Record<string, string | number | boolean> = {}\n if (!attrs) return out\n for (const [k, v] of Object.entries(attrs)) {\n if (v === null || v === undefined) continue\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {\n out[k] = v\n continue\n }\n if (Array.isArray(v)) {\n out[k] = JSON.stringify(v)\n }\n }\n return out\n}\n\nfunction readPivotString(\n attrs: Record<string, OtelAttributeValue>,\n key: string,\n): string | undefined {\n const v = attrs[key]\n return typeof v === 'string' ? v : undefined\n}\n\nfunction readPivotNumber(\n attrs: Record<string, OtelAttributeValue>,\n key: string,\n): number | undefined {\n const v = attrs[key]\n return typeof v === 'number' ? v : undefined\n}\n\nfunction resolveParentSpanId(span: OtelLikeSpan): string | undefined {\n if (span.parentSpanId) return span.parentSpanId\n const ctx = span.parentSpanContext?.()\n return ctx?.spanId\n}\n\n// ── Bridge ───────────────────────────────────────────────────────────\n\nexport interface OtelBridgeOptions {\n /** Hosted client to forward spans to. */\n client: HostedClient\n /** When set, spans missing a `tangle.runId` attribute receive this value\n * on the way out. Useful when the OTel emitter doesn't know which\n * substrate run it's serving. */\n defaultRunId?: string\n /** Max spans per ingest call. Default 200. The hosted ingest endpoint\n * caps at 5000 per call; we batch smaller by default to keep individual\n * retries cheap. */\n batchSize?: number\n /** Called when a batch fails to ingest. Defaults to a console.warn. Hook\n * this when you need backpressure or to spill to a fallback. */\n onError?: (err: unknown, batch: TraceSpanEvent[]) => void | Promise<void>\n}\n\nexport interface OtelBridge {\n /** Convert + ingest a batch of OTel-shape spans. */\n ingest(spans: OtelLikeSpan[]): Promise<void>\n /** Convert one OTel span to the wire-format event. Useful for tests or\n * custom batching pipelines. */\n spanToEvent(span: OtelLikeSpan): TraceSpanEvent\n}\n\nexport function createOtelBridge(opts: OtelBridgeOptions): OtelBridge {\n const batchSize = opts.batchSize ?? 200\n const onError =\n opts.onError ??\n ((err) => {\n console.warn('[otel-bridge] ingest batch failed:', err)\n })\n\n function convert(span: OtelLikeSpan): TraceSpanEvent {\n const ctx = span.spanContext()\n const attributes = cleanAttributes(span.attributes)\n // Pull pivot attributes off the cleaned attribute map so they round-trip\n // through the wire format's first-class fields. They REMAIN in\n // `attributes` as well so downstream OTel viewers see the same values.\n const runId = readPivotString(attributes, 'tangle.runId') ?? opts.defaultRunId\n const scenarioId = readPivotString(attributes, 'tangle.scenarioId')\n const cellId = readPivotString(attributes, 'tangle.cellId')\n const generation = readPivotNumber(attributes, 'tangle.generation')\n\n if (runId && !attributes['tangle.runId']) {\n attributes['tangle.runId'] = runId\n }\n\n const event: TraceSpanEvent = {\n traceId: ctx.traceId,\n spanId: ctx.spanId,\n name: span.name,\n startTimeUnixNano: hrTimeToUnixNano(span.startTime),\n endTimeUnixNano: hrTimeToUnixNano(span.endTime),\n attributes,\n }\n const parentSpanId = resolveParentSpanId(span)\n if (parentSpanId) event.parentSpanId = parentSpanId\n if (span.events && span.events.length > 0) {\n event.events = span.events.map((e) => {\n const eventAttrs = cleanAttributes(e.attributes)\n const node: {\n timeUnixNano: number\n name: string\n attributes?: Record<string, string | number | boolean>\n } = {\n timeUnixNano: hrTimeToUnixNano(e.time),\n name: e.name,\n }\n if (Object.keys(eventAttrs).length > 0) node.attributes = eventAttrs\n return node\n })\n }\n if (span.status) {\n event.status = { code: statusCodeName(span.status.code), message: span.status.message }\n }\n if (runId) event['tangle.runId'] = runId\n if (scenarioId) event['tangle.scenarioId'] = scenarioId\n if (cellId) event['tangle.cellId'] = cellId\n if (generation !== undefined) event['tangle.generation'] = generation\n return event\n }\n\n async function ingest(spans: OtelLikeSpan[]): Promise<void> {\n if (spans.length === 0) return\n const events = spans.map(convert)\n for (let i = 0; i < events.length; i += batchSize) {\n const batch = events.slice(i, i + batchSize)\n try {\n await opts.client.ingestTraces(batch)\n } catch (err) {\n await onError(err, batch)\n }\n }\n }\n\n return { ingest, spanToEvent: convert }\n}\n"],"mappings":"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","names":[]}
package/dist/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "@tangle-network/agent-eval — wire protocol",
5
- "version": "0.48.0",
5
+ "version": "0.49.0",
6
6
  "description": "HTTP and stdio RPC interface to agent-eval. The TypeScript runtime is the source of truth; this spec is the contract that cross-language clients (Python, Rust, Go) generate from.\n\nWire-protocol version: 1.0.0. Bumps on breaking changes to request/response schemas.",
7
7
  "contact": {
8
8
  "name": "Tangle Network",
@@ -35,7 +35,7 @@ it*. Unified at the trace level, you see both as one timeline per cell.
35
35
  - Compose: register TraceAI's instrumentations on the global tracer
36
36
  provider, then either point both at your OTLP collector or at
37
37
  TraceAI's hosted backend if you want their UI.
38
- - **Or use the bridge: `@tangle-network/agent-eval/adapters/traceai`.**
38
+ - **Or use the bridge: `@tangle-network/agent-eval/adapters/otel`.**
39
39
  Forwards finished OTel spans (`ReadableSpan` shape) directly into the
40
40
  hosted-tier ingest, lifting `tangle.runId` / `tangle.scenarioId` /
41
41
  `tangle.cellId` / `tangle.generation` to first-class wire fields so
@@ -43,10 +43,10 @@ it*. Unified at the trace level, you see both as one timeline per cell.
43
43
  at the substrate; consumers pass spans from their own OTel SDK.
44
44
  ```ts
45
45
  import { createHostedClient } from '@tangle-network/agent-eval/hosted'
46
- import { createTraceAiBridge } from '@tangle-network/agent-eval/adapters/traceai'
46
+ import { createOtelBridge } from '@tangle-network/agent-eval/adapters/otel'
47
47
 
48
48
  const client = createHostedClient({ endpoint, apiKey, tenantId })
49
- const bridge = createTraceAiBridge({ client, defaultRunId: substrateRunId })
49
+ const bridge = createOtelBridge({ client, defaultRunId: substrateRunId })
50
50
  processor.onEnd = (span) => { void bridge.ingest([span]) }
51
51
  // ...or call `bridge.ingest(batch)` from a SpanProcessor.onShutdown.
52
52
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-eval",
3
- "version": "0.48.0",
3
+ "version": "0.49.0",
4
4
  "description": "Substrate for self-improving agents: traces, verifiable rewards, preferences, GEPA / reflective mutation, auto-research, replay, sequential anytime-valid stats, and release gates.",
5
5
  "homepage": "https://github.com/tangle-network/agent-eval#readme",
6
6
  "repository": {
@@ -119,10 +119,10 @@
119
119
  "import": "./dist/adapters/http.js",
120
120
  "default": "./dist/adapters/http.js"
121
121
  },
122
- "./adapters/traceai": {
123
- "types": "./dist/adapters/traceai.d.ts",
124
- "import": "./dist/adapters/traceai.js",
125
- "default": "./dist/adapters/traceai.js"
122
+ "./adapters/otel": {
123
+ "types": "./dist/adapters/otel.d.ts",
124
+ "import": "./dist/adapters/otel.js",
125
+ "default": "./dist/adapters/otel.js"
126
126
  },
127
127
  "./hosted": {
128
128
  "types": "./dist/hosted/index.d.ts",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/adapters/traceai.ts"],"sourcesContent":["/**\n * # `@tangle-network/agent-eval/adapters/traceai` — OTel→hosted bridge.\n *\n * Forwards OpenTelemetry-shaped spans (from `future-agi/traceai`, from the\n * OTel SDK directly, or from any library that emits OTel `ReadableSpan`s)\n * into the hosted-tier ingest endpoint via `createHostedClient`.\n *\n * **Why this exists:** future-agi ships the strongest OTel-native\n * instrumentation library in the TypeScript-agent ecosystem. Partners using\n * traceai for tracing should be able to plug it into Tangle Intelligence\n * with one config line — not rebuild OTel emission from scratch. Adapter\n * shape applies equally to any OTel SpanProcessor pipeline.\n *\n * **Pattern:**\n *\n * ```ts\n * import { createHostedClient } from '@tangle-network/agent-eval/hosted'\n * import { createTraceAiBridge } from '@tangle-network/agent-eval/adapters/traceai'\n *\n * const client = createHostedClient({ endpoint, apiKey, tenantId })\n * const bridge = createTraceAiBridge({ client, defaultRunId: substrateRunId })\n *\n * // Wherever your OTel SpanProcessor hands you a finished span:\n * processor.onEnd = (span) => bridge.ingest([span])\n * // …or in a SpanProcessor.onShutdown / batch flush:\n * await bridge.ingest(batchedSpans)\n * ```\n *\n * No `@opentelemetry/*` dependency is declared here — the adapter accepts\n * a structurally-typed `OtelLikeSpan`. This keeps the substrate dep graph\n * lean while remaining compatible with OTel SDK `ReadableSpan` instances\n * and with traceai's emitted spans. If a consumer's span shape differs\n * (e.g. `parentSpanId` as a top-level field rather than via\n * `parentSpanContext()`), the adapter accepts both forms.\n */\n\nimport type { HostedClient } from '../hosted/client'\nimport type { TraceSpanEvent } from '../hosted/types'\n\n// ── OTel-compatible structural types ─────────────────────────────────\n\n/**\n * `[seconds, nanoseconds]` — the OTel SDK's `HrTime` shape. Spans emitted\n * by the OTel SDK carry timestamps in this representation; we convert to\n * a single unix-nano number for the wire format.\n */\nexport type HrTime = [number, number]\n\n/** Standard OTel `SpanStatusCode` numeric values: 0 = UNSET, 1 = OK, 2 = ERROR. */\nexport const OTEL_STATUS_UNSET = 0\nexport const OTEL_STATUS_OK = 1\nexport const OTEL_STATUS_ERROR = 2\n\nexport type OtelAttributeValue = string | number | boolean | null | undefined\n\n/**\n * Structural surface compatible with `@opentelemetry/sdk-trace-base`'s\n * `ReadableSpan`. Consumers pass instances they get from their OTel SDK\n * (or from `future-agi/traceai`, which produces spans of this shape).\n */\nexport interface OtelLikeSpan {\n spanContext: () => { traceId: string; spanId: string; traceFlags?: number }\n /** Set on the span itself by some SDKs (legacy / OTLP-shape). Some SDKs\n * expose the parent via `parentSpanContext()` instead — the adapter\n * checks both. */\n parentSpanId?: string\n parentSpanContext?: () => { spanId: string } | undefined\n name: string\n startTime: HrTime\n endTime: HrTime\n attributes: Record<string, OtelAttributeValue>\n events?: Array<{\n name: string\n time: HrTime\n attributes?: Record<string, OtelAttributeValue>\n }>\n status?: { code: number; message?: string }\n}\n\n// ── Conversion ───────────────────────────────────────────────────────\n\n/** `[seconds, nanoseconds]` → unix-nano number. */\nexport function hrTimeToUnixNano(hr: HrTime): number {\n const [seconds, nanos] = hr\n return seconds * 1_000_000_000 + nanos\n}\n\nfunction statusCodeName(code: number | undefined): 'OK' | 'ERROR' | 'UNSET' {\n if (code === OTEL_STATUS_OK) return 'OK'\n if (code === OTEL_STATUS_ERROR) return 'ERROR'\n return 'UNSET'\n}\n\n/** Drop null/undefined attribute values; keep string/number/boolean. */\nfunction cleanAttributes(\n attrs: Record<string, OtelAttributeValue> | undefined,\n): Record<string, string | number | boolean> {\n const out: Record<string, string | number | boolean> = {}\n if (!attrs) return out\n for (const [k, v] of Object.entries(attrs)) {\n if (v === null || v === undefined) continue\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {\n out[k] = v\n }\n }\n return out\n}\n\nfunction readPivotString(\n attrs: Record<string, OtelAttributeValue>,\n key: string,\n): string | undefined {\n const v = attrs[key]\n return typeof v === 'string' ? v : undefined\n}\n\nfunction readPivotNumber(\n attrs: Record<string, OtelAttributeValue>,\n key: string,\n): number | undefined {\n const v = attrs[key]\n return typeof v === 'number' ? v : undefined\n}\n\nfunction resolveParentSpanId(span: OtelLikeSpan): string | undefined {\n if (span.parentSpanId) return span.parentSpanId\n const ctx = span.parentSpanContext?.()\n return ctx?.spanId\n}\n\n// ── Bridge ───────────────────────────────────────────────────────────\n\nexport interface TraceAiBridgeOptions {\n /** Hosted client to forward spans to. */\n client: HostedClient\n /** When set, spans missing a `tangle.runId` attribute receive this value\n * on the way out. Useful when the OTel emitter doesn't know which\n * substrate run it's serving. */\n defaultRunId?: string\n /** Max spans per ingest call. Default 200. The hosted ingest endpoint\n * caps at 5000 per call; we batch smaller by default to keep individual\n * retries cheap. */\n batchSize?: number\n /** Called when a batch fails to ingest. Defaults to a console.warn. Hook\n * this when you need backpressure or to spill to a fallback. */\n onError?: (err: unknown, batch: TraceSpanEvent[]) => void | Promise<void>\n}\n\nexport interface TraceAiBridge {\n /** Convert + ingest a batch of OTel-shape spans. */\n ingest(spans: OtelLikeSpan[]): Promise<void>\n /** Convert one OTel span to the wire-format event. Useful for tests or\n * custom batching pipelines. */\n spanToEvent(span: OtelLikeSpan): TraceSpanEvent\n}\n\nexport function createTraceAiBridge(opts: TraceAiBridgeOptions): TraceAiBridge {\n const batchSize = opts.batchSize ?? 200\n const onError =\n opts.onError ??\n ((err) => {\n console.warn('[traceai-bridge] ingest batch failed:', err)\n })\n\n function convert(span: OtelLikeSpan): TraceSpanEvent {\n const ctx = span.spanContext()\n const attributes = cleanAttributes(span.attributes)\n // Pull pivot attributes off the cleaned attribute map so they round-trip\n // through the wire format's first-class fields. They REMAIN in\n // `attributes` as well so downstream OTel viewers see the same values.\n const runId = readPivotString(attributes, 'tangle.runId') ?? opts.defaultRunId\n const scenarioId = readPivotString(attributes, 'tangle.scenarioId')\n const cellId = readPivotString(attributes, 'tangle.cellId')\n const generation = readPivotNumber(attributes, 'tangle.generation')\n\n if (runId && !attributes['tangle.runId']) {\n attributes['tangle.runId'] = runId\n }\n\n const event: TraceSpanEvent = {\n traceId: ctx.traceId,\n spanId: ctx.spanId,\n name: span.name,\n startTimeUnixNano: hrTimeToUnixNano(span.startTime),\n endTimeUnixNano: hrTimeToUnixNano(span.endTime),\n attributes,\n }\n const parentSpanId = resolveParentSpanId(span)\n if (parentSpanId) event.parentSpanId = parentSpanId\n if (span.events && span.events.length > 0) {\n event.events = span.events.map((e) => {\n const eventAttrs = cleanAttributes(e.attributes)\n const node: {\n timeUnixNano: number\n name: string\n attributes?: Record<string, string | number | boolean>\n } = {\n timeUnixNano: hrTimeToUnixNano(e.time),\n name: e.name,\n }\n if (Object.keys(eventAttrs).length > 0) node.attributes = eventAttrs\n return node\n })\n }\n if (span.status) {\n event.status = { code: statusCodeName(span.status.code), message: span.status.message }\n }\n if (runId) event['tangle.runId'] = runId\n if (scenarioId) event['tangle.scenarioId'] = scenarioId\n if (cellId) event['tangle.cellId'] = cellId\n if (generation !== undefined) event['tangle.generation'] = generation\n return event\n }\n\n async function ingest(spans: OtelLikeSpan[]): Promise<void> {\n if (spans.length === 0) return\n const events = spans.map(convert)\n for (let i = 0; i < events.length; i += batchSize) {\n const batch = events.slice(i, i + batchSize)\n try {\n await opts.client.ingestTraces(batch)\n } catch (err) {\n await onError(err, batch)\n }\n }\n }\n\n return { ingest, spanToEvent: convert }\n}\n"],"mappings":"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","names":[]}