@telemetry-dev/eve 0.1.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/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@telemetry-dev/eve",
3
+ "version": "0.1.0",
4
+ "description": "Eve (Vercel agent framework) telemetry integration for telemetry.dev.",
5
+ "keywords": [
6
+ "agents",
7
+ "eve",
8
+ "genai",
9
+ "llm",
10
+ "observability",
11
+ "opentelemetry",
12
+ "telemetry",
13
+ "tracing"
14
+ ],
15
+ "homepage": "https://telemetry.dev",
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/telemetry-dev/telemetry.dev.git",
20
+ "directory": "packages/eve"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "src"
25
+ ],
26
+ "type": "module",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.mts",
30
+ "import": "./dist/index.mjs",
31
+ "default": "./dist/index.mjs"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "dependencies": {
39
+ "@opentelemetry/api": "^1.9.1",
40
+ "@telemetry-dev/sdk": "^0.1.0"
41
+ },
42
+ "devDependencies": {
43
+ "@opentelemetry/sdk-logs": "^0.218.0",
44
+ "@opentelemetry/sdk-trace-base": "^2.7.1",
45
+ "@types/node": "^25.5.0",
46
+ "@typescript/native-preview": "7.0.0-dev.20260328.1",
47
+ "ai": "7.0.0",
48
+ "eve": "0.19.0",
49
+ "typescript": "^6.0.2",
50
+ "vite-plus": "0.1.20",
51
+ "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.20",
52
+ "zod": "^4.4.3"
53
+ },
54
+ "peerDependencies": {
55
+ "eve": ">=0.19.0 <1"
56
+ },
57
+ "scripts": {
58
+ "build": "pnpm exec vp pack",
59
+ "dev": "pnpm exec vp pack --watch",
60
+ "test": "vp test",
61
+ "check": "vp check"
62
+ }
63
+ }
package/src/client.ts ADDED
@@ -0,0 +1,277 @@
1
+ import type { Attributes } from "@opentelemetry/api";
2
+ import {
3
+ startSpan,
4
+ type ClientOverrides,
5
+ type SpanHandle,
6
+ type TokenUsage,
7
+ } from "@telemetry-dev/sdk";
8
+ import type {
9
+ Client,
10
+ ClientSession,
11
+ HandleMessageStreamEvent,
12
+ SendTurnInput,
13
+ SendTurnPayload,
14
+ SessionState,
15
+ } from "eve/client";
16
+ import { isCurrentTurnBoundaryEvent, MessageResponse } from "eve/client";
17
+
18
+ import { ensureInit, type TelemetryDevEveOptions } from "./config.ts";
19
+
20
+ export interface WrapEveClientOptions extends TelemetryDevEveOptions {
21
+ /** gen_ai.agent.name on turn spans. */
22
+ agentName?: string;
23
+ /** Span name for each send() turn. Default "invoke_agent". */
24
+ spanName?: string;
25
+ }
26
+
27
+ function asRecord(value: unknown): Record<string, unknown> | undefined {
28
+ if (value === null || typeof value !== "object") return undefined;
29
+ return value as Record<string, unknown>;
30
+ }
31
+
32
+ function stringField(record: Record<string, unknown> | undefined, key: string): string | undefined {
33
+ const value = record?.[key];
34
+ return typeof value === "string" && value.length > 0 ? value : undefined;
35
+ }
36
+
37
+ function numberField(record: Record<string, unknown> | undefined, key: string): number | undefined {
38
+ const value = record?.[key];
39
+ return typeof value === "number" ? value : undefined;
40
+ }
41
+
42
+ function eventData(event: HandleMessageStreamEvent): Record<string, unknown> {
43
+ if (!("data" in event)) return {};
44
+ return asRecord(event.data) ?? {};
45
+ }
46
+
47
+ function eventAttributes(
48
+ attributes: Record<string, string | number | boolean | undefined>,
49
+ ): Attributes {
50
+ const out: Attributes = {};
51
+ for (const [key, value] of Object.entries(attributes)) {
52
+ if (value !== undefined) out[key] = value;
53
+ }
54
+ return out;
55
+ }
56
+
57
+ function addUsage(usage: TokenUsage, key: keyof TokenUsage, value: number | undefined): boolean {
58
+ if (value === undefined) return false;
59
+ usage[key] = (usage[key] ?? 0) + value;
60
+ return true;
61
+ }
62
+
63
+ function failureError(code: string | undefined, message: string | undefined): Error {
64
+ const error = new Error(message ?? code ?? "error");
65
+ if (code) error.name = code;
66
+ return error;
67
+ }
68
+
69
+ function inputForSpan<TOutput>(payload: SendTurnPayload<TOutput>): unknown {
70
+ return payload.message ?? payload.inputResponses;
71
+ }
72
+
73
+ function bindOrReturn<T extends object>(target: T, prop: string | symbol): unknown {
74
+ const value = Reflect.get(target, prop, target);
75
+ return typeof value === "function" ? value.bind(target) : value;
76
+ }
77
+
78
+ function addLifecycleEvent(span: SpanHandle, event: HandleMessageStreamEvent): void {
79
+ const data = eventData(event);
80
+ switch (event.type) {
81
+ case "subagent.called": {
82
+ const remote = asRecord(data.remote);
83
+ span.span.addEvent(
84
+ event.type,
85
+ eventAttributes({
86
+ "eve.subagent.name": stringField(data, "name"),
87
+ "gen_ai.tool.name": stringField(data, "toolName"),
88
+ "gen_ai.tool.call.id": stringField(data, "callId"),
89
+ "eve.child.session_id": stringField(data, "childSessionId"),
90
+ "eve.workflow.id": stringField(data, "workflowId"),
91
+ "eve.remote.url": stringField(remote, "url"),
92
+ }),
93
+ );
94
+ return;
95
+ }
96
+ case "input.requested": {
97
+ const requests = Array.isArray(data.requests) ? data.requests : undefined;
98
+ span.span.addEvent(
99
+ event.type,
100
+ eventAttributes({ "eve.input.request_count": requests?.length }),
101
+ );
102
+ return;
103
+ }
104
+ case "authorization.required":
105
+ span.span.addEvent(
106
+ event.type,
107
+ eventAttributes({ "eve.authorization.name": stringField(data, "name") }),
108
+ );
109
+ return;
110
+ default:
111
+ return;
112
+ }
113
+ }
114
+
115
+ async function* instrumentedStream<TOutput>(
116
+ response: MessageResponse<TOutput>,
117
+ span: SpanHandle,
118
+ start: number,
119
+ payload: SendTurnPayload<TOutput>,
120
+ ): AsyncGenerator<HandleMessageStreamEvent> {
121
+ const usage: TokenUsage = {};
122
+ let sawUsage = false;
123
+ let output: unknown;
124
+ let finishReason: string | undefined;
125
+ let timeToFirstChunkMs: number | undefined;
126
+ let error: unknown;
127
+ let sawTerminalEvent = false;
128
+
129
+ const finish = (): void => {
130
+ if (!sawTerminalEvent && error === undefined) {
131
+ if (payload.signal?.aborted) {
132
+ error = failureError("cancelled", "cancelled");
133
+ finishReason ??= "cancelled";
134
+ } else {
135
+ error = failureError("stream_incomplete", "stream ended before a terminal event");
136
+ finishReason ??= "error";
137
+ }
138
+ }
139
+ span.end({
140
+ usage: sawUsage ? usage : undefined,
141
+ finishReason,
142
+ output,
143
+ timeToFirstChunkMs,
144
+ error,
145
+ });
146
+ };
147
+
148
+ try {
149
+ for await (const event of response) {
150
+ const data = eventData(event);
151
+ if (isCurrentTurnBoundaryEvent(event)) sawTerminalEvent = true;
152
+ if (
153
+ timeToFirstChunkMs === undefined &&
154
+ (event.type === "message.appended" || event.type === "reasoning.appended")
155
+ ) {
156
+ timeToFirstChunkMs = performance.now() - start;
157
+ }
158
+
159
+ if (event.type === "step.completed") {
160
+ const eventUsage = asRecord(data.usage);
161
+ sawUsage =
162
+ addUsage(usage, "inputTokens", numberField(eventUsage, "inputTokens")) || sawUsage;
163
+ sawUsage =
164
+ addUsage(usage, "outputTokens", numberField(eventUsage, "outputTokens")) || sawUsage;
165
+ sawUsage =
166
+ addUsage(usage, "cacheReadInputTokens", numberField(eventUsage, "cacheReadTokens")) ||
167
+ sawUsage;
168
+ sawUsage =
169
+ addUsage(
170
+ usage,
171
+ "cacheCreationInputTokens",
172
+ numberField(eventUsage, "cacheWriteTokens"),
173
+ ) || sawUsage;
174
+ finishReason = stringField(data, "finishReason") ?? finishReason;
175
+ }
176
+
177
+ if (
178
+ event.type === "message.completed" &&
179
+ stringField(data, "finishReason") !== "tool-calls"
180
+ ) {
181
+ output = data.message;
182
+ }
183
+ if (event.type === "result.completed") {
184
+ output = data.result;
185
+ }
186
+ if (
187
+ event.type === "turn.failed" ||
188
+ event.type === "session.failed" ||
189
+ event.type === "step.failed"
190
+ ) {
191
+ if (error === undefined) {
192
+ const code = stringField(data, "code");
193
+ error = failureError(code, stringField(data, "message"));
194
+ if (code) span.span.setAttribute("error.code", code);
195
+ }
196
+ finishReason = "error";
197
+ }
198
+
199
+ addLifecycleEvent(span, event);
200
+ yield event;
201
+ }
202
+ } catch (caught) {
203
+ if (payload.signal?.aborted) {
204
+ error = failureError("cancelled", "cancelled");
205
+ finishReason ??= "cancelled";
206
+ } else {
207
+ error = caught;
208
+ }
209
+ throw caught;
210
+ } finally {
211
+ finish();
212
+ }
213
+ }
214
+
215
+ function wrapSession(session: ClientSession, options: WrapEveClientOptions): ClientSession {
216
+ return new Proxy(session, {
217
+ get(target, prop) {
218
+ if (prop === "send") {
219
+ const wrappedSend: ClientSession["send"] = async <TOutput = unknown>(
220
+ input: SendTurnInput<TOutput>,
221
+ ) => {
222
+ const payload: SendTurnPayload<TOutput> =
223
+ typeof input === "string" ? { message: input } : input;
224
+ const span = startSpan(options.spanName ?? "invoke_agent", {
225
+ type: "agent",
226
+ agentName: options.agentName,
227
+ input: inputForSpan(payload),
228
+ });
229
+ const start = performance.now();
230
+ let response: MessageResponse<TOutput>;
231
+ try {
232
+ response = await target.send(input);
233
+ } catch (error) {
234
+ if (payload.signal?.aborted) {
235
+ span.end({
236
+ error: failureError("cancelled", "cancelled"),
237
+ finishReason: "cancelled",
238
+ });
239
+ } else {
240
+ span.end({ error });
241
+ }
242
+ throw error;
243
+ }
244
+
245
+ span.span.setAttribute("gen_ai.conversation.id", response.sessionId);
246
+ // MessageResponse's constructor is @internal in eve; re-wrapping the stream has no
247
+ // public alternative. Revisit on eve upgrades.
248
+ return new MessageResponse<TOutput>({
249
+ continuationToken: response.continuationToken,
250
+ sessionId: response.sessionId,
251
+ createStream: () => instrumentedStream(response, span, start, payload),
252
+ });
253
+ };
254
+ return wrappedSend;
255
+ }
256
+ return bindOrReturn(target, prop);
257
+ },
258
+ });
259
+ }
260
+
261
+ export function wrapEveClient<C extends Client>(
262
+ client: C,
263
+ options: WrapEveClientOptions = {},
264
+ overrides?: ClientOverrides,
265
+ ): C {
266
+ const { agentName: _agentName, spanName: _spanName, ...sdkOptions } = options;
267
+ ensureInit(sdkOptions, overrides);
268
+
269
+ return new Proxy(client, {
270
+ get(target, prop) {
271
+ if (prop === "session") {
272
+ return (state?: SessionState | string) => wrapSession(target.session(state), options);
273
+ }
274
+ return bindOrReturn(target, prop);
275
+ },
276
+ }) as C;
277
+ }
package/src/config.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { init, shutdown, type ClientOverrides, type TelemetryOptions } from "@telemetry-dev/sdk";
2
+
3
+ /** Options accepted by every @telemetry-dev/eve entry point. */
4
+ export type TelemetryDevEveOptions = Omit<TelemetryOptions, "registerGlobal">;
5
+
6
+ export type { ClientOverrides };
7
+
8
+ let initialized = false;
9
+
10
+ /**
11
+ * Initializes the telemetry.dev SDK exactly once per process for this integration.
12
+ * registerGlobal:true lets eve's tracers (scopes "eve" and "gen_ai") resolve to
13
+ * the SDK provider. spanFilter defaults to exporting everything except eve's
14
+ * internal workflow-engine spans (scope "workflow"); pass spanFilter: () => true
15
+ * to export those too.
16
+ */
17
+ export function ensureInit(
18
+ options: TelemetryDevEveOptions = {},
19
+ overrides?: ClientOverrides,
20
+ ): void {
21
+ if (initialized) return;
22
+ initialized = true;
23
+ init(
24
+ {
25
+ ...options,
26
+ registerGlobal: true,
27
+ spanFilter: options.spanFilter ?? ((span) => span.instrumentationScope.name !== "workflow"),
28
+ },
29
+ overrides,
30
+ );
31
+ }
32
+
33
+ /** Test seam for unit tests that need a fresh SDK singleton. */
34
+ export async function resetForTesting(): Promise<void> {
35
+ initialized = false;
36
+ await shutdown();
37
+ }
package/src/hook.ts ADDED
@@ -0,0 +1,313 @@
1
+ import { log, type LogLevel } from "@telemetry-dev/sdk";
2
+ import type { HandleMessageStreamEvent } from "eve/client";
3
+ import type { HookContext, HookDefinition } from "eve/hooks";
4
+
5
+ import { ensureInit, type ClientOverrides, type TelemetryDevEveOptions } from "./config.ts";
6
+
7
+ type Attrs = Record<string, unknown>;
8
+
9
+ function asRecord(value: unknown): Record<string, unknown> | undefined {
10
+ if (value === null || typeof value !== "object") return undefined;
11
+ return value as Record<string, unknown>;
12
+ }
13
+
14
+ function eventData(event: HandleMessageStreamEvent): Record<string, unknown> {
15
+ if (!("data" in event)) return {};
16
+ return asRecord(event.data) ?? {};
17
+ }
18
+
19
+ function stringField(record: Record<string, unknown> | undefined, key: string): string | undefined {
20
+ const value = record?.[key];
21
+ return typeof value === "string" && value.length > 0 ? value : undefined;
22
+ }
23
+
24
+ function numberField(record: Record<string, unknown> | undefined, key: string): number | undefined {
25
+ const value = record?.[key];
26
+ return typeof value === "number" ? value : undefined;
27
+ }
28
+
29
+ function jsonField(record: Record<string, unknown> | undefined, key: string): string | undefined {
30
+ const value = record?.[key];
31
+ if (value === undefined) return undefined;
32
+ try {
33
+ return JSON.stringify(value);
34
+ } catch {
35
+ return undefined;
36
+ }
37
+ }
38
+
39
+ function baseAttributes(event: HandleMessageStreamEvent, ctx: HookContext): Attrs {
40
+ const data = eventData(event);
41
+ return {
42
+ "gen_ai.conversation.id": ctx.session.id,
43
+ "gen_ai.agent.name": ctx.agent.name,
44
+ "eve.channel.kind": ctx.channel.kind,
45
+ "eve.turn.id": stringField(data, "turnId"),
46
+ "eve.step.index": numberField(data, "stepIndex"),
47
+ "eve.turn.sequence": numberField(data, "sequence"),
48
+ };
49
+ }
50
+
51
+ function toolAttrs(result: unknown): Attrs {
52
+ const record = asRecord(result);
53
+ const subagentName = stringField(record, "subagentName");
54
+ return {
55
+ "gen_ai.tool.name":
56
+ stringField(record, "toolName") ??
57
+ (subagentName ? `eve:subagent:${subagentName}` : undefined),
58
+ "gen_ai.tool.call.id": stringField(record, "callId"),
59
+ "eve.subagent.name": subagentName,
60
+ };
61
+ }
62
+ function reportError(onError: ((error: unknown) => void) | undefined, error: unknown): void {
63
+ try {
64
+ onError?.(error);
65
+ } catch {
66
+ // telemetry hooks must never fail an Eve turn.
67
+ }
68
+ }
69
+
70
+ function emit(
71
+ event: HandleMessageStreamEvent,
72
+ ctx: HookContext,
73
+ level: LogLevel,
74
+ message: string,
75
+ attributes: Attrs = {},
76
+ baseEvent: HandleMessageStreamEvent = event,
77
+ ): void {
78
+ const at = event.meta?.at;
79
+ log(message, {
80
+ level,
81
+ eventName: event.type,
82
+ timestamp: at ? new Date(at) : undefined,
83
+ attributes: { ...baseAttributes(baseEvent, ctx), ...attributes },
84
+ });
85
+ }
86
+
87
+ function stepCompletedMessage(data: Record<string, unknown>): string {
88
+ const finishReason = stringField(data, "finishReason") ?? "unknown";
89
+ const usage = asRecord(data.usage);
90
+ const inputTokens = numberField(usage, "inputTokens");
91
+ const outputTokens = numberField(usage, "outputTokens");
92
+ const suffix =
93
+ inputTokens !== undefined && outputTokens !== undefined
94
+ ? `: ${inputTokens} in / ${outputTokens} out tokens`
95
+ : "";
96
+ return `Step completed (${finishReason})${suffix}`;
97
+ }
98
+
99
+ function logEvent(event: HandleMessageStreamEvent, ctx: HookContext): void {
100
+ const data = eventData(event);
101
+
102
+ switch (event.type) {
103
+ case "session.started": {
104
+ const runtime = asRecord(data.runtime);
105
+ const invocation = asRecord(data.invocation);
106
+ emit(event, ctx, "info", "Session started", {
107
+ "eve.version": stringField(runtime, "eveVersion"),
108
+ "gen_ai.request.model": stringField(runtime, "modelId"),
109
+ "eve.agent.id": stringField(runtime, "agentId"),
110
+ "eve.parent.session_id": stringField(invocation, "parentSessionId"),
111
+ "eve.parent.call_id": stringField(invocation, "parentCallId"),
112
+ "eve.parent.turn_id": stringField(invocation, "parentTurnId"),
113
+ "eve.subagent.name": stringField(invocation, "name"),
114
+ });
115
+ return;
116
+ }
117
+ case "turn.started":
118
+ emit(event, ctx, "debug", "Turn started");
119
+ return;
120
+ case "message.received":
121
+ emit(event, ctx, "debug", "User message received");
122
+ return;
123
+ case "step.completed": {
124
+ const usage = asRecord(data.usage);
125
+ emit(event, ctx, "info", stepCompletedMessage(data), {
126
+ "gen_ai.usage.input_tokens": numberField(usage, "inputTokens"),
127
+ "gen_ai.usage.output_tokens": numberField(usage, "outputTokens"),
128
+ "eve.usage.cache_read_tokens": numberField(usage, "cacheReadTokens"),
129
+ "eve.usage.cache_write_tokens": numberField(usage, "cacheWriteTokens"),
130
+ });
131
+ return;
132
+ }
133
+ case "step.failed":
134
+ emit(event, ctx, "error", `Step failed: ${stringField(data, "message") ?? ""}`, {
135
+ "error.code": stringField(data, "code"),
136
+ "eve.error.details": jsonField(data, "details"),
137
+ });
138
+ return;
139
+ case "action.result": {
140
+ const status = stringField(data, "status");
141
+ if (status === "completed") return;
142
+ const error = asRecord(data.error);
143
+ emit(
144
+ event,
145
+ ctx,
146
+ status === "failed" ? "error" : "warn",
147
+ `Tool ${status ?? "failed"}: ${stringField(error, "message") ?? ""}`,
148
+ {
149
+ "error.code": stringField(error, "code"),
150
+ ...toolAttrs(data.result),
151
+ },
152
+ );
153
+ return;
154
+ }
155
+ case "input.requested": {
156
+ const requests = Array.isArray(data.requests) ? data.requests : undefined;
157
+ emit(event, ctx, "info", "Input requested (HITL)", {
158
+ "eve.input.request_count": requests?.length,
159
+ });
160
+ return;
161
+ }
162
+ case "authorization.required":
163
+ emit(event, ctx, "warn", `Authorization required: ${stringField(data, "name") ?? ""}`, {
164
+ "eve.authorization.name": stringField(data, "name"),
165
+ });
166
+ return;
167
+ case "authorization.completed": {
168
+ const outcome = stringField(data, "outcome");
169
+ emit(
170
+ event,
171
+ ctx,
172
+ outcome === "authorized" ? "info" : "warn",
173
+ `Authorization ${outcome ?? "completed"}: ${stringField(data, "name") ?? ""}`,
174
+ {
175
+ "eve.authorization.name": stringField(data, "name"),
176
+ "eve.authorization.outcome": outcome,
177
+ "eve.authorization.reason": stringField(data, "reason"),
178
+ },
179
+ );
180
+ return;
181
+ }
182
+ case "subagent.started":
183
+ emit(event, ctx, "info", "Subagent started", {
184
+ "eve.subagent.name": stringField(data, "subagentName"),
185
+ "gen_ai.tool.call.id": stringField(data, "callId"),
186
+ });
187
+ return;
188
+ case "subagent.event": {
189
+ const child = asRecord(data.event);
190
+ const childData = asRecord(child?.data);
191
+ const childType = stringField(child, "type");
192
+ const attrs = {
193
+ "eve.subagent.name": stringField(data, "subagentName"),
194
+ "gen_ai.tool.call.id": stringField(data, "callId"),
195
+ };
196
+ if (childType === "step.failed") {
197
+ emit(
198
+ event,
199
+ ctx,
200
+ "error",
201
+ `Subagent step failed: ${stringField(childData, "message") ?? ""}`,
202
+ {
203
+ ...attrs,
204
+ "error.code": stringField(childData, "code"),
205
+ "eve.error.details": jsonField(childData, "details"),
206
+ },
207
+ child as unknown as HandleMessageStreamEvent,
208
+ );
209
+ return;
210
+ }
211
+ if (childType === "turn.failed") {
212
+ emit(
213
+ event,
214
+ ctx,
215
+ "error",
216
+ `Subagent turn failed: ${stringField(childData, "message") ?? ""}`,
217
+ {
218
+ ...attrs,
219
+ "error.code": stringField(childData, "code"),
220
+ "eve.error.details": jsonField(childData, "details"),
221
+ },
222
+ child as unknown as HandleMessageStreamEvent,
223
+ );
224
+ return;
225
+ }
226
+ if (childType === "session.failed") {
227
+ emit(
228
+ event,
229
+ ctx,
230
+ "error",
231
+ `Subagent session failed: ${stringField(childData, "message") ?? ""}`,
232
+ {
233
+ ...attrs,
234
+ "error.code": stringField(childData, "code"),
235
+ "eve.error.details": jsonField(childData, "details"),
236
+ },
237
+ child as unknown as HandleMessageStreamEvent,
238
+ );
239
+ }
240
+ return;
241
+ }
242
+ case "subagent.called": {
243
+ const remote = asRecord(data.remote);
244
+ emit(event, ctx, "info", `Subagent called: ${stringField(data, "name") ?? ""}`, {
245
+ "eve.subagent.name": stringField(data, "name"),
246
+ "gen_ai.tool.name": stringField(data, "toolName"),
247
+ "gen_ai.tool.call.id": stringField(data, "callId"),
248
+ "eve.child.session_id": stringField(data, "childSessionId"),
249
+ "eve.workflow.id": stringField(data, "workflowId"),
250
+ "eve.remote.url": stringField(remote, "url"),
251
+ });
252
+ return;
253
+ }
254
+ case "subagent.completed":
255
+ emit(event, ctx, "info", "Subagent completed", {
256
+ "eve.subagent.name": stringField(data, "subagentName"),
257
+ "gen_ai.tool.call.id": stringField(data, "callId"),
258
+ });
259
+ return;
260
+ case "compaction.requested":
261
+ emit(event, ctx, "info", `Compaction requested (${stringField(data, "modelId") ?? ""})`, {
262
+ "gen_ai.request.model": stringField(data, "modelId"),
263
+ "gen_ai.usage.input_tokens": numberField(data, "usageInputTokens"),
264
+ });
265
+ return;
266
+ case "compaction.completed":
267
+ emit(event, ctx, "debug", "Compaction completed", {
268
+ "gen_ai.request.model": stringField(data, "modelId"),
269
+ });
270
+ return;
271
+ case "turn.completed":
272
+ emit(event, ctx, "info", "Turn completed");
273
+ return;
274
+ case "turn.failed":
275
+ emit(event, ctx, "error", `Turn failed: ${stringField(data, "message") ?? ""}`, {
276
+ "error.code": stringField(data, "code"),
277
+ "eve.error.details": jsonField(data, "details"),
278
+ });
279
+ return;
280
+ case "session.waiting":
281
+ emit(event, ctx, "debug", "Session waiting");
282
+ return;
283
+ case "session.completed":
284
+ emit(event, ctx, "info", "Session completed");
285
+ return;
286
+ case "session.failed":
287
+ emit(event, ctx, "error", `Session failed: ${stringField(data, "message") ?? ""}`, {
288
+ "error.code": stringField(data, "code"),
289
+ "eve.error.details": jsonField(data, "details"),
290
+ });
291
+ return;
292
+ default:
293
+ return;
294
+ }
295
+ }
296
+
297
+ export function telemetryDevHook(
298
+ options: TelemetryDevEveOptions = {},
299
+ overrides?: ClientOverrides,
300
+ ): HookDefinition {
301
+ return {
302
+ events: {
303
+ "*"(event, ctx) {
304
+ try {
305
+ ensureInit(options, overrides);
306
+ logEvent(event, ctx);
307
+ } catch (error) {
308
+ reportError(options.onError, error);
309
+ }
310
+ },
311
+ },
312
+ };
313
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export { wrapEveClient, type WrapEveClientOptions } from "./client.ts";
2
+ export type { TelemetryDevEveOptions } from "./config.ts";
3
+ export { telemetryDevHook } from "./hook.ts";
4
+ export {
5
+ telemetryDevInstrumentation,
6
+ type TelemetryDevInstrumentationOptions,
7
+ } from "./instrumentation.ts";