braintrust 3.21.0 → 3.22.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.
@@ -2272,19 +2272,30 @@ var CallEvent = z6.union([
2272
2272
  var ChatCompletionContentPartTextWithTitle = z6.object({
2273
2273
  text: z6.string().default(""),
2274
2274
  type: z6.literal("text"),
2275
- cache_control: z6.object({ type: z6.literal("ephemeral") }).optional()
2275
+ cache_control: z6.object({
2276
+ type: z6.literal("ephemeral"),
2277
+ ttl: z6.enum(["5m", "1h"]).optional()
2278
+ }).optional()
2276
2279
  });
2277
2280
  var ChatCompletionContentPartImageWithTitle = z6.object({
2278
2281
  image_url: z6.object({
2279
2282
  url: z6.string(),
2280
2283
  detail: z6.union([z6.literal("auto"), z6.literal("low"), z6.literal("high")]).optional()
2281
2284
  }),
2282
- type: z6.literal("image_url")
2285
+ type: z6.literal("image_url"),
2286
+ cache_control: z6.object({
2287
+ type: z6.literal("ephemeral"),
2288
+ ttl: z6.enum(["5m", "1h"]).optional()
2289
+ }).optional()
2283
2290
  });
2284
2291
  var ChatCompletionContentPartFileFile = z6.object({ file_data: z6.string(), filename: z6.string(), file_id: z6.string() }).partial();
2285
2292
  var ChatCompletionContentPartFileWithTitle = z6.object({
2286
2293
  file: ChatCompletionContentPartFileFile,
2287
- type: z6.literal("file")
2294
+ type: z6.literal("file"),
2295
+ cache_control: z6.object({
2296
+ type: z6.literal("ephemeral"),
2297
+ ttl: z6.enum(["5m", "1h"]).optional()
2298
+ }).optional()
2288
2299
  });
2289
2300
  var ChatCompletionContentPart = z6.union([
2290
2301
  ChatCompletionContentPartTextWithTitle,
@@ -2294,7 +2305,10 @@ var ChatCompletionContentPart = z6.union([
2294
2305
  var ChatCompletionContentPartText = z6.object({
2295
2306
  text: z6.string().default(""),
2296
2307
  type: z6.literal("text"),
2297
- cache_control: z6.object({ type: z6.literal("ephemeral") }).optional()
2308
+ cache_control: z6.object({
2309
+ type: z6.literal("ephemeral"),
2310
+ ttl: z6.enum(["5m", "1h"]).optional()
2311
+ }).optional()
2298
2312
  });
2299
2313
  var ChatCompletionMessageToolCall = z6.object({
2300
2314
  id: z6.string(),
@@ -4319,13 +4333,23 @@ var LRUCache = class {
4319
4333
  * @returns The cached value if found, undefined otherwise.
4320
4334
  */
4321
4335
  get(key) {
4322
- const value = this.cache.get(key);
4323
- if (value === void 0) {
4336
+ const entry = this.cache.get(key);
4337
+ if (entry === void 0) {
4324
4338
  return void 0;
4325
4339
  }
4326
4340
  this.cache.delete(key);
4327
- this.cache.set(key, value);
4328
- return value;
4341
+ this.cache.set(key, entry);
4342
+ return entry.value;
4343
+ }
4344
+ /**
4345
+ * Checks whether a key exists and marks it as most recently used.
4346
+ */
4347
+ has(key) {
4348
+ if (!this.cache.has(key)) {
4349
+ return false;
4350
+ }
4351
+ this.get(key);
4352
+ return true;
4329
4353
  }
4330
4354
  /**
4331
4355
  * Stores a value in the cache.
@@ -4342,7 +4366,38 @@ var LRUCache = class {
4342
4366
  const first = this.cache.keys().next().value;
4343
4367
  this.cache.delete(first);
4344
4368
  }
4345
- this.cache.set(key, value);
4369
+ this.cache.set(key, { value });
4370
+ }
4371
+ /**
4372
+ * Removes an item from the cache.
4373
+ */
4374
+ delete(key) {
4375
+ return this.cache.delete(key);
4376
+ }
4377
+ /**
4378
+ * Iterates over cache entries from least to most recently used.
4379
+ */
4380
+ *entries() {
4381
+ for (const [key, entry] of this.cache) {
4382
+ yield [key, entry.value];
4383
+ }
4384
+ }
4385
+ /**
4386
+ * Iterates over cache keys from least to most recently used.
4387
+ */
4388
+ keys() {
4389
+ return this.cache.keys();
4390
+ }
4391
+ /**
4392
+ * Iterates over cache values from least to most recently used.
4393
+ */
4394
+ *values() {
4395
+ for (const entry of this.cache.values()) {
4396
+ yield entry.value;
4397
+ }
4398
+ }
4399
+ [Symbol.iterator]() {
4400
+ return this.entries();
4346
4401
  }
4347
4402
  /**
4348
4403
  * Removes all items from the cache.
@@ -4900,6 +4955,9 @@ function applyMaskingToField(maskingFunction, data, fieldName) {
4900
4955
  return `ERROR: Failed to mask field '${fieldName}' - ${errorType}`;
4901
4956
  }
4902
4957
  }
4958
+ var INITIAL_SPAN_WRITE_AS_MERGE = /* @__PURE__ */ Symbol(
4959
+ "braintrust.initial-span-write-as-merge"
4960
+ );
4903
4961
  var BRAINTRUST_CURRENT_SPAN_STORE = /* @__PURE__ */ Symbol.for(
4904
4962
  "braintrust.currentSpanStore"
4905
4963
  );
@@ -5898,6 +5956,28 @@ function updateSpanImpl({
5898
5956
  }));
5899
5957
  state.bgLogger().log([record]);
5900
5958
  }
5959
+ function updateSpan({
5960
+ exported,
5961
+ state,
5962
+ ...event
5963
+ }) {
5964
+ const resolvedState = state ?? _globalState;
5965
+ const components = SpanComponentsV4.fromStr(exported);
5966
+ if (!components.data.row_id) {
5967
+ throw new Error("Exported span must have a row id");
5968
+ }
5969
+ updateSpanImpl({
5970
+ state: resolvedState,
5971
+ parentObjectType: components.data.object_type,
5972
+ parentObjectId: new LazyValue(
5973
+ spanComponentsToObjectIdLambda(resolvedState, components)
5974
+ ),
5975
+ id: components.data.row_id,
5976
+ root_span_id: components.data.root_span_id,
5977
+ span_id: components.data.span_id,
5978
+ event
5979
+ });
5980
+ }
5901
5981
  function spanComponentsToObjectIdLambda(state, components) {
5902
5982
  if (components.data.object_id) {
5903
5983
  const ret = components.data.object_id;
@@ -7340,6 +7420,12 @@ ${stackTrace}` });
7340
7420
  function startSpan(args) {
7341
7421
  return startSpanAndIsLogger(args).span;
7342
7422
  }
7423
+ function _internalStartSpanWithInitialMerge(args) {
7424
+ return startSpanAndIsLogger({
7425
+ ...args,
7426
+ [INITIAL_SPAN_WRITE_AS_MERGE]: true
7427
+ }).span;
7428
+ }
7343
7429
  async function flush(options) {
7344
7430
  const state = options?.state ?? _globalState;
7345
7431
  return await state.bgLogger().flush();
@@ -8144,7 +8230,7 @@ var SpanImpl = class _SpanImpl {
8144
8230
  this._spanId = resolvedIds.spanId;
8145
8231
  this._rootSpanId = resolvedIds.rootSpanId;
8146
8232
  this._spanParents = resolvedIds.spanParents;
8147
- this.isMerge = false;
8233
+ this.isMerge = args[INITIAL_SPAN_WRITE_AS_MERGE] === true;
8148
8234
  this.logInternal({ event, internalData });
8149
8235
  this.isMerge = true;
8150
8236
  }
@@ -28937,6 +29023,1469 @@ var BraintrustPlugin = class extends BasePlugin {
28937
29023
  }
28938
29024
  };
28939
29025
 
29026
+ // src/instrumentation/plugins/eve-plugin.ts
29027
+ var EVE_TRACE_STATE_KEY = "braintrust.eve.tracing";
29028
+ var MAX_EVE_CACHE_ENTRIES = 1e4;
29029
+ var MAX_STORED_LLM_INPUTS = 100;
29030
+ var MAX_STORED_SPAN_REFERENCES = 1e4;
29031
+ var MAX_STORED_STEP_STARTS = 1e4;
29032
+ function braintrustEveHook(options) {
29033
+ const state = options.defineState(EVE_TRACE_STATE_KEY, emptyEveTraceState);
29034
+ const bridge = new EveBridge(state);
29035
+ return {
29036
+ events: {
29037
+ "*": async (event, ctx) => {
29038
+ await bridge.handle(event, ctx, options.metadata);
29039
+ }
29040
+ }
29041
+ };
29042
+ }
29043
+ function braintrustEveInstrumentation(options) {
29044
+ const state = options.defineState(EVE_TRACE_STATE_KEY, emptyEveTraceState);
29045
+ return {
29046
+ events: {
29047
+ "step.started": (input) => {
29048
+ try {
29049
+ captureEveModelInput(state, input);
29050
+ } catch (error) {
29051
+ debugLogger.warn("Error in Eve LLM input capture:", error);
29052
+ }
29053
+ }
29054
+ },
29055
+ recordInputs: false,
29056
+ recordOutputs: false,
29057
+ setup: options.setup
29058
+ };
29059
+ }
29060
+ function isEveHandleMessageStreamEvent(event) {
29061
+ return isObject(event) && typeof event["type"] === "string";
29062
+ }
29063
+ var ResumedEveSpan = class {
29064
+ constructor(reference) {
29065
+ this.reference = reference;
29066
+ this.endTime = reference.endTime;
29067
+ }
29068
+ endTime;
29069
+ get rootSpanId() {
29070
+ return this.reference.rootSpanId;
29071
+ }
29072
+ get spanId() {
29073
+ return this.reference.spanId;
29074
+ }
29075
+ log(event) {
29076
+ const metrics = {
29077
+ ...this.reference.startEvent?.metrics,
29078
+ ...this.endTime === void 0 ? {} : { end: this.endTime },
29079
+ ...event.metrics
29080
+ };
29081
+ updateSpan({
29082
+ exported: this.reference.exported,
29083
+ ...this.reference.startEvent,
29084
+ ...event,
29085
+ ...Object.keys(metrics).length > 0 ? { metrics } : {}
29086
+ });
29087
+ }
29088
+ end(args) {
29089
+ if (this.endTime === void 0) {
29090
+ this.endTime = args?.endTime ?? getCurrentUnixTimestamp();
29091
+ this.log({ metrics: { end: this.endTime } });
29092
+ }
29093
+ return this.endTime;
29094
+ }
29095
+ };
29096
+ var EveBridge = class {
29097
+ constructor(state) {
29098
+ this.state = state;
29099
+ }
29100
+ eventQueuesBySession = /* @__PURE__ */ new Map();
29101
+ sessionsById = new LRUCache({
29102
+ max: MAX_EVE_CACHE_ENTRIES
29103
+ });
29104
+ completedToolKeys = new LRUCache({
29105
+ max: MAX_EVE_CACHE_ENTRIES
29106
+ });
29107
+ toolsByCallKey = new LRUCache({
29108
+ max: MAX_EVE_CACHE_ENTRIES
29109
+ });
29110
+ turnsByKey = new LRUCache({
29111
+ max: MAX_EVE_CACHE_ENTRIES
29112
+ });
29113
+ async startEveSpan(args) {
29114
+ const rowId = args?.event?.id;
29115
+ const reference = typeof rowId === "string" && readEveTraceState(this.state).spanReferences.find(
29116
+ (candidate) => candidate.rowId === rowId
29117
+ );
29118
+ if (reference) {
29119
+ return new ResumedEveSpan(reference);
29120
+ }
29121
+ const startTime = args?.startTime ?? getCurrentUnixTimestamp();
29122
+ const parentSpanIds = args?.parentSpanIds;
29123
+ const startEvent = {
29124
+ created: (/* @__PURE__ */ new Date()).toISOString(),
29125
+ metrics: { start: startTime },
29126
+ span_attributes: {
29127
+ ...args?.name ? { name: args.name } : {},
29128
+ ...args?.type ? { type: args.type } : {},
29129
+ ...args?.spanAttributes
29130
+ },
29131
+ span_parents: parentSpanIds ? "spanId" in parentSpanIds ? [parentSpanIds.spanId] : parentSpanIds.parentSpanIds : []
29132
+ };
29133
+ const span = withCurrent(
29134
+ NOOP_SPAN,
29135
+ () => _internalStartSpanWithInitialMerge({ ...args, startTime })
29136
+ );
29137
+ if (typeof rowId !== "string") {
29138
+ return span;
29139
+ }
29140
+ try {
29141
+ const exported = await span.export();
29142
+ const reference2 = {
29143
+ exported,
29144
+ rootSpanId: span.rootSpanId,
29145
+ rowId,
29146
+ spanId: span.spanId,
29147
+ startEvent
29148
+ };
29149
+ this.state.update((current) => {
29150
+ const normalized = normalizeEveTraceState(current);
29151
+ return normalized.spanReferences.some(
29152
+ (candidate) => candidate.rowId === rowId
29153
+ ) ? normalized : {
29154
+ ...normalized,
29155
+ spanReferences: [...normalized.spanReferences, reference2].slice(
29156
+ -MAX_STORED_SPAN_REFERENCES
29157
+ )
29158
+ };
29159
+ });
29160
+ } catch (error) {
29161
+ debugLogger.warn("Error exporting Eve span for resumption:", error);
29162
+ }
29163
+ return span;
29164
+ }
29165
+ async startEveChildSpan(parent, args) {
29166
+ return await this.startEveSpan({
29167
+ ...args,
29168
+ parentSpanIds: {
29169
+ rootSpanId: parent.rootSpanId,
29170
+ spanId: parent.spanId
29171
+ }
29172
+ });
29173
+ }
29174
+ stepOrdinal(event) {
29175
+ let ordinal = 0;
29176
+ this.state.update((current) => {
29177
+ const state = normalizeEveTraceState(current);
29178
+ const previous = state.stepStarts.filter(
29179
+ (entry) => entry.turnId === event.data.turnId && entry.stepIndex === event.data.stepIndex
29180
+ ).at(-1);
29181
+ if (previous?.open) {
29182
+ ordinal = previous.ordinal;
29183
+ return state;
29184
+ }
29185
+ ordinal = state.stepStarts.filter(
29186
+ (entry) => entry.turnId === event.data.turnId
29187
+ ).length;
29188
+ return {
29189
+ ...state,
29190
+ stepStarts: [
29191
+ ...state.stepStarts,
29192
+ {
29193
+ open: true,
29194
+ ordinal,
29195
+ stepIndex: event.data.stepIndex,
29196
+ turnId: event.data.turnId
29197
+ }
29198
+ ].slice(-MAX_STORED_STEP_STARTS)
29199
+ };
29200
+ });
29201
+ return ordinal;
29202
+ }
29203
+ markStepEnded(turnId, stepIndex) {
29204
+ this.state.update((current) => {
29205
+ const state = normalizeEveTraceState(current);
29206
+ let index = -1;
29207
+ for (let i = state.stepStarts.length - 1; i >= 0; i--) {
29208
+ const entry = state.stepStarts[i];
29209
+ if (entry?.turnId === turnId && entry.stepIndex === stepIndex) {
29210
+ index = i;
29211
+ break;
29212
+ }
29213
+ }
29214
+ if (index < 0 || !state.stepStarts[index]?.open) {
29215
+ return state;
29216
+ }
29217
+ return {
29218
+ ...state,
29219
+ stepStarts: state.stepStarts.map(
29220
+ (entry, entryIndex) => entryIndex === index ? { ...entry, open: false } : entry
29221
+ )
29222
+ };
29223
+ });
29224
+ }
29225
+ async handle(event, ctx, hookMetadata) {
29226
+ if (!isEveHandleMessageStreamEvent(event)) {
29227
+ return;
29228
+ }
29229
+ const run = async () => {
29230
+ try {
29231
+ if (!await this.handleEvent(event, ctx, hookMetadata)) {
29232
+ return;
29233
+ }
29234
+ if (event.type === "session.failed") {
29235
+ const sessionId2 = event.data.sessionId || sessionIdFromContext(ctx);
29236
+ await this.flushInstrumentation();
29237
+ if (sessionId2) {
29238
+ this.cleanupSession(sessionId2);
29239
+ }
29240
+ } else if (event.type === "session.completed") {
29241
+ const sessionId2 = sessionIdFromContext(ctx);
29242
+ await this.flushInstrumentation();
29243
+ if (sessionId2) {
29244
+ this.cleanupSession(sessionId2);
29245
+ }
29246
+ }
29247
+ } catch (error) {
29248
+ debugLogger.warn("Error in Eve hook instrumentation:", error);
29249
+ }
29250
+ };
29251
+ const sessionId = event.type === "session.failed" ? event.data.sessionId || sessionIdFromContext(ctx) : sessionIdFromContext(ctx);
29252
+ if (!sessionId) {
29253
+ await run();
29254
+ return;
29255
+ }
29256
+ const previous = this.eventQueuesBySession.get(sessionId);
29257
+ const queued = previous ? previous.then(run) : run();
29258
+ this.eventQueuesBySession.set(sessionId, queued);
29259
+ try {
29260
+ await queued;
29261
+ } finally {
29262
+ if (this.eventQueuesBySession.get(sessionId) === queued) {
29263
+ this.eventQueuesBySession.delete(sessionId);
29264
+ }
29265
+ }
29266
+ }
29267
+ async handleEvent(event, ctx, hookMetadata) {
29268
+ switch (event.type) {
29269
+ case "session.started":
29270
+ await this.handleSessionStarted(event, ctx, hookMetadata);
29271
+ return true;
29272
+ case "turn.started":
29273
+ await this.handleTurnStarted(event, ctx, hookMetadata);
29274
+ return true;
29275
+ case "message.received":
29276
+ await this.handleMessageReceived(event, ctx, hookMetadata);
29277
+ return true;
29278
+ case "step.started":
29279
+ await this.handleStepStarted(event, ctx, hookMetadata);
29280
+ return true;
29281
+ case "message.completed":
29282
+ this.handleMessageCompleted(event, ctx);
29283
+ return true;
29284
+ case "result.completed":
29285
+ this.handleResultCompleted(event, ctx);
29286
+ return true;
29287
+ case "actions.requested":
29288
+ await this.handleActionsRequested(event, ctx, hookMetadata);
29289
+ return true;
29290
+ case "action.result":
29291
+ await this.handleActionResult(event, ctx, hookMetadata);
29292
+ return true;
29293
+ case "subagent.called":
29294
+ await this.handleSubagentCalled(event, ctx, hookMetadata);
29295
+ return true;
29296
+ case "subagent.completed":
29297
+ await this.handleSubagentCompleted(event, ctx, hookMetadata);
29298
+ return true;
29299
+ case "step.completed":
29300
+ this.handleStepCompleted(event, ctx);
29301
+ return true;
29302
+ case "step.failed":
29303
+ this.handleStepFailed(event, ctx);
29304
+ return true;
29305
+ case "turn.completed":
29306
+ await this.handleTurnCompleted(event, ctx);
29307
+ return true;
29308
+ case "turn.failed":
29309
+ await this.handleTurnFailed(event, ctx);
29310
+ return true;
29311
+ case "session.failed":
29312
+ await this.handleSessionFailed(event, ctx);
29313
+ return true;
29314
+ case "session.completed":
29315
+ await this.handleSessionCompleted(event, ctx);
29316
+ return true;
29317
+ default:
29318
+ return false;
29319
+ }
29320
+ }
29321
+ async handleSessionStarted(event, ctx, hookMetadata) {
29322
+ const sessionId = sessionIdFromContext(ctx);
29323
+ if (!sessionId) {
29324
+ return;
29325
+ }
29326
+ const metadata = {
29327
+ ...hookMetadata ?? {},
29328
+ ...modelMetadataFromRuntime(event.data.runtime)
29329
+ };
29330
+ this.state.update((current) => {
29331
+ const normalized = normalizeEveTraceState(current);
29332
+ return {
29333
+ ...normalized,
29334
+ metadata: { ...normalized.metadata, ...metadata }
29335
+ };
29336
+ });
29337
+ await this.ensureSession(sessionId, ctx, metadata, eventTime2(event));
29338
+ for (const [key, turn] of this.turnsByKey) {
29339
+ if (!key.startsWith(`${sessionId}:`)) {
29340
+ continue;
29341
+ }
29342
+ turn.metadata = { ...turn.metadata, ...metadata };
29343
+ turn.span.log({ metadata: turn.metadata });
29344
+ for (const step of turn.stepsByIndex.values()) {
29345
+ step.metadata = { ...step.metadata, ...metadata };
29346
+ step.span.log({ metadata: step.metadata });
29347
+ }
29348
+ }
29349
+ }
29350
+ async handleTurnStarted(event, ctx, hookMetadata) {
29351
+ const sessionId = sessionIdFromContext(ctx);
29352
+ if (!sessionId) {
29353
+ return;
29354
+ }
29355
+ const session = await this.ensureSession(
29356
+ sessionId,
29357
+ ctx,
29358
+ hookMetadata ?? {},
29359
+ eventTime2(event)
29360
+ );
29361
+ const key = turnKey2(sessionId, event.data.turnId);
29362
+ const metadata = { ...session.metadata };
29363
+ const existing = this.turnsByKey.get(key);
29364
+ if (existing) {
29365
+ existing.metadata = { ...existing.metadata, ...metadata };
29366
+ existing.span.log({ metadata: existing.metadata });
29367
+ return;
29368
+ }
29369
+ const span = await this.startTurnSpan(session, event, metadata);
29370
+ span.log({ metadata });
29371
+ this.turnsByKey.set(key, {
29372
+ key,
29373
+ metadata,
29374
+ metrics: {},
29375
+ span,
29376
+ stepsByIndex: /* @__PURE__ */ new Map(),
29377
+ turnId: event.data.turnId
29378
+ });
29379
+ }
29380
+ async handleMessageReceived(event, ctx, hookMetadata) {
29381
+ const turn = await this.ensureTurn(event, ctx, hookMetadata);
29382
+ if (!turn) {
29383
+ return;
29384
+ }
29385
+ const input = [{ content: event.data.message, role: "user" }];
29386
+ turn.span.log({ input });
29387
+ }
29388
+ async handleStepStarted(event, ctx, hookMetadata) {
29389
+ const turn = await this.ensureTurn(event, ctx, hookMetadata);
29390
+ const sessionId = sessionIdFromContext(ctx);
29391
+ if (!turn || !sessionId) {
29392
+ return;
29393
+ }
29394
+ const existing = turn.stepsByIndex.get(event.data.stepIndex);
29395
+ if (existing) {
29396
+ existing.span.log({
29397
+ ...existing.input !== void 0 ? { input: existing.input } : {},
29398
+ metadata: existing.metadata,
29399
+ metrics: existing.metrics,
29400
+ output: existing.output
29401
+ });
29402
+ const endTime = eventTime2(event);
29403
+ existing.span.end(endTime === void 0 ? void 0 : { endTime });
29404
+ this.markStepEnded(event.data.turnId, event.data.stepIndex);
29405
+ }
29406
+ const stepOrdinal = this.stepOrdinal(event);
29407
+ const metadata = {
29408
+ ...turn.metadata,
29409
+ ...this.sessionsById.get(sessionId)?.metadata ?? {}
29410
+ };
29411
+ const input = consumeCapturedEveModelInput(
29412
+ this.state,
29413
+ sessionId,
29414
+ event.data.turnId,
29415
+ event.data.stepIndex
29416
+ );
29417
+ const { rowId: eventId, spanId } = await generateEveIds(
29418
+ "step",
29419
+ sessionId,
29420
+ event.data.turnId,
29421
+ String(stepOrdinal)
29422
+ );
29423
+ const span = await this.startEveChildSpan(turn.span, {
29424
+ event: {
29425
+ id: eventId,
29426
+ ...input !== void 0 ? { input } : {},
29427
+ metadata
29428
+ },
29429
+ name: "eve.step",
29430
+ spanAttributes: { type: "llm" /* LLM */ },
29431
+ spanId,
29432
+ startTime: eventTime2(event)
29433
+ });
29434
+ span.log({ ...input !== void 0 ? { input } : {}, metadata });
29435
+ turn.stepsByIndex.set(event.data.stepIndex, {
29436
+ ...input !== void 0 ? { input } : {},
29437
+ metadata,
29438
+ metrics: {},
29439
+ span
29440
+ });
29441
+ }
29442
+ handleMessageCompleted(event, ctx) {
29443
+ const step = this.stepForEvent(event, ctx);
29444
+ if (!step) {
29445
+ return;
29446
+ }
29447
+ const existingMessage = Array.isArray(step.output) && isObject(step.output[0]) ? step.output[0].message : void 0;
29448
+ const existingToolCalls = isObject(existingMessage) ? existingMessage.tool_calls : void 0;
29449
+ step.output = [
29450
+ {
29451
+ finish_reason: normalizedFinishReason(event.data.finishReason),
29452
+ index: 0,
29453
+ message: {
29454
+ content: event.data.message,
29455
+ role: "assistant",
29456
+ ...Array.isArray(existingToolCalls) ? { tool_calls: existingToolCalls } : {}
29457
+ }
29458
+ }
29459
+ ];
29460
+ const turn = this.turnForEvent(event, ctx);
29461
+ if (turn && event.data.finishReason !== "tool-calls") {
29462
+ turn.output = event.data.message;
29463
+ }
29464
+ }
29465
+ handleResultCompleted(event, ctx) {
29466
+ const step = this.stepForEvent(event, ctx);
29467
+ if (step) {
29468
+ step.output = [
29469
+ {
29470
+ finish_reason: "stop",
29471
+ index: 0,
29472
+ message: {
29473
+ content: event.data.result,
29474
+ role: "assistant"
29475
+ }
29476
+ }
29477
+ ];
29478
+ }
29479
+ const turn = this.turnForEvent(event, ctx);
29480
+ if (turn) {
29481
+ turn.output = event.data.result;
29482
+ }
29483
+ }
29484
+ async handleActionsRequested(event, ctx, hookMetadata) {
29485
+ const turn = await this.ensureTurn(event, ctx, hookMetadata);
29486
+ const sessionId = sessionIdFromContext(ctx);
29487
+ if (!turn || !sessionId) {
29488
+ return;
29489
+ }
29490
+ const traceActions = event.data.actions.filter(isTraceableActionRequest);
29491
+ if (traceActions.length === 0) {
29492
+ return;
29493
+ }
29494
+ for (const action of traceActions) {
29495
+ if (isToolCallAction(action)) {
29496
+ await this.startRequestedTool(event, turn, sessionId, action);
29497
+ } else if (isLocalSubagentCallAction(action)) {
29498
+ await this.startRequestedSubagent(event, turn, sessionId, action);
29499
+ }
29500
+ }
29501
+ const step = turn.stepsByIndex.get(event.data.stepIndex);
29502
+ if (!step) {
29503
+ return;
29504
+ }
29505
+ step.output = [
29506
+ {
29507
+ finish_reason: "tool_calls",
29508
+ index: 0,
29509
+ message: {
29510
+ content: null,
29511
+ role: "assistant",
29512
+ tool_calls: traceActions.map((action) => {
29513
+ const name = action.kind === "tool-call" ? action.toolName : action.subagentName ?? action.name ?? "agent";
29514
+ return {
29515
+ function: {
29516
+ arguments: JSON.stringify(action.input),
29517
+ name
29518
+ },
29519
+ id: action.callId,
29520
+ type: "function"
29521
+ };
29522
+ })
29523
+ }
29524
+ }
29525
+ ];
29526
+ step.span.log({ metadata: step.metadata, output: step.output });
29527
+ }
29528
+ async handleActionResult(event, ctx, hookMetadata) {
29529
+ if (isToolResult(event.data.result)) {
29530
+ await this.handleToolResult(event, ctx, event.data.result, hookMetadata);
29531
+ return;
29532
+ }
29533
+ if (isSubagentResult(event.data.result)) {
29534
+ await this.handleSubagentResult(
29535
+ event,
29536
+ ctx,
29537
+ event.data.result,
29538
+ hookMetadata
29539
+ );
29540
+ }
29541
+ }
29542
+ async handleToolResult(event, ctx, result, hookMetadata) {
29543
+ const sessionId = sessionIdFromContext(ctx);
29544
+ if (!sessionId) {
29545
+ return;
29546
+ }
29547
+ const key = toolKey3(sessionId, result.callId);
29548
+ if (this.completedToolKeys.has(key)) {
29549
+ return;
29550
+ }
29551
+ const tool = this.toolsByCallKey.get(key) ?? await this.startSyntheticTool(event, ctx, result, hookMetadata);
29552
+ if (!tool) {
29553
+ return;
29554
+ }
29555
+ const failed = event.data.status === "failed" || result.isError === true || event.data.error !== void 0;
29556
+ tool.span.log({
29557
+ ...failed ? {
29558
+ error: actionResultError(event.data.error, result.output)
29559
+ } : {},
29560
+ metadata: tool.metadata,
29561
+ output: result.output
29562
+ });
29563
+ const endTime = eventTime2(event);
29564
+ tool.span.end(endTime === void 0 ? void 0 : { endTime });
29565
+ this.toolsByCallKey.delete(key);
29566
+ this.completedToolKeys.set(key, true);
29567
+ }
29568
+ async handleSubagentCalled(event, ctx, hookMetadata) {
29569
+ if (event.data.remote?.url) {
29570
+ return;
29571
+ }
29572
+ const turn = await this.ensureTurn(event, ctx, hookMetadata);
29573
+ const sessionId = sessionIdFromContext(ctx);
29574
+ if (!turn || !sessionId) {
29575
+ return;
29576
+ }
29577
+ const key = toolKey3(sessionId, event.data.callId);
29578
+ const metadata = toolMetadataFromTurn(turn);
29579
+ const existing = this.toolsByCallKey.get(key);
29580
+ if (existing) {
29581
+ existing.metadata = { ...existing.metadata, ...metadata };
29582
+ existing.span.log({ metadata: existing.metadata });
29583
+ return;
29584
+ }
29585
+ if (this.completedToolKeys.has(key)) {
29586
+ return;
29587
+ }
29588
+ const { rowId: eventId, spanId } = await generateEveIds(
29589
+ "subagent",
29590
+ sessionId,
29591
+ event.data.callId
29592
+ );
29593
+ const pending = this.toolsByCallKey.get(key);
29594
+ if (pending || this.completedToolKeys.has(key)) {
29595
+ if (pending) {
29596
+ pending.metadata = { ...pending.metadata, ...metadata };
29597
+ pending.span.log({ metadata: pending.metadata });
29598
+ }
29599
+ return;
29600
+ }
29601
+ const span = await this.startEveChildSpan(turn.span, {
29602
+ event: {
29603
+ id: eventId,
29604
+ metadata
29605
+ },
29606
+ name: event.data.toolName ?? event.data.name,
29607
+ spanAttributes: { type: "tool" /* TOOL */ },
29608
+ spanId,
29609
+ startTime: eventTime2(event)
29610
+ });
29611
+ span.log({ metadata });
29612
+ this.toolsByCallKey.set(key, {
29613
+ metadata,
29614
+ span,
29615
+ turnKey: turnKey2(sessionId, event.data.turnId)
29616
+ });
29617
+ }
29618
+ async handleSubagentCompleted(event, ctx, hookMetadata) {
29619
+ const sessionId = sessionIdFromContext(ctx);
29620
+ if (!sessionId) {
29621
+ return;
29622
+ }
29623
+ const key = toolKey3(sessionId, event.data.callId);
29624
+ if (this.completedToolKeys.has(key)) {
29625
+ return;
29626
+ }
29627
+ const subagent = this.toolsByCallKey.get(key) ?? await this.startSyntheticSubagent(event, ctx, hookMetadata);
29628
+ if (!subagent) {
29629
+ return;
29630
+ }
29631
+ subagent.span.log({
29632
+ ...event.data.status === "failed" ? {
29633
+ error: actionResultError(event.data.error, event.data.output)
29634
+ } : {},
29635
+ metadata: subagent.metadata,
29636
+ ...event.data.output !== void 0 ? { output: event.data.output } : {}
29637
+ });
29638
+ const endTime = eventTime2(event);
29639
+ const recordedEndTime = subagent.span.end(
29640
+ endTime === void 0 ? void 0 : { endTime }
29641
+ );
29642
+ this.state.update((current) => {
29643
+ const normalized = normalizeEveTraceState(current);
29644
+ return {
29645
+ ...normalized,
29646
+ spanReferences: normalized.spanReferences.map(
29647
+ (reference) => reference.spanId === subagent.span.spanId ? { ...reference, endTime: recordedEndTime } : reference
29648
+ )
29649
+ };
29650
+ });
29651
+ }
29652
+ async handleSubagentResult(event, ctx, result, hookMetadata) {
29653
+ const sessionId = sessionIdFromContext(ctx);
29654
+ if (!sessionId) {
29655
+ return;
29656
+ }
29657
+ const key = toolKey3(sessionId, result.callId);
29658
+ if (this.completedToolKeys.has(key)) {
29659
+ return;
29660
+ }
29661
+ const subagent = this.toolsByCallKey.get(key) ?? await this.startSyntheticSubagentResult(
29662
+ event,
29663
+ ctx,
29664
+ result,
29665
+ hookMetadata
29666
+ );
29667
+ if (!subagent) {
29668
+ return;
29669
+ }
29670
+ const isError = event.data.status === "failed" || result.isError === true || event.data.error !== void 0;
29671
+ subagent.span.log({
29672
+ ...isError ? {
29673
+ error: actionResultError(event.data.error, result.output)
29674
+ } : {},
29675
+ metadata: subagent.metadata,
29676
+ output: result.output
29677
+ });
29678
+ const endTime = eventTime2(event);
29679
+ subagent.span.end(endTime === void 0 ? void 0 : { endTime });
29680
+ this.toolsByCallKey.delete(key);
29681
+ this.completedToolKeys.set(key, true);
29682
+ }
29683
+ handleStepCompleted(event, ctx) {
29684
+ const step = this.stepForEvent(event, ctx);
29685
+ if (!step) {
29686
+ return;
29687
+ }
29688
+ const usage = event.data.usage;
29689
+ const inputTokens = typeof usage?.inputTokens === "number" && Number.isFinite(usage.inputTokens) && usage.inputTokens >= 0 ? usage.inputTokens : void 0;
29690
+ const outputTokens = typeof usage?.outputTokens === "number" && Number.isFinite(usage.outputTokens) && usage.outputTokens >= 0 ? usage.outputTokens : void 0;
29691
+ const cacheReadTokens = typeof usage?.cacheReadTokens === "number" && Number.isFinite(usage.cacheReadTokens) && usage.cacheReadTokens >= 0 ? usage.cacheReadTokens : void 0;
29692
+ const cacheWriteTokens = typeof usage?.cacheWriteTokens === "number" && Number.isFinite(usage.cacheWriteTokens) && usage.cacheWriteTokens >= 0 ? usage.cacheWriteTokens : void 0;
29693
+ const costUsd = typeof usage?.costUsd === "number" && Number.isFinite(usage.costUsd) && usage.costUsd >= 0 ? usage.costUsd : void 0;
29694
+ const total = inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0;
29695
+ const metrics = {
29696
+ ...inputTokens !== void 0 ? { prompt_tokens: inputTokens } : {},
29697
+ ...outputTokens !== void 0 ? { completion_tokens: outputTokens } : {},
29698
+ ...total !== void 0 ? { tokens: total } : {},
29699
+ ...cacheReadTokens !== void 0 ? { prompt_cached_tokens: cacheReadTokens } : {},
29700
+ ...cacheWriteTokens !== void 0 ? { prompt_cache_creation_tokens: cacheWriteTokens } : {},
29701
+ ...costUsd !== void 0 ? { estimated_cost: costUsd } : {}
29702
+ };
29703
+ step.metrics = { ...step.metrics, ...metrics };
29704
+ if (Array.isArray(step.output) && isObject(step.output[0])) {
29705
+ const finishReason = step.output[0].finish_reason;
29706
+ if (typeof finishReason !== "string") {
29707
+ step.output[0].finish_reason = normalizedFinishReason(
29708
+ event.data.finishReason
29709
+ );
29710
+ }
29711
+ }
29712
+ step.span.log({
29713
+ ...step.input !== void 0 ? { input: step.input } : {},
29714
+ metadata: step.metadata,
29715
+ metrics,
29716
+ output: step.output
29717
+ });
29718
+ const endTime = eventTime2(event);
29719
+ step.span.end(endTime === void 0 ? void 0 : { endTime });
29720
+ const turn = this.turnForEvent(event, ctx);
29721
+ if (turn) {
29722
+ for (const [key, value] of Object.entries(metrics)) {
29723
+ turn.metrics[key] = (turn.metrics[key] ?? 0) + value;
29724
+ }
29725
+ turn.stepsByIndex.delete(event.data.stepIndex);
29726
+ }
29727
+ this.markStepEnded(event.data.turnId, event.data.stepIndex);
29728
+ }
29729
+ handleStepFailed(event, ctx) {
29730
+ const step = this.stepForEvent(event, ctx);
29731
+ if (step) {
29732
+ step.span.log({
29733
+ error: errorFromMessage(
29734
+ event.data.message,
29735
+ event.data.code,
29736
+ event.data.details
29737
+ )
29738
+ });
29739
+ const endTime = eventTime2(event);
29740
+ step.span.end(endTime === void 0 ? void 0 : { endTime });
29741
+ }
29742
+ const turn = this.turnForEvent(event, ctx);
29743
+ turn?.stepsByIndex.delete(event.data.stepIndex);
29744
+ this.markStepEnded(event.data.turnId, event.data.stepIndex);
29745
+ }
29746
+ handleTurnCompleted(event, ctx) {
29747
+ const turn = this.turnForEvent(event, ctx);
29748
+ if (!turn) {
29749
+ return;
29750
+ }
29751
+ this.finalizeTurn(turn, {
29752
+ endTime: eventTime2(event)
29753
+ });
29754
+ }
29755
+ handleTurnFailed(event, ctx) {
29756
+ const turn = this.turnForEvent(event, ctx);
29757
+ if (!turn) {
29758
+ return;
29759
+ }
29760
+ this.finalizeTurn(turn, {
29761
+ endTime: eventTime2(event),
29762
+ error: errorFromMessage(
29763
+ event.data.message,
29764
+ event.data.code,
29765
+ event.data.details
29766
+ )
29767
+ });
29768
+ }
29769
+ async handleSessionFailed(event, ctx) {
29770
+ const sessionId = event.data.sessionId || sessionIdFromContext(ctx);
29771
+ if (!sessionId) {
29772
+ return;
29773
+ }
29774
+ const session = await this.ensureSession(
29775
+ sessionId,
29776
+ ctx,
29777
+ {},
29778
+ eventTime2(event)
29779
+ );
29780
+ const error = errorFromMessage(
29781
+ event.data.message,
29782
+ event.data.code,
29783
+ event.data.details
29784
+ );
29785
+ for (const [key, turn] of this.turnsByKey) {
29786
+ if (!key.startsWith(`${sessionId}:`)) {
29787
+ continue;
29788
+ }
29789
+ this.finalizeTurn(turn, {
29790
+ endTime: eventTime2(event),
29791
+ error
29792
+ });
29793
+ }
29794
+ for (const [key, tool] of this.toolsByCallKey) {
29795
+ if (key.startsWith(`${sessionId}:`)) {
29796
+ const endTime2 = eventTime2(event);
29797
+ if (!tool.endedByTurn) {
29798
+ tool.span.log({ metadata: tool.metadata });
29799
+ tool.span.end(endTime2 === void 0 ? void 0 : { endTime: endTime2 });
29800
+ tool.endedByTurn = true;
29801
+ }
29802
+ }
29803
+ }
29804
+ session.span.log({ error });
29805
+ const endTime = eventTime2(event);
29806
+ session.span.end(endTime === void 0 ? void 0 : { endTime });
29807
+ }
29808
+ async handleSessionCompleted(event, ctx) {
29809
+ const sessionId = sessionIdFromContext(ctx);
29810
+ if (!sessionId) {
29811
+ return;
29812
+ }
29813
+ const session = await this.ensureSession(
29814
+ sessionId,
29815
+ ctx,
29816
+ {},
29817
+ eventTime2(event)
29818
+ );
29819
+ for (const [key, turn] of this.turnsByKey) {
29820
+ if (!key.startsWith(`${sessionId}:`)) {
29821
+ continue;
29822
+ }
29823
+ this.finalizeTurn(turn, {
29824
+ endTime: eventTime2(event)
29825
+ });
29826
+ }
29827
+ for (const [key, tool] of this.toolsByCallKey) {
29828
+ if (key.startsWith(`${sessionId}:`) && !tool.endedByTurn) {
29829
+ const endTime2 = eventTime2(event);
29830
+ tool.span.log({ metadata: tool.metadata });
29831
+ tool.span.end(endTime2 === void 0 ? void 0 : { endTime: endTime2 });
29832
+ tool.endedByTurn = true;
29833
+ }
29834
+ }
29835
+ session.span.log({ metadata: session.metadata });
29836
+ const endTime = eventTime2(event);
29837
+ session.span.end(endTime === void 0 ? void 0 : { endTime });
29838
+ }
29839
+ async ensureSession(sessionId, ctx, metadata, startTime) {
29840
+ metadata = {
29841
+ ...readEveTraceState(this.state).metadata,
29842
+ ...metadata
29843
+ };
29844
+ const existing = this.sessionsById.get(sessionId);
29845
+ if (existing) {
29846
+ existing.metadata = { ...existing.metadata, ...metadata };
29847
+ existing.span.log({ metadata: existing.metadata });
29848
+ return existing;
29849
+ }
29850
+ const lineage = parentLineageFromContext(ctx);
29851
+ const parentSubagent = lineage ? this.toolsByCallKey.get(toolKey3(lineage.sessionId, lineage.callId)) : void 0;
29852
+ const activeParent = currentSpan();
29853
+ const [
29854
+ { rowId: eventId, spanId },
29855
+ parentSubagentSpanId,
29856
+ fallbackRootSpanId
29857
+ ] = await Promise.all([
29858
+ generateEveIds("session", sessionId),
29859
+ lineage ? spanIdForSubagent(lineage.sessionId, lineage.callId) : Promise.resolve(void 0),
29860
+ lineage ? rootSpanIdForSession(lineage.rootSessionId) : rootSpanIdForSession(sessionId)
29861
+ ]);
29862
+ const span = await this.startEveSpan({
29863
+ event: {
29864
+ id: eventId,
29865
+ metadata
29866
+ },
29867
+ name: "eve.session",
29868
+ parentSpanIds: lineage && parentSubagentSpanId ? {
29869
+ rootSpanId: parentSubagent?.span.rootSpanId ?? fallbackRootSpanId,
29870
+ spanId: parentSubagentSpanId
29871
+ } : !Object.is(activeParent, NOOP_SPAN) ? {
29872
+ rootSpanId: activeParent.rootSpanId,
29873
+ spanId: activeParent.spanId
29874
+ } : {
29875
+ parentSpanIds: [],
29876
+ rootSpanId: fallbackRootSpanId
29877
+ },
29878
+ spanAttributes: { type: "task" /* TASK */ },
29879
+ spanId,
29880
+ startTime
29881
+ });
29882
+ span.log({ metadata });
29883
+ const session = { metadata, sessionId, span };
29884
+ this.sessionsById.set(sessionId, session);
29885
+ return session;
29886
+ }
29887
+ async ensureTurn(event, ctx, hookMetadata) {
29888
+ const sessionId = sessionIdFromContext(ctx);
29889
+ if (!sessionId) {
29890
+ return void 0;
29891
+ }
29892
+ const session = await this.ensureSession(
29893
+ sessionId,
29894
+ ctx,
29895
+ hookMetadata ?? {},
29896
+ eventTime2(event)
29897
+ );
29898
+ const key = turnKey2(sessionId, event.data.turnId);
29899
+ const existing = this.turnsByKey.get(key);
29900
+ if (existing) {
29901
+ return existing;
29902
+ }
29903
+ const metadata = { ...session.metadata };
29904
+ const span = await this.startTurnSpan(session, event, metadata);
29905
+ span.log({ metadata });
29906
+ const state = {
29907
+ key,
29908
+ metadata,
29909
+ metrics: {},
29910
+ span,
29911
+ stepsByIndex: /* @__PURE__ */ new Map(),
29912
+ turnId: event.data.turnId
29913
+ };
29914
+ this.turnsByKey.set(key, state);
29915
+ return state;
29916
+ }
29917
+ async startRequestedTool(event, turn, sessionId, action) {
29918
+ const key = toolKey3(sessionId, action.callId);
29919
+ if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) {
29920
+ return;
29921
+ }
29922
+ const metadata = toolMetadataFromTurn(turn);
29923
+ const { rowId: eventId, spanId } = await generateEveIds(
29924
+ "tool",
29925
+ sessionId,
29926
+ event.data.turnId,
29927
+ action.callId
29928
+ );
29929
+ if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) {
29930
+ return;
29931
+ }
29932
+ const span = await this.startEveChildSpan(turn.span, {
29933
+ event: {
29934
+ id: eventId,
29935
+ input: action.input,
29936
+ metadata
29937
+ },
29938
+ name: action.toolName,
29939
+ spanAttributes: { type: "tool" /* TOOL */ },
29940
+ spanId,
29941
+ startTime: eventTime2(event)
29942
+ });
29943
+ span.log({ input: action.input, metadata });
29944
+ this.toolsByCallKey.set(key, {
29945
+ metadata,
29946
+ span,
29947
+ turnKey: turnKey2(sessionId, event.data.turnId)
29948
+ });
29949
+ }
29950
+ async startRequestedSubagent(event, turn, sessionId, action) {
29951
+ const key = toolKey3(sessionId, action.callId);
29952
+ if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) {
29953
+ return;
29954
+ }
29955
+ const name = action.subagentName ?? action.name ?? "agent";
29956
+ const metadata = toolMetadataFromTurn(turn);
29957
+ const { rowId: eventId, spanId } = await generateEveIds(
29958
+ "subagent",
29959
+ sessionId,
29960
+ action.callId
29961
+ );
29962
+ if (this.toolsByCallKey.has(key) || this.completedToolKeys.has(key)) {
29963
+ return;
29964
+ }
29965
+ const span = await this.startEveChildSpan(turn.span, {
29966
+ event: {
29967
+ id: eventId,
29968
+ input: action.input,
29969
+ metadata
29970
+ },
29971
+ name,
29972
+ spanAttributes: { type: "tool" /* TOOL */ },
29973
+ spanId,
29974
+ startTime: eventTime2(event)
29975
+ });
29976
+ span.log({ input: action.input, metadata });
29977
+ this.toolsByCallKey.set(key, {
29978
+ metadata,
29979
+ span,
29980
+ turnKey: turnKey2(sessionId, event.data.turnId)
29981
+ });
29982
+ }
29983
+ async startSyntheticTool(event, ctx, result, hookMetadata) {
29984
+ const turn = await this.ensureTurn(event, ctx, hookMetadata);
29985
+ const sessionId = sessionIdFromContext(ctx);
29986
+ if (!turn || !sessionId) {
29987
+ return void 0;
29988
+ }
29989
+ const metadata = toolMetadataFromTurn(turn);
29990
+ const { rowId: eventId, spanId } = await generateEveIds(
29991
+ "tool",
29992
+ sessionId,
29993
+ event.data.turnId,
29994
+ result.callId
29995
+ );
29996
+ const existing = this.toolsByCallKey.get(toolKey3(sessionId, result.callId));
29997
+ if (existing) {
29998
+ return existing;
29999
+ }
30000
+ const span = await this.startEveChildSpan(turn.span, {
30001
+ event: {
30002
+ id: eventId,
30003
+ metadata
30004
+ },
30005
+ name: result.toolName,
30006
+ spanAttributes: { type: "tool" /* TOOL */ },
30007
+ spanId,
30008
+ startTime: eventTime2(event)
30009
+ });
30010
+ span.log({ metadata });
30011
+ const state = {
30012
+ metadata,
30013
+ span,
30014
+ turnKey: turnKey2(sessionId, event.data.turnId)
30015
+ };
30016
+ this.toolsByCallKey.set(toolKey3(sessionId, result.callId), state);
30017
+ return state;
30018
+ }
30019
+ async startSyntheticSubagent(event, ctx, hookMetadata) {
30020
+ const turn = await this.ensureTurn(event, ctx, hookMetadata);
30021
+ const sessionId = sessionIdFromContext(ctx);
30022
+ if (!turn || !sessionId) {
30023
+ return void 0;
30024
+ }
30025
+ const metadata = toolMetadataFromTurn(turn);
30026
+ const { rowId: eventId, spanId } = await generateEveIds(
30027
+ "subagent",
30028
+ sessionId,
30029
+ event.data.callId
30030
+ );
30031
+ const existing = this.toolsByCallKey.get(
30032
+ toolKey3(sessionId, event.data.callId)
30033
+ );
30034
+ if (existing) {
30035
+ return existing;
30036
+ }
30037
+ const span = await this.startEveChildSpan(turn.span, {
30038
+ event: {
30039
+ id: eventId,
30040
+ metadata
30041
+ },
30042
+ name: event.data.subagentName,
30043
+ spanAttributes: { type: "tool" /* TOOL */ },
30044
+ spanId,
30045
+ startTime: eventTime2(event)
30046
+ });
30047
+ span.log({ metadata });
30048
+ const state = {
30049
+ metadata,
30050
+ span,
30051
+ turnKey: turnKey2(sessionId, event.data.turnId)
30052
+ };
30053
+ this.toolsByCallKey.set(toolKey3(sessionId, event.data.callId), state);
30054
+ return state;
30055
+ }
30056
+ async startSyntheticSubagentResult(event, ctx, result, hookMetadata) {
30057
+ const turn = await this.ensureTurn(event, ctx, hookMetadata);
30058
+ const sessionId = sessionIdFromContext(ctx);
30059
+ if (!turn || !sessionId) {
30060
+ return void 0;
30061
+ }
30062
+ const metadata = toolMetadataFromTurn(turn);
30063
+ const { rowId: eventId, spanId } = await generateEveIds(
30064
+ "subagent",
30065
+ sessionId,
30066
+ result.callId
30067
+ );
30068
+ const existing = this.toolsByCallKey.get(toolKey3(sessionId, result.callId));
30069
+ if (existing) {
30070
+ return existing;
30071
+ }
30072
+ const span = await this.startEveChildSpan(turn.span, {
30073
+ event: {
30074
+ id: eventId,
30075
+ metadata
30076
+ },
30077
+ name: result.subagentName,
30078
+ spanAttributes: { type: "tool" /* TOOL */ },
30079
+ spanId,
30080
+ startTime: eventTime2(event)
30081
+ });
30082
+ span.log({ metadata });
30083
+ const state = {
30084
+ metadata,
30085
+ span,
30086
+ turnKey: turnKey2(sessionId, event.data.turnId)
30087
+ };
30088
+ this.toolsByCallKey.set(toolKey3(sessionId, result.callId), state);
30089
+ return state;
30090
+ }
30091
+ async startTurnSpan(session, event, metadata) {
30092
+ const { rowId: eventId, spanId } = await generateEveIds(
30093
+ "turn",
30094
+ session.sessionId,
30095
+ event.data.turnId
30096
+ );
30097
+ return await this.startEveSpan({
30098
+ event: {
30099
+ id: eventId,
30100
+ metadata
30101
+ },
30102
+ name: "eve.turn",
30103
+ parentSpanIds: {
30104
+ rootSpanId: session.span.rootSpanId,
30105
+ spanId: session.span.spanId
30106
+ },
30107
+ spanAttributes: { type: "task" /* TASK */ },
30108
+ spanId,
30109
+ startTime: eventTime2(event)
30110
+ });
30111
+ }
30112
+ turnForEvent(event, ctx) {
30113
+ const sessionId = sessionIdFromContext(ctx);
30114
+ return sessionId ? this.turnsByKey.get(turnKey2(sessionId, event.data.turnId)) : void 0;
30115
+ }
30116
+ stepForEvent(event, ctx) {
30117
+ return this.turnForEvent(event, ctx)?.stepsByIndex.get(
30118
+ event.data.stepIndex
30119
+ );
30120
+ }
30121
+ finalizeTurn(turn, args) {
30122
+ const { endTime } = args;
30123
+ for (const step of turn.stepsByIndex.values()) {
30124
+ step.span.log({
30125
+ ...step.input !== void 0 ? { input: step.input } : {},
30126
+ metadata: step.metadata,
30127
+ metrics: step.metrics,
30128
+ output: step.output
30129
+ });
30130
+ step.span.end(endTime === void 0 ? void 0 : { endTime });
30131
+ }
30132
+ turn.stepsByIndex.clear();
30133
+ for (const tool of this.toolsByCallKey.values()) {
30134
+ if (tool.turnKey !== turn.key) {
30135
+ continue;
30136
+ }
30137
+ if (tool.endedByTurn) {
30138
+ continue;
30139
+ }
30140
+ tool.span.log({ metadata: tool.metadata });
30141
+ tool.span.end(endTime === void 0 ? void 0 : { endTime });
30142
+ tool.endedByTurn = true;
30143
+ }
30144
+ if (args.error) {
30145
+ turn.span.log({ error: args.error });
30146
+ } else {
30147
+ turn.span.log({
30148
+ metadata: turn.metadata,
30149
+ metrics: turn.metrics,
30150
+ output: turn.output
30151
+ });
30152
+ }
30153
+ turn.span.end(endTime === void 0 ? void 0 : { endTime });
30154
+ this.turnsByKey.delete(turn.key);
30155
+ this.state.update((current) => {
30156
+ const normalized = normalizeEveTraceState(current);
30157
+ return {
30158
+ ...normalized,
30159
+ stepStarts: normalized.stepStarts.filter(
30160
+ (entry) => entry.turnId !== turn.turnId
30161
+ )
30162
+ };
30163
+ });
30164
+ }
30165
+ cleanupSession(sessionId) {
30166
+ const keyPrefix = `${sessionId}:`;
30167
+ this.sessionsById.delete(sessionId);
30168
+ for (const key of this.turnsByKey.keys()) {
30169
+ if (key.startsWith(keyPrefix)) {
30170
+ this.turnsByKey.delete(key);
30171
+ }
30172
+ }
30173
+ for (const key of this.toolsByCallKey.keys()) {
30174
+ if (key.startsWith(keyPrefix)) {
30175
+ this.toolsByCallKey.delete(key);
30176
+ }
30177
+ }
30178
+ for (const key of this.completedToolKeys.keys()) {
30179
+ if (key.startsWith(keyPrefix)) {
30180
+ this.completedToolKeys.delete(key);
30181
+ }
30182
+ }
30183
+ this.state.update(() => emptyEveTraceState());
30184
+ }
30185
+ async flushInstrumentation() {
30186
+ try {
30187
+ await flush();
30188
+ return true;
30189
+ } catch (error) {
30190
+ debugLogger.warn("Error in Eve flush instrumentation:", error);
30191
+ return false;
30192
+ }
30193
+ }
30194
+ };
30195
+ function emptyEveTraceState() {
30196
+ return {
30197
+ llmInputs: [],
30198
+ metadata: {},
30199
+ spanReferences: [],
30200
+ stepStarts: []
30201
+ };
30202
+ }
30203
+ function normalizeEveTraceState(state) {
30204
+ if (!isObject(state)) {
30205
+ return emptyEveTraceState();
30206
+ }
30207
+ const metadata = isObject(state["metadata"]) ? state["metadata"] : {};
30208
+ const spanReferences = Array.isArray(state["spanReferences"]) ? state["spanReferences"].flatMap((entry) => {
30209
+ if (!isObject(entry)) {
30210
+ return [];
30211
+ }
30212
+ const exported = entry["exported"];
30213
+ const endTime = entry["endTime"];
30214
+ const rootSpanId = entry["rootSpanId"];
30215
+ const rowId = entry["rowId"];
30216
+ const spanId = entry["spanId"];
30217
+ const startEvent = entry["startEvent"];
30218
+ const startEventCreated = isObject(startEvent) ? startEvent["created"] : void 0;
30219
+ const startEventMetrics = isObject(startEvent) ? startEvent["metrics"] : void 0;
30220
+ const startEventSpanAttributes = isObject(startEvent) ? startEvent["span_attributes"] : void 0;
30221
+ const startEventSpanParents = isObject(startEvent) ? startEvent["span_parents"] : void 0;
30222
+ const normalizedStartEvent = typeof startEventCreated === "string" && isObject(startEventMetrics) && typeof startEventMetrics["start"] === "number" && Number.isFinite(startEventMetrics["start"]) && isObject(startEventSpanAttributes) && Array.isArray(startEventSpanParents) && startEventSpanParents.every(
30223
+ (parent) => typeof parent === "string"
30224
+ ) ? {
30225
+ created: startEventCreated,
30226
+ metrics: { start: startEventMetrics["start"] },
30227
+ span_attributes: { ...startEventSpanAttributes },
30228
+ span_parents: [...startEventSpanParents]
30229
+ } : void 0;
30230
+ return typeof exported === "string" && typeof rootSpanId === "string" && typeof rowId === "string" && typeof spanId === "string" ? [
30231
+ {
30232
+ ...typeof endTime === "number" && Number.isFinite(endTime) ? { endTime } : {},
30233
+ exported,
30234
+ rootSpanId,
30235
+ rowId,
30236
+ spanId,
30237
+ ...normalizedStartEvent ? { startEvent: normalizedStartEvent } : {}
30238
+ }
30239
+ ] : [];
30240
+ }).slice(-MAX_STORED_SPAN_REFERENCES) : [];
30241
+ const llmInputs = Array.isArray(state["llmInputs"]) ? state["llmInputs"].flatMap((entry) => {
30242
+ if (!isObject(entry)) {
30243
+ return [];
30244
+ }
30245
+ const key = entry["key"];
30246
+ const input = entry["input"];
30247
+ return typeof key === "string" && isCapturedModelInput(input) ? [{ input, key }] : [];
30248
+ }).slice(-MAX_STORED_LLM_INPUTS) : [];
30249
+ const stepStarts = Array.isArray(state["stepStarts"]) ? state["stepStarts"].flatMap((entry) => {
30250
+ if (!isObject(entry)) {
30251
+ return [];
30252
+ }
30253
+ const ordinal = entry["ordinal"];
30254
+ const open = entry["open"];
30255
+ const stepIndex = entry["stepIndex"];
30256
+ const turnId = entry["turnId"];
30257
+ return typeof ordinal === "number" && Number.isInteger(ordinal) && ordinal >= 0 && typeof open === "boolean" && typeof stepIndex === "number" && Number.isInteger(stepIndex) && typeof turnId === "string" ? [{ open, ordinal, stepIndex, turnId }] : [];
30258
+ }).slice(-MAX_STORED_STEP_STARTS) : [];
30259
+ return { llmInputs, metadata: { ...metadata }, spanReferences, stepStarts };
30260
+ }
30261
+ function readEveTraceState(state) {
30262
+ try {
30263
+ return normalizeEveTraceState(state.get());
30264
+ } catch {
30265
+ return emptyEveTraceState();
30266
+ }
30267
+ }
30268
+ function captureEveModelInput(state, input) {
30269
+ if (!isObject(input)) {
30270
+ return;
30271
+ }
30272
+ const session = input["session"];
30273
+ const turn = input["turn"];
30274
+ const step = input["step"];
30275
+ if (!isObject(session) || !isObject(turn) || !isObject(step)) {
30276
+ return;
30277
+ }
30278
+ const sessionId = session["id"];
30279
+ const turnId = turn["id"];
30280
+ const stepIndex = step["index"];
30281
+ if (typeof sessionId !== "string" || typeof turnId !== "string" || typeof stepIndex !== "number" || !Number.isInteger(stepIndex)) {
30282
+ return;
30283
+ }
30284
+ const captured = capturedModelInput(input["modelInput"]);
30285
+ if (!captured) {
30286
+ return;
30287
+ }
30288
+ const key = llmInputKey(sessionId, turnId, stepIndex);
30289
+ state.update((current) => {
30290
+ const normalized = normalizeEveTraceState(current);
30291
+ const llmInputs = [...normalized.llmInputs, { input: captured, key }];
30292
+ return {
30293
+ ...normalized,
30294
+ llmInputs: llmInputs.slice(-MAX_STORED_LLM_INPUTS)
30295
+ };
30296
+ });
30297
+ }
30298
+ function consumeCapturedEveModelInput(state, sessionId, turnId, stepIndex) {
30299
+ try {
30300
+ const key = llmInputKey(sessionId, turnId, stepIndex);
30301
+ let input;
30302
+ state.update((current) => {
30303
+ const normalized = normalizeEveTraceState(current);
30304
+ const index = normalized.llmInputs.findIndex(
30305
+ (candidate) => candidate.key === key
30306
+ );
30307
+ if (index < 0) {
30308
+ return normalized;
30309
+ }
30310
+ input = normalized.llmInputs[index]?.input;
30311
+ return {
30312
+ ...normalized,
30313
+ llmInputs: normalized.llmInputs.filter(
30314
+ (_, candidateIndex) => candidateIndex !== index
30315
+ )
30316
+ };
30317
+ });
30318
+ return input;
30319
+ } catch (error) {
30320
+ debugLogger.warn("Error in Eve LLM input consumption:", error);
30321
+ return void 0;
30322
+ }
30323
+ }
30324
+ function capturedModelInput(modelInput) {
30325
+ if (!isObject(modelInput)) {
30326
+ return void 0;
30327
+ }
30328
+ const messages = modelInput["messages"];
30329
+ if (!Array.isArray(messages)) {
30330
+ return void 0;
30331
+ }
30332
+ const instructions = modelInput["instructions"];
30333
+ const value = [
30334
+ ...instructions !== void 0 ? [{ content: instructions, role: "system" }] : [],
30335
+ ...messages
30336
+ ];
30337
+ try {
30338
+ const cloned = JSON.parse(JSON.stringify(value));
30339
+ if (!Array.isArray(cloned)) {
30340
+ return void 0;
30341
+ }
30342
+ return cloned;
30343
+ } catch {
30344
+ return void 0;
30345
+ }
30346
+ }
30347
+ function isCapturedModelInput(input) {
30348
+ return Array.isArray(input);
30349
+ }
30350
+ function llmInputKey(sessionId, turnId, stepIndex) {
30351
+ return `${sessionId}\0${turnId}\0${stepIndex}`;
30352
+ }
30353
+ function modelMetadataFromRuntime(runtime) {
30354
+ if (!isObject(runtime)) {
30355
+ return {};
30356
+ }
30357
+ const modelId = runtime["modelId"];
30358
+ return typeof modelId === "string" ? modelMetadataFromModelId(modelId) : {};
30359
+ }
30360
+ function modelMetadataFromModelId(modelId) {
30361
+ const normalized = modelId.trim();
30362
+ if (!normalized) {
30363
+ return {};
30364
+ }
30365
+ const slashIndex = normalized.indexOf("/");
30366
+ if (slashIndex > 0 && slashIndex < normalized.length - 1) {
30367
+ return {
30368
+ model: normalized.slice(slashIndex + 1),
30369
+ provider: normalized.slice(0, slashIndex)
30370
+ };
30371
+ }
30372
+ return {
30373
+ model: normalized
30374
+ };
30375
+ }
30376
+ function sessionIdFromContext(ctx) {
30377
+ if (!isObject(ctx)) {
30378
+ return void 0;
30379
+ }
30380
+ const session = ctx["session"];
30381
+ if (!isObject(session)) {
30382
+ return void 0;
30383
+ }
30384
+ const id = session["id"];
30385
+ return typeof id === "string" ? id : void 0;
30386
+ }
30387
+ function toolMetadataFromTurn(turn) {
30388
+ const { model: _model, provider: _provider, ...metadata } = turn.metadata;
30389
+ return metadata;
30390
+ }
30391
+ function parentLineageFromContext(ctx) {
30392
+ if (!isObject(ctx)) {
30393
+ return void 0;
30394
+ }
30395
+ const session = ctx.session;
30396
+ if (!isObject(session)) {
30397
+ return void 0;
30398
+ }
30399
+ const parent = session["parent"];
30400
+ if (!isObject(parent)) {
30401
+ return void 0;
30402
+ }
30403
+ const callId = parent["callId"];
30404
+ const rootSessionId = parent["rootSessionId"];
30405
+ const sessionId = parent["sessionId"];
30406
+ if (typeof callId !== "string" || typeof rootSessionId !== "string" || typeof sessionId !== "string") {
30407
+ return void 0;
30408
+ }
30409
+ return { callId, rootSessionId, sessionId };
30410
+ }
30411
+ function isToolCallAction(action) {
30412
+ return isObject(action) && action["kind"] === "tool-call" && typeof action["callId"] === "string" && typeof action["toolName"] === "string" && isObject(action["input"]);
30413
+ }
30414
+ function isLocalSubagentCallAction(action) {
30415
+ return isObject(action) && action["kind"] === "subagent-call" && typeof action["callId"] === "string" && isObject(action["input"]);
30416
+ }
30417
+ function isTraceableActionRequest(action) {
30418
+ return isToolCallAction(action) || isLocalSubagentCallAction(action);
30419
+ }
30420
+ function isToolResult(result) {
30421
+ return isObject(result) && result["kind"] === "tool-result" && typeof result["callId"] === "string" && typeof result["toolName"] === "string";
30422
+ }
30423
+ function isSubagentResult(result) {
30424
+ return isObject(result) && result["kind"] === "subagent-result" && typeof result["callId"] === "string" && typeof result["subagentName"] === "string";
30425
+ }
30426
+ function normalizedFinishReason(finishReason) {
30427
+ switch (finishReason) {
30428
+ case "content-filter":
30429
+ return "content_filter";
30430
+ case "tool-calls":
30431
+ return "tool_calls";
30432
+ default:
30433
+ return finishReason;
30434
+ }
30435
+ }
30436
+ function errorFromMessage(message, code, details) {
30437
+ const error = new Error(`${code}: ${message}`);
30438
+ if (details !== void 0) {
30439
+ error.cause = details;
30440
+ }
30441
+ return error;
30442
+ }
30443
+ function actionResultError(error, output) {
30444
+ if (error) {
30445
+ return errorFromMessage(error.message, error.code);
30446
+ }
30447
+ const result = new Error("Eve action failed");
30448
+ if (output !== void 0) {
30449
+ result.cause = output;
30450
+ }
30451
+ return result;
30452
+ }
30453
+ function eventTime2(event) {
30454
+ if (!event.meta?.at) {
30455
+ return void 0;
30456
+ }
30457
+ const timestamp = Date.parse(event.meta.at);
30458
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
30459
+ }
30460
+ function turnKey2(sessionId, turnId) {
30461
+ return `${sessionId}:${turnId}`;
30462
+ }
30463
+ function toolKey3(sessionId, callId) {
30464
+ return `${sessionId}:${callId}`;
30465
+ }
30466
+ async function rootSpanIdForSession(sessionId) {
30467
+ return deterministicEveId("eve:root", sessionId);
30468
+ }
30469
+ async function generateEveIds(kind, ...parts) {
30470
+ const [rowId, spanId] = await Promise.all([
30471
+ deterministicEveId(`eve:row:${kind}`, ...parts),
30472
+ deterministicEveId(`eve:${kind}`, ...parts)
30473
+ ]);
30474
+ return { rowId, spanId };
30475
+ }
30476
+ async function spanIdForSubagent(sessionId, callId) {
30477
+ return deterministicEveId("eve:subagent", sessionId, callId);
30478
+ }
30479
+ async function deterministicEveId(...parts) {
30480
+ const data = new TextEncoder().encode(
30481
+ parts.map((part) => `${part.length}:${part}`).join("\0")
30482
+ );
30483
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", data);
30484
+ const bytes = Array.from(new Uint8Array(digest, 0, 16));
30485
+ const hex = bytes.map((byte) => byte.toString(16).padStart(2, "0")).join("");
30486
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
30487
+ }
30488
+
28940
30489
  // src/instrumentation/config.ts
28941
30490
  var envIntegrationAliases = {
28942
30491
  openai: "openai",
@@ -29145,6 +30694,8 @@ export {
29145
30694
  BasePlugin,
29146
30695
  BraintrustPlugin,
29147
30696
  OpenAIAgentsTraceProcessor,
30697
+ braintrustEveHook,
30698
+ braintrustEveInstrumentation,
29148
30699
  braintrustFlueInstrumentation,
29149
30700
  braintrustFlueObserver,
29150
30701
  configureInstrumentation,