@robota-sdk/agent-telemetry-otel 0.0.0-stage → 3.0.0-beta.101
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/CHANGELOG.md +94 -0
- package/LICENSE +661 -0
- package/README.md +130 -2
- package/dist/node/index.cjs +1 -0
- package/dist/node/index.d.cts +142 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +142 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +2 -0
- package/dist/node/index.js.map +1 -0
- package/package.json +79 -3
- package/src/__tests__/metrics.test.ts +247 -0
- package/src/__tests__/projection.test.ts +262 -0
- package/src/__tests__/run-tracing.test.ts +230 -0
- package/src/index.ts +31 -0
- package/src/metrics.ts +140 -0
- package/src/projection.ts +216 -0
- package/src/run-tracing.ts +255 -0
- package/src/span-ids.ts +47 -0
- package/src/timing.ts +47 -0
package/README.md
CHANGED
|
@@ -1,3 +1,131 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @robota-sdk/agent-telemetry-otel
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
OpenTelemetry tracing and GenAI metrics for Robota agents, in three forms:
|
|
4
|
+
|
|
5
|
+
- **`createOtelTracing`** records an agent run's provider calls and tool bodies as spans on your own
|
|
6
|
+
tracer, under your span, with the span IDs Robota sent to providers and subprocesses in `traceparent`.
|
|
7
|
+
- **`projectLivePromptTrace`** maps one `ILivePromptTraceBatch` (from
|
|
8
|
+
`@robota-sdk/agent-interface-analytics`) onto span shapes as plain data, for a host that writes
|
|
9
|
+
spans itself.
|
|
10
|
+
- **`projectLivePromptMetricSamples`** maps the same batch onto the OpenTelemetry GenAI metrics as plain
|
|
11
|
+
samples, for a host that records or exports metrics itself.
|
|
12
|
+
|
|
13
|
+
All are content-free: no prompt, completion, tool argument or tool result becomes a span or metric
|
|
14
|
+
attribute. None exports anything; your OpenTelemetry SDK and exporter do.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @robota-sdk/agent-telemetry-otel @opentelemetry/api
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Requires Node.js 22.12 or later.
|
|
23
|
+
|
|
24
|
+
## Tracing a run
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { ConversationAgent } from '@robota-sdk/agent-core';
|
|
28
|
+
import { TracerProvider, BatchSpanProcessor } from '@opentelemetry/sdk-trace';
|
|
29
|
+
import { createOtelTracing, createStructuralIdGenerator } from '@robota-sdk/agent-telemetry-otel';
|
|
30
|
+
|
|
31
|
+
const idGenerator = createStructuralIdGenerator();
|
|
32
|
+
const provider = new TracerProvider({ idGenerator, spanProcessors: [new BatchSpanProcessor({ exporter })] });
|
|
33
|
+
const tracer = provider.getTracer('my-app');
|
|
34
|
+
|
|
35
|
+
const tracing = createOtelTracing({ tracer, idGenerator, mode: 'gen_ai' });
|
|
36
|
+
const agent = new ConversationAgent({ /* ... */, plugins: [tracing.plugin] });
|
|
37
|
+
|
|
38
|
+
const span = tracer.startSpan('invoke_agent my-agent');
|
|
39
|
+
const run = tracing.startRun(span.spanContext(), { allowedOrigins: ['https://api.anthropic.com'] });
|
|
40
|
+
try {
|
|
41
|
+
await agent.run(prompt, { traceContext: run.traceContext, onExecutionEvent: run.onExecutionEvent });
|
|
42
|
+
} finally {
|
|
43
|
+
run.end();
|
|
44
|
+
span.end();
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Register `tracing.plugin` once; it records tool bodies for every run started with `startRun`. Each
|
|
49
|
+
run's provider calls come from its `onExecutionEvent`, so pass the run's callback to that run (wrap
|
|
50
|
+
it if you also handle execution events). The host span is yours: name it, add `gen_ai.agent.name` or
|
|
51
|
+
`gen_ai.conversation.id`, and end it.
|
|
52
|
+
|
|
53
|
+
`allowedOrigins` lists the exact origins that may receive `traceparent`; with none, provider calls
|
|
54
|
+
send nothing and are still traced. `subprocessClasses: ['shell', 'hooks']` lets shell children and
|
|
55
|
+
command hooks inherit the trace.
|
|
56
|
+
|
|
57
|
+
**Span IDs.** Robota fixes each provider call's and tool body's span ID before the operation runs,
|
|
58
|
+
so a recipient's spans name it as their parent. `createStructuralIdGenerator()` lets your
|
|
59
|
+
`TracerProvider` record the span under that ID; it gives every other span a random one. Without it,
|
|
60
|
+
spans still share your trace and parent, but a recipient's spans point at a span ID your trace does
|
|
61
|
+
not contain.
|
|
62
|
+
|
|
63
|
+
## Projecting a trace batch
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { projectLivePromptTrace } from '@robota-sdk/agent-telemetry-otel';
|
|
67
|
+
|
|
68
|
+
// `batch` is an ILivePromptTraceBatch, e.g. from the framework's `livePromptTrace` port.
|
|
69
|
+
const { schemaUrl, root, children } = projectLivePromptTrace(batch, {
|
|
70
|
+
mode: 'gen_ai',
|
|
71
|
+
agentName: 'my-agent',
|
|
72
|
+
});
|
|
73
|
+
// root: { name: 'invoke_agent my-agent', kind: 'internal', traceId, spanId, startedAt, endedAt, status, attributes }
|
|
74
|
+
// children: [{ name: 'chat claude-sonnet-4-5', kind: 'client', parentSpanId: root.spanId, ... }, ...]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Each span carries `name`, `kind` (`internal` or `client`), `traceId`, `spanId`, `parentSpanId`
|
|
78
|
+
(children only), `startedAt` and `endedAt` (ISO timestamps), `status` (`error` for a failed
|
|
79
|
+
outcome) and `attributes`. Permission decisions in the batch get no span.
|
|
80
|
+
|
|
81
|
+
## Modes
|
|
82
|
+
|
|
83
|
+
| Mode | Span names | Attributes |
|
|
84
|
+
| ------------ | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
85
|
+
| `agent` | `agent.prompt_execution`, `agent.provider_call`, `agent.tool_body` | Robota's `agent.*` names, as the CLI's OTLP export uses them |
|
|
86
|
+
| `gen_ai` | `invoke_agent {agent}`, `chat {model}`, `execute_tool {tool}` | OpenTelemetry GenAI `gen_ai.*` names where the conventions define the fact; Robota-only facts (turn, outcome, timing, omitted counts, usage provenance, request ID) stay `agent.*` |
|
|
87
|
+
| `gen_ai/dup` | GenAI names | Both sets, so dashboards can move from one to the other |
|
|
88
|
+
|
|
89
|
+
In the GenAI modes the provider-call span is `client` and `schemaUrl` is `GEN_AI_SCHEMA_URL`, the
|
|
90
|
+
GenAI semantic-convention version the names follow. `gen_ai.provider.name` is set only for providers
|
|
91
|
+
whose GenAI name is certain; `agent.provider.id` always names the Robota provider.
|
|
92
|
+
|
|
93
|
+
## Metrics
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { projectLivePromptMetricSamples } from '@robota-sdk/agent-telemetry-otel';
|
|
97
|
+
|
|
98
|
+
const samples = projectLivePromptMetricSamples(batch, { mode: 'gen_ai', agentName: 'my-agent' });
|
|
99
|
+
// [{ name: 'gen_ai.client.inference.duration', instrument: 'histogram', unit: 's',
|
|
100
|
+
// value: 1.2, boundaries: [0.01, ...], attributes: { 'gen_ai.operation.name': 'chat', ... } }, ...]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Each sample is one measurement: a `counter` increment, or a `histogram` value with the explicit
|
|
104
|
+
`boundaries` the GenAI conventions recommend. Record each on an instrument of that name, unit and
|
|
105
|
+
boundaries; the attributes are the measurement's labels. `mode` is `gen_ai` or `gen_ai/dup`, as for
|
|
106
|
+
spans (there is no `agent` form of these metrics).
|
|
107
|
+
|
|
108
|
+
| Metric | Instrument, unit | Per |
|
|
109
|
+
| ------------------------------------------------------------------- | ---------------------- | --------------------- |
|
|
110
|
+
| `gen_ai.client.inference.duration` | histogram, `s` | invoked provider call |
|
|
111
|
+
| `gen_ai.client.inference.operation.input_tokens` / `.output_tokens` | histogram, `{token}` | invoked provider call |
|
|
112
|
+
| `gen_ai.client.inference.usage.input_tokens` / `.output_tokens` | counter, `{token}` | invoked provider call |
|
|
113
|
+
| `gen_ai.execute_tool.duration` | histogram, `s` | tool body |
|
|
114
|
+
| `gen_ai.invoke_agent.duration` | histogram, `s` | prompt |
|
|
115
|
+
| `gen_ai.invoke_agent.inference_calls` / `.tool_calls` | histogram, call counts | prompt |
|
|
116
|
+
|
|
117
|
+
Cache hits and refused calls are not inferences and produce nothing, and neither do permission
|
|
118
|
+
decisions. The usage counters carry `gen_ai.token.modality` `unknown`, and a call whose usage was not
|
|
119
|
+
fully received records `0` tokens, as the conventions specify. Samples that need every provider (or tool) child are left out when the
|
|
120
|
+
batch omitted any, so a bounded batch never yields a total that looks complete. `gen_ai.request.model`
|
|
121
|
+
and `gen_ai.tool.name` appear only when label-safe, and `gen_ai.provider.name` only for providers whose
|
|
122
|
+
GenAI name is certain. As on the provider span, the inference samples also keep the label-safe
|
|
123
|
+
`agent.provider.id` (the conventions do not define it, so a provider outside the known map can still be
|
|
124
|
+
told apart) and, in `gen_ai/dup` only, `agent.model.id`. The bucket constants (`GEN_AI_DURATION_BUCKETS_S`
|
|
125
|
+
and the others) are exported. The Robota CLI exports these metrics in a scope whose schema URL is
|
|
126
|
+
`GEN_AI_SCHEMA_URL`, as it does for spans.
|
|
127
|
+
|
|
128
|
+
## Documents
|
|
129
|
+
|
|
130
|
+
- [docs/README.md](./docs/README.md)
|
|
131
|
+
- [docs/SPEC.md](./docs/SPEC.md) — package contract and design decisions.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@opentelemetry/api"),t=require("@robota-sdk/agent-core");const n=e=>typeof e==`object`&&e&&!Array.isArray(e)?e:void 0,r=e=>typeof e==`number`&&Number.isSafeInteger(e)&&e>=0;function i(e){let t=n(e);if(!(!t||!r(t.durationMs)||!r(t.samples)||!r(t.invalid)||t.samples===0&&t.durationMs!==0))return{durationMs:t.durationMs,samples:t.samples,invalid:t.invalid}}function a(e){let t=n(e);if(!t)return;let a=i(t.provider),o=i(t.tool),s=i(t.queue),c=n(t.queue),l=s&&c&&r(c.admissionStarted)&&r(c.notDispatched)&&c.admissionStarted+c.notDispatched===s.samples?{...s,admissionStarted:c.admissionStarted,notDispatched:c.notDispatched}:void 0;if(!(!a&&!o&&!l))return{...a?{provider:a}:{},...o?{tool:o}:{},...l?{queue:l}:{}}}function o(e){let t=a(e),n={};for(let e of[`provider`,`tool`,`queue`]){let r=t?.[e];r&&(n[`agent.timing.${e}.duration_ms`]=r.durationMs,n[`agent.timing.${e}.samples`]=r.samples,n[`agent.timing.${e}.invalid`]=r.invalid)}return t?.queue&&(n[`agent.timing.queue.admission_started`]=t.queue.admissionStarted,n[`agent.timing.queue.not_dispatched`]=t.queue.notDispatched),n}const s=`https://opentelemetry.io/schemas/gen-ai-dev/1.42.0-dev`,c=/^[A-Za-z0-9_-]{1,128}$/u,l=/^[A-Za-z0-9._:/-]{1,128}$/u,u={anthropic:`anthropic`,openai:`openai`,deepseek:`deepseek`,gemini:`gcp.gen_ai`,google:`gcp.gen_ai`},d=e=>typeof e==`string`&&c.test(e)?e:void 0,f=e=>typeof e==`string`&&l.test(e)?e:void 0,p=e=>e!==void 0&&Object.hasOwn(u,e)?u[e]:void 0,m=e=>e===`failure`?`error`:`unset`,h=(e,t)=>t?`${e} ${t}`:e;function g(e,t,n){return n!==`agent`&&t===`failure`?{...e,"error.type":`_OTHER`}:e}function _(e){return e.kind!==`permission`}function v(e,t){let n={"agent.turn.id":e.turnId,"agent.outcome":e.root.outcome,"agent.omitted.provider_count":e.omittedChildren.provider,"agent.omitted.tool_count":e.omittedChildren.tool,"agent.omitted.permission_count":e.omittedChildren.permission,...o(e.timingTotals)},r={"agent.session.id":e.sessionId},i={"gen_ai.operation.name":`invoke_agent`,...t.agentName?{"gen_ai.agent.name":t.agentName}:{},"gen_ai.conversation.id":e.sessionId};return t.mode===`agent`?{...r,...n}:t.mode===`gen_ai`?{...i,...n}:{...i,...r,...n}}function y(e,t){let n=e.usageProvenance===`complete`,r=d(e.providerRequestId),i=t!==`gen_ai`,a={"agent.outcome":e.outcome,"agent.provider.round":e.round,...e.disposition?{"agent.provider.disposition":e.disposition}:{},...e.providerId?{"agent.provider.id":e.providerId}:{},...i&&e.modelId?{"agent.model.id":e.modelId}:{},...e.usageProvenance?{"agent.usage.provenance":e.usageProvenance}:{},...n?{...i?{"agent.usage.input_tokens":e.promptTokens??0,"agent.usage.output_tokens":e.completionTokens??0}:{},"agent.usage.total_tokens":e.totalTokens??0}:{},...r?{"agent.provider.request_id":r}:{}},o=p(e.providerId),s={"gen_ai.operation.name":`chat`,...o?{"gen_ai.provider.name":o}:{},...e.modelId?{"gen_ai.request.model":e.modelId}:{},...n?{"gen_ai.usage.input_tokens":e.promptTokens??0,"gen_ai.usage.output_tokens":e.completionTokens??0}:{}};return t===`agent`?a:{...s,...a}}function b(e,t){let n=d(e.toolCallId),r=f(e.toolName),i={"agent.outcome":e.outcome},a=n?{"agent.tool.call_id":n}:{},o={"gen_ai.operation.name":`execute_tool`,...r?{"gen_ai.tool.name":r}:{},...n?{"gen_ai.tool.call.id":n}:{}};return t===`agent`?{...i,...a}:t===`gen_ai`?{...o,...i}:{...o,...i,...a}}function x(e,t,n,r){let{trace:i}=e,a={traceId:t,spanId:i.spanId,parentSpanId:n,startedAt:i.startedAt,endedAt:i.endedAt,status:m(i.outcome)};return e.kind===`provider`?{...a,name:r===`agent`?`agent.provider_call`:h(`chat`,e.trace.modelId),kind:r===`agent`?`internal`:`client`,attributes:g(y(e.trace,r),i.outcome,r)}:{...a,name:r===`agent`?`agent.tool_body`:h(`execute_tool`,f(e.trace.toolName)),kind:`internal`,attributes:g(b(e.trace,r),i.outcome,r)}}function S(e,t){let{mode:n}=t,r={name:n===`agent`?`agent.prompt_execution`:h(`invoke_agent`,t.agentName),kind:`internal`,traceId:e.root.traceId,spanId:e.root.spanId,startedAt:e.root.startedAt,endedAt:e.root.endedAt,status:m(e.root.outcome),attributes:g(v(e,t),e.root.outcome,n)},i=e.children.filter(_).map(e=>x(e,r.traceId,r.spanId,n));return{...n===`agent`?{}:{schemaUrl:s},root:r,children:i}}const C=[.01,.02,.04,.08,.16,.32,.64,1.28,2.56,5.12,10.24,20.48,40.96,81.92],w=[.1,.2,.4,.8,1.6,3.2,6.4,12.8,25.6,51.2,102.4,204.8,409.6],T=[1,4,16,64,256,1024,4096,16384,65536,262144,1048576,4194304,16777216,67108864],E=[1,2,4,8,16,32,64,128],D={"error.type":`_OTHER`},O=(e,t)=>{let n=(Date.parse(t)-Date.parse(e))/1e3;return Number.isFinite(n)&&n>=0?n:void 0},k=e=>typeof e==`number`&&Number.isSafeInteger(e)&&e>=0?e:void 0;function A(e,t){let n=p(e.providerId),r=f(e.providerId),i=f(e.modelId),a={"gen_ai.operation.name":`chat`,...n?{"gen_ai.provider.name":n}:{},...i?{"gen_ai.request.model":i}:{},...r?{"agent.provider.id":r}:{},...t===`gen_ai/dup`&&i?{"agent.model.id":i}:{}},o=e.outcome===`failure`?{...a,...D}:a,s=k(e.promptTokens),c=k(e.completionTokens),l=e.usageProvenance===`complete`&&s!==void 0&&c!==void 0,u=l?s:0,d=l?c:0,m={...a,"gen_ai.token.modality":`unknown`},h=O(e.startedAt,e.endedAt),g=(e,t)=>({name:e,instrument:`histogram`,unit:`{token}`,value:t,attributes:o,boundaries:T});return[...h===void 0?[]:[{name:`gen_ai.client.inference.duration`,instrument:`histogram`,unit:`s`,value:h,attributes:o,boundaries:C}],{name:`gen_ai.client.inference.usage.input_tokens`,instrument:`counter`,unit:`{token}`,value:u,attributes:m},{name:`gen_ai.client.inference.usage.output_tokens`,instrument:`counter`,unit:`{token}`,value:d,attributes:m},g(`gen_ai.client.inference.operation.input_tokens`,u),g(`gen_ai.client.inference.operation.output_tokens`,d)]}function j(e,t){let n=t.agentName?{"gen_ai.agent.name":t.agentName}:{},r=[],i=e.omittedChildren.provider===0,a=e.omittedChildren.tool===0,o=0,s=0;for(let n of e.children)n.kind===`provider`&&n.trace.disposition===`invoked`&&(o+=1,i&&r.push(...A(n.trace,t.mode)));for(let t of e.children){if(t.kind!==`tool`)continue;s+=1;let e=a?O(t.trace.startedAt,t.trace.endedAt):void 0;if(e===void 0)continue;let i=f(t.trace.toolName);r.push({name:`gen_ai.execute_tool.duration`,instrument:`histogram`,unit:`s`,value:e,boundaries:C,attributes:{...i?{"gen_ai.tool.name":i}:{},...n,...t.trace.outcome===`failure`?D:{}}})}let c=O(e.root.startedAt,e.root.endedAt);return c!==void 0&&r.push({name:`gen_ai.invoke_agent.duration`,instrument:`histogram`,unit:`s`,value:c,boundaries:w,attributes:{...n,...e.root.outcome===`failure`?D:{}}}),i&&r.push({name:`gen_ai.invoke_agent.inference_calls`,instrument:`histogram`,unit:`{inference_call}`,value:o,attributes:n,boundaries:E}),a&&r.push({name:`gen_ai.invoke_agent.tool_calls`,instrument:`histogram`,unit:`{tool_call}`,value:s,attributes:n,boundaries:E}),r}const M=/^[A-Za-z0-9_-]{1,128}$/u,N=/^[A-Za-z0-9._:/-]{1,128}$/u,P=/^(?!0{16}$)[0-9a-f]{16}$/u,F=new Set([`success`,`failure`,`interrupted`]),I=new Set([`invoked`,`cache-hit`,`preflight-refused`]),L=new Set([`complete`,`partial`,`absent`]),R=(e,t)=>typeof e==`string`&&t.test(e)?e:void 0,z=e=>typeof e==`number`&&Number.isSafeInteger(e)&&e>=0,B=e=>typeof e==`string`&&!Number.isNaN(Date.parse(e));function V(e,n){let r=R(e.callId,M),i=r===void 0?void 0:(0,t.spanIdFromMintedId)(r);if(i===void 0||!P.test(i)||!B(e.startedAt)||!B(e.endedAt)||!F.has(e.outcome)||!z(e.round)||e.round<1)return;let a=I.has(e.disposition)?e.disposition:void 0,o=L.has(e.usageProvenance)?e.usageProvenance:void 0,s=o===`complete`&&z(e.promptTokens)&&z(e.completionTokens)&&z(e.totalTokens),c=R(e.providerId,N),l=R(e.modelId,N),u=a===`invoked`?R(e.providerRequestId,M):void 0;return{kind:`provider`,trace:{callId:r,traceId:n.traceId,parentSpanId:n.spanId,spanId:i,startedAt:e.startedAt,endedAt:e.endedAt,outcome:e.outcome,round:e.round,...a?{disposition:a}:{},...c?{providerId:c}:{},...l?{modelId:l}:{},...o?{usageProvenance:s||o!==`complete`?o:`partial`}:{},...s?{promptTokens:e.promptTokens,completionTokens:e.completionTokens,totalTokens:e.totalTokens}:{},...u?{providerRequestId:u}:{}}}}function H(t,n,r,i){let a=e.trace.setSpanContext(e.ROOT_CONTEXT,r),o=()=>t.startSpan(i.name,{kind:i.kind===`client`?e.SpanKind.CLIENT:e.SpanKind.INTERNAL,startTime:new Date(i.startedAt),attributes:i.attributes},a),s=n?n.withSpanId(i.spanId,o):o();i.status===`error`&&s.setStatus({code:e.SpanStatusCode.ERROR}),s.end(new Date(i.endedAt))}function U(e){let t=e?.ownerPath??[];for(let e=t.length-1;e>=0;e--)if(t[e]?.type===`execution`)return t[e]?.id}function W(e){try{e()}catch{}}var G=class extends t.AbstractPlugin{onBody;name=`OtelToolBodyPlugin`;version=`1.0.0`;pending=new WeakMap;category=t.PluginCategory.MONITORING;priority=t.PluginPriority.LOW;constructor(e){super(),this.onBody=e}async beforeToolCall(e,n,r){W(()=>{let e=R(r?.toolBodyId,M),i=e===void 0?void 0:(0,t.spanIdFromMintedId)(e);i===void 0||!P.test(i)||this.pending.set(n,{startedAtMs:Date.now(),spanId:i,executionId:U(r),toolCallId:R(r?.executionId,M),signal:r?.signal})})}async afterToolCall(e,t,n){W(()=>{let r=this.pending.get(t);r&&(this.pending.delete(t),this.onBody(r,e,r.signal?.aborted?`interrupted`:n.success?`success`:`failure`))})}};function K(n){let{tracer:r,mode:i,idGenerator:a}=n,o=new Map;return{plugin:new G((e,t,n)=>{let s=e.executionId===void 0?void 0:o.get(e.executionId);if(!s)return;let c=new Date(e.startedAtMs).toISOString(),l={kind:`tool`,trace:{traceId:s.parent.traceId,parentSpanId:s.parent.spanId,spanId:e.spanId,startedAt:c,endedAt:new Date(Math.max(Date.now(),e.startedAtMs)).toISOString(),outcome:n,...e.toolCallId?{toolCallId:e.toolCallId}:{},...R(t,N)?{toolName:t}:{}}};H(r,a,s.parent,x(l,s.parent.traceId,s.parent.spanId,i))}),startRun(n,s={}){if(!(0,e.isSpanContextValid)(n)||(n.traceFlags&e.TraceFlags.SAMPLED)===0)return{onExecutionEvent:()=>void 0,end:()=>void 0};let c={parent:n,executionIds:new Set};return{traceContext:{traceId:n.traceId,parentSpanId:n.spanId,allowedOrigins:[...s.allowedOrigins??[]],...s.subprocessClasses?{subprocessClasses:[...s.subprocessClasses]}:{},...s.onPropagationUnavailable?{onPropagationUnavailable:s.onPropagationUnavailable}:{}},onExecutionEvent(e,s){W(()=>{let l=s.executionId;if(typeof l==`string`&&!c.executionIds.has(l)&&(c.executionIds.add(l),o.set(l,c)),e!==t.PROVIDER_CALL_EVENTS.COMPLETED)return;let u=V(s,n);u&&H(r,a,n,x(u,n.traceId,n.spanId,i))})},end(){for(let e of c.executionIds)o.get(e)===c&&o.delete(e);c.executionIds.clear()}}}}}function q(e){let t=new Uint8Array(e);do globalThis.crypto.getRandomValues(t);while(t.every(e=>e===0));return Array.from(t,e=>e.toString(16).padStart(2,`0`)).join(``)}function J(){let e;return{generateTraceId:()=>q(16),generateSpanId(){let t=e;return e=void 0,t??q(8)},withSpanId(t,n){e=t;try{return n()}finally{e=void 0}}}}exports.GEN_AI_AGENT_DURATION_BUCKETS_S=w,exports.GEN_AI_CALL_COUNT_BUCKETS=E,exports.GEN_AI_DURATION_BUCKETS_S=C,exports.GEN_AI_SCHEMA_URL=s,exports.GEN_AI_TOKEN_BUCKETS=T,exports.createOtelTracing=K,exports.createStructuralIdGenerator=J,exports.projectLivePromptMetricSamples=j,exports.projectLivePromptTrace=S;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { ILivePromptTraceBatch } from "@robota-sdk/agent-interface-analytics";
|
|
2
|
+
import { AbstractPlugin, IRunTraceContext, TExecutionEventCallback, TSubprocessTraceClass } from "@robota-sdk/agent-core";
|
|
3
|
+
import { SpanContext, Tracer } from "@opentelemetry/api";
|
|
4
|
+
//#region src/projection.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Which names a projected span carries. `agent` keeps Robota's own `agent.*` names; `gen_ai` uses the
|
|
7
|
+
* OpenTelemetry GenAI semantic conventions; `gen_ai/dup` uses GenAI span names and emits both attribute
|
|
8
|
+
* sets, so a backend can move from one set of names to the other without a gap.
|
|
9
|
+
*/
|
|
10
|
+
type TOtelProjectionMode = 'agent' | 'gen_ai/dup' | 'gen_ai';
|
|
11
|
+
interface IOtelProjectionOptions {
|
|
12
|
+
readonly mode: TOtelProjectionMode;
|
|
13
|
+
/** Host-chosen agent name for the root `invoke_agent` span; GenAI modes only. */
|
|
14
|
+
readonly agentName?: string;
|
|
15
|
+
}
|
|
16
|
+
type TOtelSpanKind = 'internal' | 'client';
|
|
17
|
+
type TOtelAttributes = Readonly<Record<string, string | number>>;
|
|
18
|
+
/** One span to record, independent of any OpenTelemetry SDK. */
|
|
19
|
+
interface IOtelSpanProjection {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly kind: TOtelSpanKind;
|
|
22
|
+
readonly traceId: string;
|
|
23
|
+
readonly spanId: string;
|
|
24
|
+
/** Absent on the root; every child's parent is the root. */
|
|
25
|
+
readonly parentSpanId?: string;
|
|
26
|
+
readonly startedAt: string;
|
|
27
|
+
readonly endedAt: string;
|
|
28
|
+
/** `error` for a failed outcome; an interrupted one is not an error. */
|
|
29
|
+
readonly status: 'unset' | 'error';
|
|
30
|
+
readonly attributes: TOtelAttributes;
|
|
31
|
+
}
|
|
32
|
+
interface IOtelTraceProjection {
|
|
33
|
+
/** The schema the attribute names follow; absent in `agent` mode, whose names are Robota's own. */
|
|
34
|
+
readonly schemaUrl?: string;
|
|
35
|
+
readonly root: IOtelSpanProjection;
|
|
36
|
+
readonly children: readonly IOtelSpanProjection[];
|
|
37
|
+
}
|
|
38
|
+
/** The GenAI semantic-convention version the `gen_ai` names follow. */
|
|
39
|
+
declare const GEN_AI_SCHEMA_URL = "https://opentelemetry.io/schemas/gen-ai-dev/1.42.0-dev";
|
|
40
|
+
/**
|
|
41
|
+
* Map one live prompt trace batch onto the spans an OpenTelemetry exporter records: the prompt root,
|
|
42
|
+
* then one child per provider call and tool body, in batch order. Permission decisions get no span.
|
|
43
|
+
* The result carries only what the batch carries, so it stays content-free.
|
|
44
|
+
*/
|
|
45
|
+
declare function projectLivePromptTrace(batch: ILivePromptTraceBatch, options: IOtelProjectionOptions): IOtelTraceProjection;
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/metrics.d.ts
|
|
48
|
+
/**
|
|
49
|
+
* Bucket boundaries the GenAI conventions recommend, as pinned at semantic-conventions-genai
|
|
50
|
+
* f3c6d0c9bb66ebd474668a3a0be25c61226f02c2. A histogram needs one more count than it has boundaries,
|
|
51
|
+
* for values above the last one.
|
|
52
|
+
*/
|
|
53
|
+
declare const GEN_AI_DURATION_BUCKETS_S: readonly number[];
|
|
54
|
+
declare const GEN_AI_AGENT_DURATION_BUCKETS_S: readonly number[];
|
|
55
|
+
declare const GEN_AI_TOKEN_BUCKETS: readonly number[];
|
|
56
|
+
declare const GEN_AI_CALL_COUNT_BUCKETS: readonly number[];
|
|
57
|
+
/** One measurement to record, independent of any OpenTelemetry SDK: a counter increment or a histogram value. */
|
|
58
|
+
interface IOtelMetricSample {
|
|
59
|
+
readonly name: string;
|
|
60
|
+
readonly instrument: 'counter' | 'histogram';
|
|
61
|
+
readonly unit: string;
|
|
62
|
+
readonly value: number;
|
|
63
|
+
readonly attributes: TOtelAttributes;
|
|
64
|
+
/** Explicit bucket upper bounds of a histogram; absent for a counter. */
|
|
65
|
+
readonly boundaries?: readonly number[];
|
|
66
|
+
}
|
|
67
|
+
interface IOtelMetricSampleOptions {
|
|
68
|
+
/** The GenAI attribute set in use, as for spans: `gen_ai/dup` also keeps `agent.model.id`, `gen_ai` does not. */
|
|
69
|
+
readonly mode: Exclude<TOtelProjectionMode, 'agent'>;
|
|
70
|
+
/** Host-chosen agent name carried on the agent, tool and root metrics. */
|
|
71
|
+
readonly agentName?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Map one live prompt trace batch onto the GenAI metrics a host records: one inference measurement set
|
|
75
|
+
* per invoked provider call, one duration per tool body, and the prompt's agent-level duration and call
|
|
76
|
+
* counts. Provider-derived samples are left out when the batch omitted provider children, and
|
|
77
|
+
* tool-derived ones when it omitted tool children, because a bounded child list cannot prove a complete
|
|
78
|
+
* total. Cache hits and refused calls were not inferences, and permission decisions have no duration, so
|
|
79
|
+
* they produce nothing. Like the span projection, it is pure and content-free.
|
|
80
|
+
*/
|
|
81
|
+
declare function projectLivePromptMetricSamples(batch: ILivePromptTraceBatch, options: IOtelMetricSampleOptions): IOtelMetricSample[];
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/span-ids.d.ts
|
|
84
|
+
/**
|
|
85
|
+
* The OpenTelemetry SDK `IdGenerator` shape, declared structurally so this package does not depend
|
|
86
|
+
* on an SDK version.
|
|
87
|
+
*/
|
|
88
|
+
interface IOtelIdGenerator {
|
|
89
|
+
generateTraceId(): string;
|
|
90
|
+
generateSpanId(): string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* An `IdGenerator` for a host's `TracerProvider` that can hand the next span a chosen ID. Robota
|
|
94
|
+
* derives each provider call's and tool body's span ID before the operation runs and sends it in
|
|
95
|
+
* `traceparent`; recording the span under that same ID is what lets a recipient's spans join the
|
|
96
|
+
* trace. Every other span gets a random ID.
|
|
97
|
+
*/
|
|
98
|
+
interface IStructuralIdGenerator extends IOtelIdGenerator {
|
|
99
|
+
/** Run `start` with `spanId` as the ID of the one span it starts. */
|
|
100
|
+
withSpanId<T>(spanId: string, start: () => T): T;
|
|
101
|
+
}
|
|
102
|
+
declare function createStructuralIdGenerator(): IStructuralIdGenerator;
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/run-tracing.d.ts
|
|
105
|
+
interface IOtelTracingOptions {
|
|
106
|
+
/** The host's tracer; spans are recorded on it and never exported by this package. */
|
|
107
|
+
readonly tracer: Tracer;
|
|
108
|
+
readonly mode: TOtelProjectionMode;
|
|
109
|
+
/**
|
|
110
|
+
* The generator the host gave its `TracerProvider`. With it, each span carries the ID Robota sent
|
|
111
|
+
* in `traceparent`; without it, spans get the provider's own IDs and only the trace and parent match.
|
|
112
|
+
*/
|
|
113
|
+
readonly idGenerator?: IStructuralIdGenerator;
|
|
114
|
+
}
|
|
115
|
+
interface IOtelRunPropagation {
|
|
116
|
+
/** Exact origins that may receive `traceparent` from provider calls and tool bodies. */
|
|
117
|
+
readonly allowedOrigins?: readonly string[];
|
|
118
|
+
readonly subprocessClasses?: readonly TSubprocessTraceClass[];
|
|
119
|
+
readonly onPropagationUnavailable?: (providerId: string) => void;
|
|
120
|
+
}
|
|
121
|
+
/** What one `run()` takes, plus `end()` once it has returned. */
|
|
122
|
+
interface IOtelRunTracing {
|
|
123
|
+
/** Absent when the parent span is invalid or not sampled: the run then traces and propagates nothing. */
|
|
124
|
+
readonly traceContext?: IRunTraceContext;
|
|
125
|
+
readonly onExecutionEvent: TExecutionEventCallback;
|
|
126
|
+
/** Stop attributing tool bodies to this run. */
|
|
127
|
+
end(): void;
|
|
128
|
+
}
|
|
129
|
+
interface IOtelTracing {
|
|
130
|
+
/** Register once in the agent's `plugins`; it records tool bodies for every traced run. */
|
|
131
|
+
readonly plugin: AbstractPlugin;
|
|
132
|
+
/** Trace one run under `parent`, typically the host's own `invoke_agent` span. */
|
|
133
|
+
startRun(parent: SpanContext, propagation?: IOtelRunPropagation): IOtelRunTracing;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Record a Robota agent's provider calls and tool bodies as OpenTelemetry spans on the host's tracer,
|
|
137
|
+
* named by `mode` exactly as {@link projectLivePromptTrace} names them.
|
|
138
|
+
*/
|
|
139
|
+
declare function createOtelTracing(options: IOtelTracingOptions): IOtelTracing;
|
|
140
|
+
//#endregion
|
|
141
|
+
export { GEN_AI_AGENT_DURATION_BUCKETS_S, GEN_AI_CALL_COUNT_BUCKETS, GEN_AI_DURATION_BUCKETS_S, GEN_AI_SCHEMA_URL, GEN_AI_TOKEN_BUCKETS, type IOtelIdGenerator, type IOtelMetricSample, type IOtelMetricSampleOptions, type IOtelProjectionOptions, type IOtelRunPropagation, type IOtelRunTracing, type IOtelSpanProjection, type IOtelTraceProjection, type IOtelTracing, type IOtelTracingOptions, type IStructuralIdGenerator, type TOtelAttributes, type TOtelProjectionMode, type TOtelSpanKind, createOtelTracing, createStructuralIdGenerator, projectLivePromptMetricSamples, projectLivePromptTrace };
|
|
142
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/projection.ts","../../src/metrics.ts","../../src/span-ids.ts","../../src/run-tracing.ts"],"mappings":";;;;;;;;;KAaY;UAEK;WACN,MAAM;;WAEN;;KAGC;KACA,kBAAkB,SAAS;;UAGtB;WACN;WACA,MAAM;WACN;WACA;;WAEA;WACA;WACA;;WAEA;WACA,YAAY;;UAGN;;WAEN;WACA,MAAM;WACN,mBAAmB;;;cAIjB;;;;;;iBAsJG,uBACd,OAAO,uBAAuB,SAAS,yBACtC;;;;;;;;cC7LU;cAEA;cAEA;cAEA;;UAGI;WACN;WACA;WACA;WACA;WACA,YAAY;;WAEZ;;UAGM;;WAEN,MAAM,QAAQ;;WAEd;;;;;;;;;;iBA4DK,+BACd,OAAO,uBAAuB,SAAS,2BACtC;;;;;;;UC3Fc;EACf;EACA;;;;;;;;UASe,+BAA+B;;EAE9C,WAAW,GAAG,gBAAgB,aAAa,IAAI;;iBAUjC,+BAA+B;;;UCH9B;;WAEN,QAAQ;WACR,MAAM;;;;;WAKN,cAAc;;UAGR;;WAEN;WACA,6BAA6B;WAC7B,4BAA4B;;;UAItB;;WAEN,eAAe;WACf,kBAAkB;;EAE3B;;UAGe;;WAEN,QAAQ;;EAEjB,SAAS,QAAQ,aAAa,cAAc,sBAAsB;;;;;;iBA+IpD,kBAAkB,SAAS,sBAAsB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { SpanContext, Tracer } from "@opentelemetry/api";
|
|
2
|
+
import { AbstractPlugin, IRunTraceContext, TExecutionEventCallback, TSubprocessTraceClass } from "@robota-sdk/agent-core";
|
|
3
|
+
import { ILivePromptTraceBatch } from "@robota-sdk/agent-interface-analytics";
|
|
4
|
+
//#region src/projection.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Which names a projected span carries. `agent` keeps Robota's own `agent.*` names; `gen_ai` uses the
|
|
7
|
+
* OpenTelemetry GenAI semantic conventions; `gen_ai/dup` uses GenAI span names and emits both attribute
|
|
8
|
+
* sets, so a backend can move from one set of names to the other without a gap.
|
|
9
|
+
*/
|
|
10
|
+
type TOtelProjectionMode = 'agent' | 'gen_ai/dup' | 'gen_ai';
|
|
11
|
+
interface IOtelProjectionOptions {
|
|
12
|
+
readonly mode: TOtelProjectionMode;
|
|
13
|
+
/** Host-chosen agent name for the root `invoke_agent` span; GenAI modes only. */
|
|
14
|
+
readonly agentName?: string;
|
|
15
|
+
}
|
|
16
|
+
type TOtelSpanKind = 'internal' | 'client';
|
|
17
|
+
type TOtelAttributes = Readonly<Record<string, string | number>>;
|
|
18
|
+
/** One span to record, independent of any OpenTelemetry SDK. */
|
|
19
|
+
interface IOtelSpanProjection {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly kind: TOtelSpanKind;
|
|
22
|
+
readonly traceId: string;
|
|
23
|
+
readonly spanId: string;
|
|
24
|
+
/** Absent on the root; every child's parent is the root. */
|
|
25
|
+
readonly parentSpanId?: string;
|
|
26
|
+
readonly startedAt: string;
|
|
27
|
+
readonly endedAt: string;
|
|
28
|
+
/** `error` for a failed outcome; an interrupted one is not an error. */
|
|
29
|
+
readonly status: 'unset' | 'error';
|
|
30
|
+
readonly attributes: TOtelAttributes;
|
|
31
|
+
}
|
|
32
|
+
interface IOtelTraceProjection {
|
|
33
|
+
/** The schema the attribute names follow; absent in `agent` mode, whose names are Robota's own. */
|
|
34
|
+
readonly schemaUrl?: string;
|
|
35
|
+
readonly root: IOtelSpanProjection;
|
|
36
|
+
readonly children: readonly IOtelSpanProjection[];
|
|
37
|
+
}
|
|
38
|
+
/** The GenAI semantic-convention version the `gen_ai` names follow. */
|
|
39
|
+
declare const GEN_AI_SCHEMA_URL = "https://opentelemetry.io/schemas/gen-ai-dev/1.42.0-dev";
|
|
40
|
+
/**
|
|
41
|
+
* Map one live prompt trace batch onto the spans an OpenTelemetry exporter records: the prompt root,
|
|
42
|
+
* then one child per provider call and tool body, in batch order. Permission decisions get no span.
|
|
43
|
+
* The result carries only what the batch carries, so it stays content-free.
|
|
44
|
+
*/
|
|
45
|
+
declare function projectLivePromptTrace(batch: ILivePromptTraceBatch, options: IOtelProjectionOptions): IOtelTraceProjection;
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/metrics.d.ts
|
|
48
|
+
/**
|
|
49
|
+
* Bucket boundaries the GenAI conventions recommend, as pinned at semantic-conventions-genai
|
|
50
|
+
* f3c6d0c9bb66ebd474668a3a0be25c61226f02c2. A histogram needs one more count than it has boundaries,
|
|
51
|
+
* for values above the last one.
|
|
52
|
+
*/
|
|
53
|
+
declare const GEN_AI_DURATION_BUCKETS_S: readonly number[];
|
|
54
|
+
declare const GEN_AI_AGENT_DURATION_BUCKETS_S: readonly number[];
|
|
55
|
+
declare const GEN_AI_TOKEN_BUCKETS: readonly number[];
|
|
56
|
+
declare const GEN_AI_CALL_COUNT_BUCKETS: readonly number[];
|
|
57
|
+
/** One measurement to record, independent of any OpenTelemetry SDK: a counter increment or a histogram value. */
|
|
58
|
+
interface IOtelMetricSample {
|
|
59
|
+
readonly name: string;
|
|
60
|
+
readonly instrument: 'counter' | 'histogram';
|
|
61
|
+
readonly unit: string;
|
|
62
|
+
readonly value: number;
|
|
63
|
+
readonly attributes: TOtelAttributes;
|
|
64
|
+
/** Explicit bucket upper bounds of a histogram; absent for a counter. */
|
|
65
|
+
readonly boundaries?: readonly number[];
|
|
66
|
+
}
|
|
67
|
+
interface IOtelMetricSampleOptions {
|
|
68
|
+
/** The GenAI attribute set in use, as for spans: `gen_ai/dup` also keeps `agent.model.id`, `gen_ai` does not. */
|
|
69
|
+
readonly mode: Exclude<TOtelProjectionMode, 'agent'>;
|
|
70
|
+
/** Host-chosen agent name carried on the agent, tool and root metrics. */
|
|
71
|
+
readonly agentName?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Map one live prompt trace batch onto the GenAI metrics a host records: one inference measurement set
|
|
75
|
+
* per invoked provider call, one duration per tool body, and the prompt's agent-level duration and call
|
|
76
|
+
* counts. Provider-derived samples are left out when the batch omitted provider children, and
|
|
77
|
+
* tool-derived ones when it omitted tool children, because a bounded child list cannot prove a complete
|
|
78
|
+
* total. Cache hits and refused calls were not inferences, and permission decisions have no duration, so
|
|
79
|
+
* they produce nothing. Like the span projection, it is pure and content-free.
|
|
80
|
+
*/
|
|
81
|
+
declare function projectLivePromptMetricSamples(batch: ILivePromptTraceBatch, options: IOtelMetricSampleOptions): IOtelMetricSample[];
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/span-ids.d.ts
|
|
84
|
+
/**
|
|
85
|
+
* The OpenTelemetry SDK `IdGenerator` shape, declared structurally so this package does not depend
|
|
86
|
+
* on an SDK version.
|
|
87
|
+
*/
|
|
88
|
+
interface IOtelIdGenerator {
|
|
89
|
+
generateTraceId(): string;
|
|
90
|
+
generateSpanId(): string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* An `IdGenerator` for a host's `TracerProvider` that can hand the next span a chosen ID. Robota
|
|
94
|
+
* derives each provider call's and tool body's span ID before the operation runs and sends it in
|
|
95
|
+
* `traceparent`; recording the span under that same ID is what lets a recipient's spans join the
|
|
96
|
+
* trace. Every other span gets a random ID.
|
|
97
|
+
*/
|
|
98
|
+
interface IStructuralIdGenerator extends IOtelIdGenerator {
|
|
99
|
+
/** Run `start` with `spanId` as the ID of the one span it starts. */
|
|
100
|
+
withSpanId<T>(spanId: string, start: () => T): T;
|
|
101
|
+
}
|
|
102
|
+
declare function createStructuralIdGenerator(): IStructuralIdGenerator;
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/run-tracing.d.ts
|
|
105
|
+
interface IOtelTracingOptions {
|
|
106
|
+
/** The host's tracer; spans are recorded on it and never exported by this package. */
|
|
107
|
+
readonly tracer: Tracer;
|
|
108
|
+
readonly mode: TOtelProjectionMode;
|
|
109
|
+
/**
|
|
110
|
+
* The generator the host gave its `TracerProvider`. With it, each span carries the ID Robota sent
|
|
111
|
+
* in `traceparent`; without it, spans get the provider's own IDs and only the trace and parent match.
|
|
112
|
+
*/
|
|
113
|
+
readonly idGenerator?: IStructuralIdGenerator;
|
|
114
|
+
}
|
|
115
|
+
interface IOtelRunPropagation {
|
|
116
|
+
/** Exact origins that may receive `traceparent` from provider calls and tool bodies. */
|
|
117
|
+
readonly allowedOrigins?: readonly string[];
|
|
118
|
+
readonly subprocessClasses?: readonly TSubprocessTraceClass[];
|
|
119
|
+
readonly onPropagationUnavailable?: (providerId: string) => void;
|
|
120
|
+
}
|
|
121
|
+
/** What one `run()` takes, plus `end()` once it has returned. */
|
|
122
|
+
interface IOtelRunTracing {
|
|
123
|
+
/** Absent when the parent span is invalid or not sampled: the run then traces and propagates nothing. */
|
|
124
|
+
readonly traceContext?: IRunTraceContext;
|
|
125
|
+
readonly onExecutionEvent: TExecutionEventCallback;
|
|
126
|
+
/** Stop attributing tool bodies to this run. */
|
|
127
|
+
end(): void;
|
|
128
|
+
}
|
|
129
|
+
interface IOtelTracing {
|
|
130
|
+
/** Register once in the agent's `plugins`; it records tool bodies for every traced run. */
|
|
131
|
+
readonly plugin: AbstractPlugin;
|
|
132
|
+
/** Trace one run under `parent`, typically the host's own `invoke_agent` span. */
|
|
133
|
+
startRun(parent: SpanContext, propagation?: IOtelRunPropagation): IOtelRunTracing;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Record a Robota agent's provider calls and tool bodies as OpenTelemetry spans on the host's tracer,
|
|
137
|
+
* named by `mode` exactly as {@link projectLivePromptTrace} names them.
|
|
138
|
+
*/
|
|
139
|
+
declare function createOtelTracing(options: IOtelTracingOptions): IOtelTracing;
|
|
140
|
+
//#endregion
|
|
141
|
+
export { GEN_AI_AGENT_DURATION_BUCKETS_S, GEN_AI_CALL_COUNT_BUCKETS, GEN_AI_DURATION_BUCKETS_S, GEN_AI_SCHEMA_URL, GEN_AI_TOKEN_BUCKETS, type IOtelIdGenerator, type IOtelMetricSample, type IOtelMetricSampleOptions, type IOtelProjectionOptions, type IOtelRunPropagation, type IOtelRunTracing, type IOtelSpanProjection, type IOtelTraceProjection, type IOtelTracing, type IOtelTracingOptions, type IStructuralIdGenerator, type TOtelAttributes, type TOtelProjectionMode, type TOtelSpanKind, createOtelTracing, createStructuralIdGenerator, projectLivePromptMetricSamples, projectLivePromptTrace };
|
|
142
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/projection.ts","../../src/metrics.ts","../../src/span-ids.ts","../../src/run-tracing.ts"],"mappings":";;;;;;;;;KAaY;UAEK;WACN,MAAM;;WAEN;;KAGC;KACA,kBAAkB,SAAS;;UAGtB;WACN;WACA,MAAM;WACN;WACA;;WAEA;WACA;WACA;;WAEA;WACA,YAAY;;UAGN;;WAEN;WACA,MAAM;WACN,mBAAmB;;;cAIjB;;;;;;iBAsJG,uBACd,OAAO,uBAAuB,SAAS,yBACtC;;;;;;;;cC7LU;cAEA;cAEA;cAEA;;UAGI;WACN;WACA;WACA;WACA;WACA,YAAY;;WAEZ;;UAGM;;WAEN,MAAM,QAAQ;;WAEd;;;;;;;;;;iBA4DK,+BACd,OAAO,uBAAuB,SAAS,2BACtC;;;;;;;UC3Fc;EACf;EACA;;;;;;;;UASe,+BAA+B;;EAE9C,WAAW,GAAG,gBAAgB,aAAa,IAAI;;iBAUjC,+BAA+B;;;UCH9B;;WAEN,QAAQ;WACR,MAAM;;;;;WAKN,cAAc;;UAGR;;WAEN;WACA,6BAA6B;WAC7B,4BAA4B;;;UAItB;;WAEN,eAAe;WACf,kBAAkB;;EAE3B;;UAGe;;WAEN,QAAQ;;EAEjB,SAAS,QAAQ,aAAa,cAAc,sBAAsB;;;;;;iBA+IpD,kBAAkB,SAAS,sBAAsB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{ROOT_CONTEXT as e,SpanKind as t,SpanStatusCode as n,TraceFlags as r,isSpanContextValid as i,trace as a}from"@opentelemetry/api";import{AbstractPlugin as o,PROVIDER_CALL_EVENTS as s,PluginCategory as c,PluginPriority as l,spanIdFromMintedId as u}from"@robota-sdk/agent-core";const d=e=>typeof e==`object`&&e&&!Array.isArray(e)?e:void 0,f=e=>typeof e==`number`&&Number.isSafeInteger(e)&&e>=0;function p(e){let t=d(e);if(!(!t||!f(t.durationMs)||!f(t.samples)||!f(t.invalid)||t.samples===0&&t.durationMs!==0))return{durationMs:t.durationMs,samples:t.samples,invalid:t.invalid}}function m(e){let t=d(e);if(!t)return;let n=p(t.provider),r=p(t.tool),i=p(t.queue),a=d(t.queue),o=i&&a&&f(a.admissionStarted)&&f(a.notDispatched)&&a.admissionStarted+a.notDispatched===i.samples?{...i,admissionStarted:a.admissionStarted,notDispatched:a.notDispatched}:void 0;if(!(!n&&!r&&!o))return{...n?{provider:n}:{},...r?{tool:r}:{},...o?{queue:o}:{}}}function h(e){let t=m(e),n={};for(let e of[`provider`,`tool`,`queue`]){let r=t?.[e];r&&(n[`agent.timing.${e}.duration_ms`]=r.durationMs,n[`agent.timing.${e}.samples`]=r.samples,n[`agent.timing.${e}.invalid`]=r.invalid)}return t?.queue&&(n[`agent.timing.queue.admission_started`]=t.queue.admissionStarted,n[`agent.timing.queue.not_dispatched`]=t.queue.notDispatched),n}const g=`https://opentelemetry.io/schemas/gen-ai-dev/1.42.0-dev`,_=/^[A-Za-z0-9_-]{1,128}$/u,v=/^[A-Za-z0-9._:/-]{1,128}$/u,y={anthropic:`anthropic`,openai:`openai`,deepseek:`deepseek`,gemini:`gcp.gen_ai`,google:`gcp.gen_ai`},b=e=>typeof e==`string`&&_.test(e)?e:void 0,x=e=>typeof e==`string`&&v.test(e)?e:void 0,S=e=>e!==void 0&&Object.hasOwn(y,e)?y[e]:void 0,C=e=>e===`failure`?`error`:`unset`,w=(e,t)=>t?`${e} ${t}`:e;function T(e,t,n){return n!==`agent`&&t===`failure`?{...e,"error.type":`_OTHER`}:e}function E(e){return e.kind!==`permission`}function D(e,t){let n={"agent.turn.id":e.turnId,"agent.outcome":e.root.outcome,"agent.omitted.provider_count":e.omittedChildren.provider,"agent.omitted.tool_count":e.omittedChildren.tool,"agent.omitted.permission_count":e.omittedChildren.permission,...h(e.timingTotals)},r={"agent.session.id":e.sessionId},i={"gen_ai.operation.name":`invoke_agent`,...t.agentName?{"gen_ai.agent.name":t.agentName}:{},"gen_ai.conversation.id":e.sessionId};return t.mode===`agent`?{...r,...n}:t.mode===`gen_ai`?{...i,...n}:{...i,...r,...n}}function ee(e,t){let n=e.usageProvenance===`complete`,r=b(e.providerRequestId),i=t!==`gen_ai`,a={"agent.outcome":e.outcome,"agent.provider.round":e.round,...e.disposition?{"agent.provider.disposition":e.disposition}:{},...e.providerId?{"agent.provider.id":e.providerId}:{},...i&&e.modelId?{"agent.model.id":e.modelId}:{},...e.usageProvenance?{"agent.usage.provenance":e.usageProvenance}:{},...n?{...i?{"agent.usage.input_tokens":e.promptTokens??0,"agent.usage.output_tokens":e.completionTokens??0}:{},"agent.usage.total_tokens":e.totalTokens??0}:{},...r?{"agent.provider.request_id":r}:{}},o=S(e.providerId),s={"gen_ai.operation.name":`chat`,...o?{"gen_ai.provider.name":o}:{},...e.modelId?{"gen_ai.request.model":e.modelId}:{},...n?{"gen_ai.usage.input_tokens":e.promptTokens??0,"gen_ai.usage.output_tokens":e.completionTokens??0}:{}};return t===`agent`?a:{...s,...a}}function O(e,t){let n=b(e.toolCallId),r=x(e.toolName),i={"agent.outcome":e.outcome},a=n?{"agent.tool.call_id":n}:{},o={"gen_ai.operation.name":`execute_tool`,...r?{"gen_ai.tool.name":r}:{},...n?{"gen_ai.tool.call.id":n}:{}};return t===`agent`?{...i,...a}:t===`gen_ai`?{...o,...i}:{...o,...i,...a}}function k(e,t,n,r){let{trace:i}=e,a={traceId:t,spanId:i.spanId,parentSpanId:n,startedAt:i.startedAt,endedAt:i.endedAt,status:C(i.outcome)};return e.kind===`provider`?{...a,name:r===`agent`?`agent.provider_call`:w(`chat`,e.trace.modelId),kind:r===`agent`?`internal`:`client`,attributes:T(ee(e.trace,r),i.outcome,r)}:{...a,name:r===`agent`?`agent.tool_body`:w(`execute_tool`,x(e.trace.toolName)),kind:`internal`,attributes:T(O(e.trace,r),i.outcome,r)}}function A(e,t){let{mode:n}=t,r={name:n===`agent`?`agent.prompt_execution`:w(`invoke_agent`,t.agentName),kind:`internal`,traceId:e.root.traceId,spanId:e.root.spanId,startedAt:e.root.startedAt,endedAt:e.root.endedAt,status:C(e.root.outcome),attributes:T(D(e,t),e.root.outcome,n)},i=e.children.filter(E).map(e=>k(e,r.traceId,r.spanId,n));return{...n===`agent`?{}:{schemaUrl:g},root:r,children:i}}const j=[.01,.02,.04,.08,.16,.32,.64,1.28,2.56,5.12,10.24,20.48,40.96,81.92],M=[.1,.2,.4,.8,1.6,3.2,6.4,12.8,25.6,51.2,102.4,204.8,409.6],N=[1,4,16,64,256,1024,4096,16384,65536,262144,1048576,4194304,16777216,67108864],P=[1,2,4,8,16,32,64,128],F={"error.type":`_OTHER`},I=(e,t)=>{let n=(Date.parse(t)-Date.parse(e))/1e3;return Number.isFinite(n)&&n>=0?n:void 0},L=e=>typeof e==`number`&&Number.isSafeInteger(e)&&e>=0?e:void 0;function R(e,t){let n=S(e.providerId),r=x(e.providerId),i=x(e.modelId),a={"gen_ai.operation.name":`chat`,...n?{"gen_ai.provider.name":n}:{},...i?{"gen_ai.request.model":i}:{},...r?{"agent.provider.id":r}:{},...t===`gen_ai/dup`&&i?{"agent.model.id":i}:{}},o=e.outcome===`failure`?{...a,...F}:a,s=L(e.promptTokens),c=L(e.completionTokens),l=e.usageProvenance===`complete`&&s!==void 0&&c!==void 0,u=l?s:0,d=l?c:0,f={...a,"gen_ai.token.modality":`unknown`},p=I(e.startedAt,e.endedAt),m=(e,t)=>({name:e,instrument:`histogram`,unit:`{token}`,value:t,attributes:o,boundaries:N});return[...p===void 0?[]:[{name:`gen_ai.client.inference.duration`,instrument:`histogram`,unit:`s`,value:p,attributes:o,boundaries:j}],{name:`gen_ai.client.inference.usage.input_tokens`,instrument:`counter`,unit:`{token}`,value:u,attributes:f},{name:`gen_ai.client.inference.usage.output_tokens`,instrument:`counter`,unit:`{token}`,value:d,attributes:f},m(`gen_ai.client.inference.operation.input_tokens`,u),m(`gen_ai.client.inference.operation.output_tokens`,d)]}function z(e,t){let n=t.agentName?{"gen_ai.agent.name":t.agentName}:{},r=[],i=e.omittedChildren.provider===0,a=e.omittedChildren.tool===0,o=0,s=0;for(let n of e.children)n.kind===`provider`&&n.trace.disposition===`invoked`&&(o+=1,i&&r.push(...R(n.trace,t.mode)));for(let t of e.children){if(t.kind!==`tool`)continue;s+=1;let e=a?I(t.trace.startedAt,t.trace.endedAt):void 0;if(e===void 0)continue;let i=x(t.trace.toolName);r.push({name:`gen_ai.execute_tool.duration`,instrument:`histogram`,unit:`s`,value:e,boundaries:j,attributes:{...i?{"gen_ai.tool.name":i}:{},...n,...t.trace.outcome===`failure`?F:{}}})}let c=I(e.root.startedAt,e.root.endedAt);return c!==void 0&&r.push({name:`gen_ai.invoke_agent.duration`,instrument:`histogram`,unit:`s`,value:c,boundaries:M,attributes:{...n,...e.root.outcome===`failure`?F:{}}}),i&&r.push({name:`gen_ai.invoke_agent.inference_calls`,instrument:`histogram`,unit:`{inference_call}`,value:o,attributes:n,boundaries:P}),a&&r.push({name:`gen_ai.invoke_agent.tool_calls`,instrument:`histogram`,unit:`{tool_call}`,value:s,attributes:n,boundaries:P}),r}const B=/^[A-Za-z0-9_-]{1,128}$/u,V=/^[A-Za-z0-9._:/-]{1,128}$/u,H=/^(?!0{16}$)[0-9a-f]{16}$/u,U=new Set([`success`,`failure`,`interrupted`]),W=new Set([`invoked`,`cache-hit`,`preflight-refused`]),G=new Set([`complete`,`partial`,`absent`]),K=(e,t)=>typeof e==`string`&&t.test(e)?e:void 0,q=e=>typeof e==`number`&&Number.isSafeInteger(e)&&e>=0,J=e=>typeof e==`string`&&!Number.isNaN(Date.parse(e));function Y(e,t){let n=K(e.callId,B),r=n===void 0?void 0:u(n);if(r===void 0||!H.test(r)||!J(e.startedAt)||!J(e.endedAt)||!U.has(e.outcome)||!q(e.round)||e.round<1)return;let i=W.has(e.disposition)?e.disposition:void 0,a=G.has(e.usageProvenance)?e.usageProvenance:void 0,o=a===`complete`&&q(e.promptTokens)&&q(e.completionTokens)&&q(e.totalTokens),s=K(e.providerId,V),c=K(e.modelId,V),l=i===`invoked`?K(e.providerRequestId,B):void 0;return{kind:`provider`,trace:{callId:n,traceId:t.traceId,parentSpanId:t.spanId,spanId:r,startedAt:e.startedAt,endedAt:e.endedAt,outcome:e.outcome,round:e.round,...i?{disposition:i}:{},...s?{providerId:s}:{},...c?{modelId:c}:{},...a?{usageProvenance:o||a!==`complete`?a:`partial`}:{},...o?{promptTokens:e.promptTokens,completionTokens:e.completionTokens,totalTokens:e.totalTokens}:{},...l?{providerRequestId:l}:{}}}}function X(r,i,o,s){let c=a.setSpanContext(e,o),l=()=>r.startSpan(s.name,{kind:s.kind===`client`?t.CLIENT:t.INTERNAL,startTime:new Date(s.startedAt),attributes:s.attributes},c),u=i?i.withSpanId(s.spanId,l):l();s.status===`error`&&u.setStatus({code:n.ERROR}),u.end(new Date(s.endedAt))}function Z(e){let t=e?.ownerPath??[];for(let e=t.length-1;e>=0;e--)if(t[e]?.type===`execution`)return t[e]?.id}function Q(e){try{e()}catch{}}var te=class extends o{onBody;name=`OtelToolBodyPlugin`;version=`1.0.0`;pending=new WeakMap;category=c.MONITORING;priority=l.LOW;constructor(e){super(),this.onBody=e}async beforeToolCall(e,t,n){Q(()=>{let e=K(n?.toolBodyId,B),r=e===void 0?void 0:u(e);r===void 0||!H.test(r)||this.pending.set(t,{startedAtMs:Date.now(),spanId:r,executionId:Z(n),toolCallId:K(n?.executionId,B),signal:n?.signal})})}async afterToolCall(e,t,n){Q(()=>{let r=this.pending.get(t);r&&(this.pending.delete(t),this.onBody(r,e,r.signal?.aborted?`interrupted`:n.success?`success`:`failure`))})}};function ne(e){let{tracer:t,mode:n,idGenerator:a}=e,o=new Map;return{plugin:new te((e,r,i)=>{let s=e.executionId===void 0?void 0:o.get(e.executionId);if(!s)return;let c=new Date(e.startedAtMs).toISOString(),l={kind:`tool`,trace:{traceId:s.parent.traceId,parentSpanId:s.parent.spanId,spanId:e.spanId,startedAt:c,endedAt:new Date(Math.max(Date.now(),e.startedAtMs)).toISOString(),outcome:i,...e.toolCallId?{toolCallId:e.toolCallId}:{},...K(r,V)?{toolName:r}:{}}};X(t,a,s.parent,k(l,s.parent.traceId,s.parent.spanId,n))}),startRun(e,c={}){if(!i(e)||(e.traceFlags&r.SAMPLED)===0)return{onExecutionEvent:()=>void 0,end:()=>void 0};let l={parent:e,executionIds:new Set};return{traceContext:{traceId:e.traceId,parentSpanId:e.spanId,allowedOrigins:[...c.allowedOrigins??[]],...c.subprocessClasses?{subprocessClasses:[...c.subprocessClasses]}:{},...c.onPropagationUnavailable?{onPropagationUnavailable:c.onPropagationUnavailable}:{}},onExecutionEvent(r,i){Q(()=>{let c=i.executionId;if(typeof c==`string`&&!l.executionIds.has(c)&&(l.executionIds.add(c),o.set(c,l)),r!==s.COMPLETED)return;let u=Y(i,e);u&&X(t,a,e,k(u,e.traceId,e.spanId,n))})},end(){for(let e of l.executionIds)o.get(e)===l&&o.delete(e);l.executionIds.clear()}}}}}function $(e){let t=new Uint8Array(e);do globalThis.crypto.getRandomValues(t);while(t.every(e=>e===0));return Array.from(t,e=>e.toString(16).padStart(2,`0`)).join(``)}function re(){let e;return{generateTraceId:()=>$(16),generateSpanId(){let t=e;return e=void 0,t??$(8)},withSpanId(t,n){e=t;try{return n()}finally{e=void 0}}}}export{M as GEN_AI_AGENT_DURATION_BUCKETS_S,P as GEN_AI_CALL_COUNT_BUCKETS,j as GEN_AI_DURATION_BUCKETS_S,g as GEN_AI_SCHEMA_URL,N as GEN_AI_TOKEN_BUCKETS,ne as createOtelTracing,re as createStructuralIdGenerator,z as projectLivePromptMetricSamples,A as projectLivePromptTrace};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["record","count","LABEL"],"sources":["../../src/timing.ts","../../src/projection.ts","../../src/metrics.ts","../../src/run-tracing.ts","../../src/span-ids.ts"],"sourcesContent":["import type { ILivePromptDurationTotal, ILivePromptQueueSummary } from '@robota-sdk/agent-interface-analytics';\n\ninterface IProjectedTimingTotals {\n provider?: ILivePromptDurationTotal;\n tool?: ILivePromptDurationTotal;\n queue?: ILivePromptQueueSummary;\n}\nconst record = (value: unknown): Record<string, unknown> | undefined =>\n value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined;\nconst count = (value: unknown): value is number => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;\nfunction duration(value: unknown): ILivePromptDurationTotal | undefined {\n const data = record(value);\n if (!data || !count(data.durationMs) || !count(data.samples) || !count(data.invalid) ||\n (data.samples === 0 && data.durationMs !== 0)) return undefined;\n return { durationMs: data.durationMs, samples: data.samples, invalid: data.invalid };\n}\n\n/** Allowlisted numeric evidence only, even when an embedding host passes an untyped batch. */\nfunction projectTimingTotals(value: unknown): IProjectedTimingTotals | undefined {\n const data = record(value);\n if (!data) return undefined;\n const provider = duration(data.provider), tool = duration(data.tool), queueDuration = duration(data.queue);\n const queueData = record(data.queue);\n const queue = queueDuration && queueData && count(queueData.admissionStarted) && count(queueData.notDispatched) &&\n queueData.admissionStarted + queueData.notDispatched === queueDuration.samples\n ? { ...queueDuration, admissionStarted: queueData.admissionStarted, notDispatched: queueData.notDispatched } : undefined;\n if (!provider && !tool && !queue) return undefined;\n return { ...(provider ? { provider } : {}), ...(tool ? { tool } : {}), ...(queue ? { queue } : {}) };\n}\n\n/** The prompt's timing totals as `agent.timing.*` attributes; GenAI conventions have no equivalent. */\nexport function timingAttributes(value: unknown): Record<string, number> {\n const totals = projectTimingTotals(value);\n const attributes: Record<string, number> = {};\n for (const kind of ['provider', 'tool', 'queue'] as const) {\n const total = totals?.[kind];\n if (!total) continue;\n attributes[`agent.timing.${kind}.duration_ms`] = total.durationMs;\n attributes[`agent.timing.${kind}.samples`] = total.samples;\n attributes[`agent.timing.${kind}.invalid`] = total.invalid;\n }\n if (totals?.queue) {\n attributes['agent.timing.queue.admission_started'] = totals.queue.admissionStarted;\n attributes['agent.timing.queue.not_dispatched'] = totals.queue.notDispatched;\n }\n return attributes;\n}\n","import type {\n ILivePromptTraceBatch,\n IProviderCallTraceEntry,\n IToolBodyTraceEntry,\n} from '@robota-sdk/agent-interface-analytics';\n\nimport { timingAttributes } from './timing.js';\n\n/**\n * Which names a projected span carries. `agent` keeps Robota's own `agent.*` names; `gen_ai` uses the\n * OpenTelemetry GenAI semantic conventions; `gen_ai/dup` uses GenAI span names and emits both attribute\n * sets, so a backend can move from one set of names to the other without a gap.\n */\nexport type TOtelProjectionMode = 'agent' | 'gen_ai/dup' | 'gen_ai';\n\nexport interface IOtelProjectionOptions {\n readonly mode: TOtelProjectionMode;\n /** Host-chosen agent name for the root `invoke_agent` span; GenAI modes only. */\n readonly agentName?: string;\n}\n\nexport type TOtelSpanKind = 'internal' | 'client';\nexport type TOtelAttributes = Readonly<Record<string, string | number>>;\n\n/** One span to record, independent of any OpenTelemetry SDK. */\nexport interface IOtelSpanProjection {\n readonly name: string;\n readonly kind: TOtelSpanKind;\n readonly traceId: string;\n readonly spanId: string;\n /** Absent on the root; every child's parent is the root. */\n readonly parentSpanId?: string;\n readonly startedAt: string;\n readonly endedAt: string;\n /** `error` for a failed outcome; an interrupted one is not an error. */\n readonly status: 'unset' | 'error';\n readonly attributes: TOtelAttributes;\n}\n\nexport interface IOtelTraceProjection {\n /** The schema the attribute names follow; absent in `agent` mode, whose names are Robota's own. */\n readonly schemaUrl?: string;\n readonly root: IOtelSpanProjection;\n readonly children: readonly IOtelSpanProjection[];\n}\n\n/** The GenAI semantic-convention version the `gen_ai` names follow. */\nexport const GEN_AI_SCHEMA_URL = 'https://opentelemetry.io/schemas/gen-ai-dev/1.42.0-dev';\n\n/** The same opaque-ID shape the framework's live-prompt-trace boundary allows through. */\nconst OPAQUE_ID = /^[A-Za-z0-9_-]{1,128}$/u;\n/** The same label shape the framework allows for provider and model labels. */\nconst LABEL = /^[A-Za-z0-9._:/-]{1,128}$/u;\n\n/**\n * Robota provider IDs whose GenAI provider name is certain. A Gemini client can point at either the\n * Gemini API or Vertex AI, so it gets the convention's name for any Google GenAI endpoint. An ID not\n * listed here gets no `gen_ai.provider.name` rather than a guessed one; `agent.provider.id` still names it.\n */\nconst GEN_AI_PROVIDER_NAMES: Readonly<Record<string, string>> = {\n anthropic: 'anthropic',\n openai: 'openai',\n deepseek: 'deepseek',\n gemini: 'gcp.gen_ai',\n google: 'gcp.gen_ai',\n};\n\nconst opaqueId = (value: unknown): string | undefined =>\n typeof value === 'string' && OPAQUE_ID.test(value) ? value : undefined;\n/** A model or tool name only in the label shape the framework allows; anything else is dropped. */\nexport const label = (value: unknown): string | undefined =>\n typeof value === 'string' && LABEL.test(value) ? value : undefined;\n/** Own keys only: an inherited property such as `constructor` is not a provider. */\nexport const genAiProviderName = (providerId: string | undefined): string | undefined =>\n providerId !== undefined && Object.hasOwn(GEN_AI_PROVIDER_NAMES, providerId) ? GEN_AI_PROVIDER_NAMES[providerId] : undefined;\nconst status = (outcome: string): 'unset' | 'error' => outcome === 'failure' ? 'error' : 'unset';\nconst spanName = (operation: string, subject: string | undefined): string =>\n subject ? `${operation} ${subject}` : operation;\n\n/**\n * GenAI conventions require `error.type` on a failed operation. The batch records that a span failed\n * but not why, so the projection uses the conventions' fallback value instead of naming a cause.\n */\nfunction withErrorType(attributes: TOtelAttributes, outcome: string, mode: TOtelProjectionMode): TOtelAttributes {\n return mode !== 'agent' && outcome === 'failure' ? { ...attributes, 'error.type': '_OTHER' } : attributes;\n}\n\n/** A provider-call or tool-body child of a trace: the children that get a span. */\nexport type TSpannedChild = Exclude<ILivePromptTraceBatch['children'][number], { readonly kind: 'permission' }>;\n\n/** A permission decision is content-free but has no duration of its own — it never gets a span. */\nfunction hasSpan(child: ILivePromptTraceBatch['children'][number]): child is TSpannedChild {\n return child.kind !== 'permission';\n}\n\nfunction rootAttributes(batch: ILivePromptTraceBatch, options: IOtelProjectionOptions): TOtelAttributes {\n const shared = {\n 'agent.turn.id': batch.turnId,\n 'agent.outcome': batch.root.outcome,\n 'agent.omitted.provider_count': batch.omittedChildren.provider,\n 'agent.omitted.tool_count': batch.omittedChildren.tool,\n 'agent.omitted.permission_count': batch.omittedChildren.permission,\n ...timingAttributes(batch.timingTotals),\n };\n const agent = { 'agent.session.id': batch.sessionId };\n const genAi = {\n 'gen_ai.operation.name': 'invoke_agent',\n ...(options.agentName ? { 'gen_ai.agent.name': options.agentName } : {}),\n 'gen_ai.conversation.id': batch.sessionId,\n };\n if (options.mode === 'agent') return { ...agent, ...shared };\n return options.mode === 'gen_ai' ? { ...genAi, ...shared } : { ...genAi, ...agent, ...shared };\n}\n\nfunction providerAttributes(trace: IProviderCallTraceEntry, mode: TOtelProjectionMode): TOtelAttributes {\n const complete = trace.usageProvenance === 'complete';\n const requestId = opaqueId(trace.providerRequestId);\n const agentOnly = mode !== 'gen_ai';\n // The CLI's attribute order, with the facts GenAI names differently present only outside `gen_ai`.\n const robota = {\n 'agent.outcome': trace.outcome,\n 'agent.provider.round': trace.round,\n ...(trace.disposition ? { 'agent.provider.disposition': trace.disposition } : {}),\n ...(trace.providerId ? { 'agent.provider.id': trace.providerId } : {}),\n ...(agentOnly && trace.modelId ? { 'agent.model.id': trace.modelId } : {}),\n ...(trace.usageProvenance ? { 'agent.usage.provenance': trace.usageProvenance } : {}),\n ...(complete ? {\n ...(agentOnly ? {\n 'agent.usage.input_tokens': trace.promptTokens ?? 0,\n 'agent.usage.output_tokens': trace.completionTokens ?? 0,\n } : {}),\n // GenAI conventions define input and output tokens but no total.\n 'agent.usage.total_tokens': trace.totalTokens ?? 0,\n } : {}),\n ...(requestId ? { 'agent.provider.request_id': requestId } : {}),\n };\n const providerName = genAiProviderName(trace.providerId);\n const genAi = {\n 'gen_ai.operation.name': 'chat',\n ...(providerName ? { 'gen_ai.provider.name': providerName } : {}),\n ...(trace.modelId ? { 'gen_ai.request.model': trace.modelId } : {}),\n ...(complete ? {\n 'gen_ai.usage.input_tokens': trace.promptTokens ?? 0,\n 'gen_ai.usage.output_tokens': trace.completionTokens ?? 0,\n } : {}),\n };\n return mode === 'agent' ? robota : { ...genAi, ...robota };\n}\n\nfunction toolAttributes(trace: IToolBodyTraceEntry, mode: TOtelProjectionMode): TOtelAttributes {\n const callId = opaqueId(trace.toolCallId);\n const toolName = label(trace.toolName);\n const shared = { 'agent.outcome': trace.outcome };\n const agent: TOtelAttributes = callId ? { 'agent.tool.call_id': callId } : {};\n const genAi = {\n 'gen_ai.operation.name': 'execute_tool',\n ...(toolName ? { 'gen_ai.tool.name': toolName } : {}),\n ...(callId ? { 'gen_ai.tool.call.id': callId } : {}),\n };\n if (mode === 'agent') return { ...shared, ...agent };\n return mode === 'gen_ai' ? { ...genAi, ...shared } : { ...genAi, ...shared, ...agent };\n}\n\n/** One child's span under `parentSpanId` in trace `traceId`; shared by the batch projection and the run observer. */\nexport function projectTraceChild(\n child: TSpannedChild, traceId: string, parentSpanId: string, mode: TOtelProjectionMode,\n): IOtelSpanProjection {\n const { trace } = child;\n const common = {\n traceId,\n spanId: trace.spanId,\n parentSpanId,\n startedAt: trace.startedAt,\n endedAt: trace.endedAt,\n status: status(trace.outcome),\n };\n if (child.kind === 'provider') {\n return {\n ...common,\n name: mode === 'agent' ? 'agent.provider_call' : spanName('chat', child.trace.modelId),\n kind: mode === 'agent' ? 'internal' : 'client',\n attributes: withErrorType(providerAttributes(child.trace, mode), trace.outcome, mode),\n };\n }\n return {\n ...common,\n name: mode === 'agent' ? 'agent.tool_body' : spanName('execute_tool', label(child.trace.toolName)),\n kind: 'internal',\n attributes: withErrorType(toolAttributes(child.trace, mode), trace.outcome, mode),\n };\n}\n\n/**\n * Map one live prompt trace batch onto the spans an OpenTelemetry exporter records: the prompt root,\n * then one child per provider call and tool body, in batch order. Permission decisions get no span.\n * The result carries only what the batch carries, so it stays content-free.\n */\nexport function projectLivePromptTrace(\n batch: ILivePromptTraceBatch, options: IOtelProjectionOptions,\n): IOtelTraceProjection {\n const { mode } = options;\n const root: IOtelSpanProjection = {\n name: mode === 'agent' ? 'agent.prompt_execution' : spanName('invoke_agent', options.agentName),\n kind: 'internal',\n traceId: batch.root.traceId,\n spanId: batch.root.spanId,\n startedAt: batch.root.startedAt,\n endedAt: batch.root.endedAt,\n status: status(batch.root.outcome),\n attributes: withErrorType(rootAttributes(batch, options), batch.root.outcome, mode),\n };\n // Every child belongs to the root's trace, whatever trace ID an unchecked batch gave it.\n const children = batch.children.filter(hasSpan)\n .map((child) => projectTraceChild(child, root.traceId, root.spanId, mode));\n return { ...(mode === 'agent' ? {} : { schemaUrl: GEN_AI_SCHEMA_URL }), root, children };\n}\n","import type { ILivePromptTraceBatch, IProviderCallTraceEntry } from '@robota-sdk/agent-interface-analytics';\n\nimport { genAiProviderName, label } from './projection.js';\nimport type { TOtelAttributes, TOtelProjectionMode } from './projection.js';\n\n/**\n * Bucket boundaries the GenAI conventions recommend, as pinned at semantic-conventions-genai\n * f3c6d0c9bb66ebd474668a3a0be25c61226f02c2. A histogram needs one more count than it has boundaries,\n * for values above the last one.\n */\nexport const GEN_AI_DURATION_BUCKETS_S: readonly number[] =\n [0.01, 0.02, 0.04, 0.08, 0.16, 0.32, 0.64, 1.28, 2.56, 5.12, 10.24, 20.48, 40.96, 81.92];\nexport const GEN_AI_AGENT_DURATION_BUCKETS_S: readonly number[] =\n [0.1, 0.2, 0.4, 0.8, 1.6, 3.2, 6.4, 12.8, 25.6, 51.2, 102.4, 204.8, 409.6];\nexport const GEN_AI_TOKEN_BUCKETS: readonly number[] =\n [1, 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864];\nexport const GEN_AI_CALL_COUNT_BUCKETS: readonly number[] = [1, 2, 4, 8, 16, 32, 64, 128];\n\n/** One measurement to record, independent of any OpenTelemetry SDK: a counter increment or a histogram value. */\nexport interface IOtelMetricSample {\n readonly name: string;\n readonly instrument: 'counter' | 'histogram';\n readonly unit: string;\n readonly value: number;\n readonly attributes: TOtelAttributes;\n /** Explicit bucket upper bounds of a histogram; absent for a counter. */\n readonly boundaries?: readonly number[];\n}\n\nexport interface IOtelMetricSampleOptions {\n /** The GenAI attribute set in use, as for spans: `gen_ai/dup` also keeps `agent.model.id`, `gen_ai` does not. */\n readonly mode: Exclude<TOtelProjectionMode, 'agent'>;\n /** Host-chosen agent name carried on the agent, tool and root metrics. */\n readonly agentName?: string;\n}\n\n/** Same fallback value the span projection uses: the batch records that a call failed, not why. */\nconst ERROR_TYPE = { 'error.type': '_OTHER' } as const;\n\nconst seconds = (startedAt: string, endedAt: string): number | undefined => {\n const value = (Date.parse(endedAt) - Date.parse(startedAt)) / 1000;\n return Number.isFinite(value) && value >= 0 ? value : undefined;\n};\nconst tokenCount = (value: unknown): number | undefined =>\n typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : undefined;\n\nfunction providerCallSamples(trace: IProviderCallTraceEntry, mode: IOtelMetricSampleOptions['mode']): IOtelMetricSample[] {\n const providerName = genAiProviderName(trace.providerId);\n const providerId = label(trace.providerId);\n const model = label(trace.modelId);\n // Same rule as the provider span: a fact the conventions do not define keeps its `agent.*` name, so an\n // operator's provider split survives (a provider outside the known map has no `gen_ai.provider.name`);\n // the model has a convention name, so `agent.model.id` is repeated only while both sets are emitted.\n const base: TOtelAttributes = {\n 'gen_ai.operation.name': 'chat',\n ...(providerName ? { 'gen_ai.provider.name': providerName } : {}),\n ...(model ? { 'gen_ai.request.model': model } : {}),\n ...(providerId ? { 'agent.provider.id': providerId } : {}),\n ...(mode === 'gen_ai/dup' && model ? { 'agent.model.id': model } : {}),\n };\n const attributes: TOtelAttributes = trace.outcome === 'failure' ? { ...base, ...ERROR_TYPE } : base;\n // Without a complete count the conventions ask for 0, not for a missing sample; providers do not\n // report usage per modality, so the counters' modality is `unknown`.\n const promptTokens = tokenCount(trace.promptTokens);\n const completionTokens = tokenCount(trace.completionTokens);\n const known = trace.usageProvenance === 'complete' && promptTokens !== undefined && completionTokens !== undefined;\n const input = known ? promptTokens : 0;\n const output = known ? completionTokens : 0;\n const counterAttributes: TOtelAttributes = { ...base, 'gen_ai.token.modality': 'unknown' };\n const duration = seconds(trace.startedAt, trace.endedAt);\n const tokens = (name: string, value: number): IOtelMetricSample => ({\n name, instrument: 'histogram', unit: '{token}', value, attributes, boundaries: GEN_AI_TOKEN_BUCKETS,\n });\n return [\n ...(duration === undefined ? [] : [{\n name: 'gen_ai.client.inference.duration', instrument: 'histogram' as const, unit: 's', value: duration,\n attributes, boundaries: GEN_AI_DURATION_BUCKETS_S,\n }]),\n { name: 'gen_ai.client.inference.usage.input_tokens', instrument: 'counter', unit: '{token}', value: input, attributes: counterAttributes },\n { name: 'gen_ai.client.inference.usage.output_tokens', instrument: 'counter', unit: '{token}', value: output, attributes: counterAttributes },\n tokens('gen_ai.client.inference.operation.input_tokens', input),\n tokens('gen_ai.client.inference.operation.output_tokens', output),\n ];\n}\n\n/**\n * Map one live prompt trace batch onto the GenAI metrics a host records: one inference measurement set\n * per invoked provider call, one duration per tool body, and the prompt's agent-level duration and call\n * counts. Provider-derived samples are left out when the batch omitted provider children, and\n * tool-derived ones when it omitted tool children, because a bounded child list cannot prove a complete\n * total. Cache hits and refused calls were not inferences, and permission decisions have no duration, so\n * they produce nothing. Like the span projection, it is pure and content-free.\n */\nexport function projectLivePromptMetricSamples(\n batch: ILivePromptTraceBatch, options: IOtelMetricSampleOptions,\n): IOtelMetricSample[] {\n const agent: TOtelAttributes = options.agentName ? { 'gen_ai.agent.name': options.agentName } : {};\n const samples: IOtelMetricSample[] = [];\n const providerComplete = batch.omittedChildren.provider === 0;\n const toolComplete = batch.omittedChildren.tool === 0;\n let invokedCalls = 0;\n let toolBodies = 0;\n\n for (const child of batch.children) {\n if (child.kind !== 'provider' || child.trace.disposition !== 'invoked') continue;\n invokedCalls += 1;\n if (providerComplete) samples.push(...providerCallSamples(child.trace, options.mode));\n }\n for (const child of batch.children) {\n if (child.kind !== 'tool') continue;\n toolBodies += 1;\n const duration = toolComplete ? seconds(child.trace.startedAt, child.trace.endedAt) : undefined;\n if (duration === undefined) continue;\n const toolName = label(child.trace.toolName);\n samples.push({\n name: 'gen_ai.execute_tool.duration', instrument: 'histogram', unit: 's', value: duration,\n boundaries: GEN_AI_DURATION_BUCKETS_S,\n attributes: {\n ...(toolName ? { 'gen_ai.tool.name': toolName } : {}), ...agent,\n ...(child.trace.outcome === 'failure' ? ERROR_TYPE : {}),\n },\n });\n }\n\n const rootDuration = seconds(batch.root.startedAt, batch.root.endedAt);\n if (rootDuration !== undefined) samples.push({\n name: 'gen_ai.invoke_agent.duration', instrument: 'histogram', unit: 's', value: rootDuration,\n boundaries: GEN_AI_AGENT_DURATION_BUCKETS_S,\n attributes: { ...agent, ...(batch.root.outcome === 'failure' ? ERROR_TYPE : {}) },\n });\n if (providerComplete) samples.push({\n name: 'gen_ai.invoke_agent.inference_calls', instrument: 'histogram', unit: '{inference_call}',\n value: invokedCalls, attributes: agent, boundaries: GEN_AI_CALL_COUNT_BUCKETS,\n });\n if (toolComplete) samples.push({\n name: 'gen_ai.invoke_agent.tool_calls', instrument: 'histogram', unit: '{tool_call}',\n value: toolBodies, attributes: agent, boundaries: GEN_AI_CALL_COUNT_BUCKETS,\n });\n return samples;\n}\n","import { ROOT_CONTEXT, SpanKind, SpanStatusCode, TraceFlags, isSpanContextValid, trace } from '@opentelemetry/api';\nimport {\n AbstractPlugin,\n PROVIDER_CALL_EVENTS,\n PluginCategory,\n PluginPriority,\n spanIdFromMintedId,\n} from '@robota-sdk/agent-core';\n\nimport { projectTraceChild } from './projection.js';\n\nimport type { IStructuralIdGenerator } from './span-ids.js';\nimport type { IOtelSpanProjection, TOtelProjectionMode, TSpannedChild } from './projection.js';\nimport type { SpanContext, Tracer } from '@opentelemetry/api';\nimport type {\n IRunTraceContext,\n IToolExecutionContext,\n IToolExecutionResult,\n TExecutionEventCallback,\n TSubprocessTraceClass,\n TToolParameters,\n} from '@robota-sdk/agent-core';\nimport type { IProviderCallTraceEntry } from '@robota-sdk/agent-interface-analytics';\n\nexport interface IOtelTracingOptions {\n /** The host's tracer; spans are recorded on it and never exported by this package. */\n readonly tracer: Tracer;\n readonly mode: TOtelProjectionMode;\n /**\n * The generator the host gave its `TracerProvider`. With it, each span carries the ID Robota sent\n * in `traceparent`; without it, spans get the provider's own IDs and only the trace and parent match.\n */\n readonly idGenerator?: IStructuralIdGenerator;\n}\n\nexport interface IOtelRunPropagation {\n /** Exact origins that may receive `traceparent` from provider calls and tool bodies. */\n readonly allowedOrigins?: readonly string[];\n readonly subprocessClasses?: readonly TSubprocessTraceClass[];\n readonly onPropagationUnavailable?: (providerId: string) => void;\n}\n\n/** What one `run()` takes, plus `end()` once it has returned. */\nexport interface IOtelRunTracing {\n /** Absent when the parent span is invalid or not sampled: the run then traces and propagates nothing. */\n readonly traceContext?: IRunTraceContext;\n readonly onExecutionEvent: TExecutionEventCallback;\n /** Stop attributing tool bodies to this run. */\n end(): void;\n}\n\nexport interface IOtelTracing {\n /** Register once in the agent's `plugins`; it records tool bodies for every traced run. */\n readonly plugin: AbstractPlugin;\n /** Trace one run under `parent`, typically the host's own `invoke_agent` span. */\n startRun(parent: SpanContext, propagation?: IOtelRunPropagation): IOtelRunTracing;\n}\n\nconst ID = /^[A-Za-z0-9_-]{1,128}$/u;\nconst LABEL = /^[A-Za-z0-9._:/-]{1,128}$/u;\nconst SPAN_ID = /^(?!0{16}$)[0-9a-f]{16}$/u;\nconst OUTCOMES = new Set(['success', 'failure', 'interrupted']);\nconst DISPOSITIONS = new Set(['invoked', 'cache-hit', 'preflight-refused']);\nconst PROVENANCES = new Set(['complete', 'partial', 'absent']);\n\ntype TOutcome = IProviderCallTraceEntry['outcome'];\n\ninterface IRun {\n readonly parent: SpanContext;\n readonly executionIds: Set<string>;\n}\n\ninterface IPendingToolBody {\n readonly startedAtMs: number;\n readonly spanId: string;\n readonly executionId: string | undefined;\n readonly toolCallId: string | undefined;\n readonly signal: AbortSignal | undefined;\n}\n\nconst text = (value: unknown, pattern: RegExp): string | undefined =>\n typeof value === 'string' && pattern.test(value) ? value : undefined;\nconst count = (value: unknown): value is number =>\n typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;\nconst instant = (value: unknown): value is string =>\n typeof value === 'string' && !Number.isNaN(Date.parse(value));\n\n/** A provider-call child from agent-core's content-free completion event, or undefined when malformed. */\nfunction providerChild(data: Record<string, unknown>, parent: SpanContext): TSpannedChild | undefined {\n const callId = text(data['callId'], ID);\n const spanId = callId === undefined ? undefined : spanIdFromMintedId(callId);\n if (spanId === undefined || !SPAN_ID.test(spanId) || !instant(data['startedAt']) || !instant(data['endedAt']) ||\n !OUTCOMES.has(data['outcome'] as string) || !count(data['round']) || data['round'] < 1) return undefined;\n const disposition = DISPOSITIONS.has(data['disposition'] as string)\n ? data['disposition'] as IProviderCallTraceEntry['disposition'] : undefined;\n const provenance = PROVENANCES.has(data['usageProvenance'] as string)\n ? data['usageProvenance'] as IProviderCallTraceEntry['usageProvenance'] : undefined;\n const complete = provenance === 'complete' &&\n count(data['promptTokens']) && count(data['completionTokens']) && count(data['totalTokens']);\n const providerId = text(data['providerId'], LABEL);\n const modelId = text(data['modelId'], LABEL);\n const providerRequestId = disposition === 'invoked' ? text(data['providerRequestId'], ID) : undefined;\n return {\n kind: 'provider',\n trace: {\n callId, traceId: parent.traceId, parentSpanId: parent.spanId, spanId,\n startedAt: data['startedAt'], endedAt: data['endedAt'], outcome: data['outcome'] as TOutcome,\n round: data['round'],\n ...(disposition ? { disposition } : {}),\n ...(providerId ? { providerId } : {}),\n ...(modelId ? { modelId } : {}),\n ...(provenance ? { usageProvenance: complete || provenance !== 'complete' ? provenance : 'partial' } : {}),\n ...(complete ? {\n promptTokens: data['promptTokens'] as number,\n completionTokens: data['completionTokens'] as number,\n totalTokens: data['totalTokens'] as number,\n } : {}),\n ...(providerRequestId ? { providerRequestId } : {}),\n },\n };\n}\n\nfunction record(tracer: Tracer, ids: IStructuralIdGenerator | undefined, parent: SpanContext,\n span: IOtelSpanProjection): void {\n const context = trace.setSpanContext(ROOT_CONTEXT, parent);\n const start = (): ReturnType<Tracer['startSpan']> => tracer.startSpan(span.name, {\n kind: span.kind === 'client' ? SpanKind.CLIENT : SpanKind.INTERNAL,\n startTime: new Date(span.startedAt),\n attributes: span.attributes,\n }, context);\n const started = ids ? ids.withSpanId(span.spanId, start) : start();\n if (span.status === 'error') started.setStatus({ code: SpanStatusCode.ERROR });\n started.end(new Date(span.endedAt));\n}\n\n/**\n * The run a tool body belongs to: the innermost execution on its owner path. An agent created inside\n * another agent's tool keeps that ancestor's path in front of its own segments.\n */\nfunction executionIdOf(context: IToolExecutionContext | undefined): string | undefined {\n const path = context?.ownerPath ?? [];\n for (let index = path.length - 1; index >= 0; index--) {\n if (path[index]?.type === 'execution') return path[index]?.id;\n }\n return undefined;\n}\n\n/** Tracing must never change a run: a failure to record drops the span, never the operation. */\nfunction guarded(observe: () => void): void {\n try {\n observe();\n } catch {\n // allow-fallback: an observer failure loses one span and leaves the run untouched.\n }\n}\n\nclass OtelToolBodyPlugin extends AbstractPlugin {\n readonly name = 'OtelToolBodyPlugin';\n readonly version = '1.0.0';\n // Keyed by the call's parameters object: agent-core hands the same object to both hooks of one\n // call, and the vendor tool call ID can repeat within and across runs.\n private readonly pending = new WeakMap<TToolParameters, IPendingToolBody>();\n\n override category = PluginCategory.MONITORING;\n override priority: number = PluginPriority.LOW;\n\n constructor(private readonly onBody: (body: IPendingToolBody, toolName: string, outcome: TOutcome) => void) {\n super();\n }\n\n override async beforeToolCall(_toolName: string, parameters: TToolParameters,\n context?: IToolExecutionContext): Promise<void> {\n guarded(() => {\n const bodyId = text(context?.toolBodyId, ID);\n const spanId = bodyId === undefined ? undefined : spanIdFromMintedId(bodyId);\n if (spanId === undefined || !SPAN_ID.test(spanId)) return;\n this.pending.set(parameters, {\n startedAtMs: Date.now(), spanId, executionId: executionIdOf(context),\n toolCallId: text(context?.executionId, ID), signal: context?.signal,\n });\n });\n }\n\n override async afterToolCall(toolName: string, parameters: TToolParameters,\n result: IToolExecutionResult): Promise<void> {\n guarded(() => {\n const body = this.pending.get(parameters);\n if (!body) return;\n this.pending.delete(parameters);\n this.onBody(body, toolName, body.signal?.aborted ? 'interrupted' : result.success ? 'success' : 'failure');\n });\n }\n}\n\n/**\n * Record a Robota agent's provider calls and tool bodies as OpenTelemetry spans on the host's tracer,\n * named by `mode` exactly as {@link projectLivePromptTrace} names them.\n */\nexport function createOtelTracing(options: IOtelTracingOptions): IOtelTracing {\n const { tracer, mode, idGenerator } = options;\n const runs = new Map<string, IRun>();\n const plugin = new OtelToolBodyPlugin((body, toolName, outcome) => {\n const run = body.executionId === undefined ? undefined : runs.get(body.executionId);\n if (!run) return;\n const startedAt = new Date(body.startedAtMs).toISOString();\n const child: TSpannedChild = {\n kind: 'tool',\n trace: {\n traceId: run.parent.traceId, parentSpanId: run.parent.spanId, spanId: body.spanId,\n startedAt, endedAt: new Date(Math.max(Date.now(), body.startedAtMs)).toISOString(), outcome,\n ...(body.toolCallId ? { toolCallId: body.toolCallId } : {}),\n ...(text(toolName, LABEL) ? { toolName } : {}),\n },\n };\n record(tracer, idGenerator, run.parent, projectTraceChild(child, run.parent.traceId, run.parent.spanId, mode));\n });\n return {\n plugin,\n startRun(parent, propagation = {}) {\n // Robota always sends a sampled `traceparent`; under a parent the host dropped, recipients would\n // record spans for a trace that does not exist, so nothing is propagated or recorded.\n if (!isSpanContextValid(parent) || (parent.traceFlags & TraceFlags.SAMPLED) === 0) {\n return { onExecutionEvent: () => undefined, end: () => undefined };\n }\n const run: IRun = { parent, executionIds: new Set() };\n return {\n traceContext: {\n traceId: parent.traceId,\n parentSpanId: parent.spanId,\n allowedOrigins: [...(propagation.allowedOrigins ?? [])],\n ...(propagation.subprocessClasses ? { subprocessClasses: [...propagation.subprocessClasses] } : {}),\n ...(propagation.onPropagationUnavailable\n ? { onPropagationUnavailable: propagation.onPropagationUnavailable } : {}),\n },\n onExecutionEvent(event, data) {\n guarded(() => {\n // Every run event names its execution before any tool body of it is dispatched.\n const executionId = data['executionId'];\n if (typeof executionId === 'string' && !run.executionIds.has(executionId)) {\n run.executionIds.add(executionId);\n runs.set(executionId, run);\n }\n if (event !== PROVIDER_CALL_EVENTS.COMPLETED) return;\n const child = providerChild(data, parent);\n if (child) record(tracer, idGenerator, parent, projectTraceChild(child, parent.traceId, parent.spanId, mode));\n });\n },\n end() {\n for (const executionId of run.executionIds) if (runs.get(executionId) === run) runs.delete(executionId);\n run.executionIds.clear();\n },\n };\n },\n };\n}\n","/**\n * The OpenTelemetry SDK `IdGenerator` shape, declared structurally so this package does not depend\n * on an SDK version.\n */\nexport interface IOtelIdGenerator {\n generateTraceId(): string;\n generateSpanId(): string;\n}\n\n/**\n * An `IdGenerator` for a host's `TracerProvider` that can hand the next span a chosen ID. Robota\n * derives each provider call's and tool body's span ID before the operation runs and sends it in\n * `traceparent`; recording the span under that same ID is what lets a recipient's spans join the\n * trace. Every other span gets a random ID.\n */\nexport interface IStructuralIdGenerator extends IOtelIdGenerator {\n /** Run `start` with `spanId` as the ID of the one span it starts. */\n withSpanId<T>(spanId: string, start: () => T): T;\n}\n\nfunction randomHex(bytes: number): string {\n const values = new Uint8Array(bytes);\n // A valid ID is never all zeros; retry the one-in-2^64 draw that is.\n do globalThis.crypto.getRandomValues(values); while (values.every((value) => value === 0));\n return Array.from(values, (value) => value.toString(16).padStart(2, '0')).join('');\n}\n\nexport function createStructuralIdGenerator(): IStructuralIdGenerator {\n let next: string | undefined;\n return {\n generateTraceId: () => randomHex(16),\n generateSpanId() {\n const chosen = next;\n next = undefined;\n return chosen ?? randomHex(8);\n },\n withSpanId(spanId, start) {\n next = spanId;\n try {\n return start();\n } finally {\n // A tracer that never asked for an ID (a no-op tracer) must not pass it to the next span.\n next = undefined;\n }\n },\n };\n}\n"],"mappings":"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"}
|