braintrust 3.14.0 → 3.15.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.
Files changed (42) hide show
  1. package/dev/dist/index.js +616 -1026
  2. package/dev/dist/index.mjs +577 -987
  3. package/dist/apply-auto-instrumentation.js +176 -186
  4. package/dist/apply-auto-instrumentation.mjs +6 -16
  5. package/dist/auto-instrumentations/bundler/esbuild.cjs +5 -39
  6. package/dist/auto-instrumentations/bundler/esbuild.mjs +1 -1
  7. package/dist/auto-instrumentations/bundler/next.cjs +5 -39
  8. package/dist/auto-instrumentations/bundler/next.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/rollup.cjs +5 -39
  10. package/dist/auto-instrumentations/bundler/rollup.mjs +1 -1
  11. package/dist/auto-instrumentations/bundler/vite.cjs +5 -39
  12. package/dist/auto-instrumentations/bundler/vite.mjs +1 -1
  13. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +5 -39
  14. package/dist/auto-instrumentations/bundler/webpack.cjs +5 -39
  15. package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
  16. package/dist/auto-instrumentations/{chunk-OTUQ7KH5.mjs → chunk-CNQ7BUKN.mjs} +1 -1
  17. package/dist/auto-instrumentations/{chunk-XKAAVWT6.mjs → chunk-VXJONZVX.mjs} +5 -39
  18. package/dist/auto-instrumentations/hook.mjs +5 -39
  19. package/dist/browser.d.mts +13 -17
  20. package/dist/browser.d.ts +13 -17
  21. package/dist/browser.js +581 -1015
  22. package/dist/browser.mjs +581 -1015
  23. package/dist/{chunk-NU2GSPHX.mjs → chunk-O4ZIWXO3.mjs} +0 -24
  24. package/dist/{chunk-NKD77KGB.js → chunk-VMBQETG3.js} +0 -24
  25. package/dist/cli.js +578 -988
  26. package/dist/edge-light.d.mts +1 -1
  27. package/dist/edge-light.d.ts +1 -1
  28. package/dist/edge-light.js +581 -1015
  29. package/dist/edge-light.mjs +581 -1015
  30. package/dist/index.d.mts +13 -17
  31. package/dist/index.d.ts +13 -17
  32. package/dist/index.js +967 -1377
  33. package/dist/index.mjs +582 -992
  34. package/dist/instrumentation/index.d.mts +11 -1
  35. package/dist/instrumentation/index.d.ts +11 -1
  36. package/dist/instrumentation/index.js +611 -1015
  37. package/dist/instrumentation/index.mjs +610 -1015
  38. package/dist/workerd.d.mts +1 -1
  39. package/dist/workerd.d.ts +1 -1
  40. package/dist/workerd.js +581 -1015
  41. package/dist/workerd.mjs +581 -1015
  42. package/package.json +1 -1
@@ -23430,586 +23430,210 @@ var flueChannels = defineChannels("@flue/runtime", {
23430
23430
  createContext: channel({
23431
23431
  channelName: "createFlueContext",
23432
23432
  kind: "sync-stream"
23433
- }),
23434
- openSession: channel({
23435
- channelName: "Harness.openSession",
23436
- kind: "async"
23437
- }),
23438
- contextEvent: channel({
23439
- channelName: "context.event",
23440
- kind: "sync-stream"
23441
- }),
23442
- prompt: channel({
23443
- channelName: "session.prompt",
23444
- kind: "async"
23445
- }),
23446
- skill: channel({
23447
- channelName: "session.skill",
23448
- kind: "async"
23449
- }),
23450
- task: channel({
23451
- channelName: "session.task",
23452
- kind: "async"
23453
- }),
23454
- compact: channel({
23455
- channelName: "session.compact",
23456
- kind: "async"
23457
23433
  })
23458
23434
  });
23459
23435
 
23460
- // src/wrappers/flue.ts
23461
- var WRAPPED_FLUE_CONTEXT = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-context");
23462
- var WRAPPED_FLUE_HARNESS = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-harness");
23463
- var WRAPPED_FLUE_SESSION = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-session");
23464
- var SUBSCRIBED_FLUE_CONTEXT_EVENTS = /* @__PURE__ */ Symbol.for(
23465
- "braintrust.flue.subscribed-context-events"
23466
- );
23467
- function patchFlueContextInPlace(ctx) {
23468
- const context = ctx;
23469
- if (context[WRAPPED_FLUE_CONTEXT]) {
23470
- return ctx;
23471
- }
23472
- const originalInit = context.init.bind(context);
23473
- try {
23474
- Object.defineProperty(context, WRAPPED_FLUE_CONTEXT, {
23475
- configurable: false,
23476
- enumerable: false,
23477
- value: true
23478
- });
23479
- Object.defineProperty(context, "init", {
23480
- configurable: true,
23481
- value: async function wrappedFlueInit(options) {
23482
- const harness = await originalInit(options);
23483
- return wrapFlueHarness(harness);
23484
- },
23485
- writable: true
23486
- });
23487
- } catch {
23488
- }
23489
- return ctx;
23490
- }
23491
- function subscribeFlueContextEvents(ctx, options = {}) {
23492
- if (!ctx || typeof ctx !== "object" || typeof ctx.subscribeEvent !== "function") {
23493
- return void 0;
23436
+ // src/instrumentation/plugins/flue-plugin.ts
23437
+ var FLUE_AUTO_STATE = /* @__PURE__ */ Symbol.for("braintrust.flue.auto-state");
23438
+ var FLUE_OBSERVE_BRIDGE = /* @__PURE__ */ Symbol.for("braintrust.flue.observe-bridge");
23439
+ var braintrustFlueObserver = (event, ctx) => {
23440
+ getObserveBridge().handle(event, ctx);
23441
+ };
23442
+ var FluePlugin = class extends BasePlugin {
23443
+ onEnable() {
23444
+ this.unsubscribers.push(enableFlueAutoInstrumentation());
23494
23445
  }
23495
- const context = ctx;
23496
- const captureTurnSpans = options.captureTurnSpans ?? true;
23497
- const existingSubscription = context[SUBSCRIBED_FLUE_CONTEXT_EVENTS];
23498
- if (existingSubscription) {
23499
- if (existingSubscription.captureTurnSpans || !captureTurnSpans) {
23500
- return void 0;
23501
- }
23502
- try {
23503
- existingSubscription.unsubscribe();
23504
- } catch {
23446
+ onDisable() {
23447
+ for (const unsubscribe of this.unsubscribers) {
23448
+ unsubscribe();
23505
23449
  }
23450
+ this.unsubscribers = [];
23506
23451
  }
23507
- try {
23508
- const unsubscribe = ctx.subscribeEvent((event) => {
23509
- flueChannels.contextEvent.traceSync(() => void 0, {
23510
- arguments: [event],
23511
- captureTurnSpans,
23512
- context: ctx
23513
- });
23514
- });
23515
- if (existingSubscription) {
23516
- existingSubscription.captureTurnSpans = captureTurnSpans;
23517
- existingSubscription.unsubscribe = unsubscribe;
23518
- } else {
23519
- Object.defineProperty(context, SUBSCRIBED_FLUE_CONTEXT_EVENTS, {
23520
- configurable: false,
23521
- enumerable: false,
23522
- value: {
23523
- captureTurnSpans,
23524
- unsubscribe
23525
- }
23526
- });
23452
+ };
23453
+ function enableFlueAutoInstrumentation() {
23454
+ const state = getAutoState();
23455
+ state.refCount += 1;
23456
+ if (!state.handlers) {
23457
+ const channel2 = flueChannels.createContext.tracingChannel();
23458
+ const handlers = {
23459
+ end: (event) => {
23460
+ subscribeToFlueContext(event.result, state);
23461
+ }
23462
+ };
23463
+ channel2.subscribe(handlers);
23464
+ state.channel = channel2;
23465
+ state.handlers = handlers;
23466
+ }
23467
+ let released = false;
23468
+ return () => {
23469
+ if (released) {
23470
+ return;
23527
23471
  }
23528
- return unsubscribe;
23529
- } catch {
23530
- return void 0;
23472
+ released = true;
23473
+ releaseAutoState(state);
23474
+ };
23475
+ }
23476
+ function getAutoState() {
23477
+ const existing = Reflect.get(globalThis, FLUE_AUTO_STATE);
23478
+ if (isAutoState(existing)) {
23479
+ return existing;
23531
23480
  }
23481
+ const state = {
23482
+ contexts: /* @__PURE__ */ new WeakSet(),
23483
+ refCount: 0
23484
+ };
23485
+ Reflect.set(globalThis, FLUE_AUTO_STATE, state);
23486
+ return state;
23532
23487
  }
23533
- function wrapFlueHarness(harness) {
23534
- if (!isPlausibleFlueHarness(harness)) {
23535
- return harness;
23488
+ function getObserveBridge() {
23489
+ const existing = Reflect.get(globalThis, FLUE_OBSERVE_BRIDGE);
23490
+ if (isFlueObserveBridge(existing)) {
23491
+ return existing;
23536
23492
  }
23537
- const target = harness;
23538
- if (target[WRAPPED_FLUE_HARNESS]) {
23539
- return harness;
23493
+ const bridge = new FlueObserveBridge();
23494
+ Reflect.set(globalThis, FLUE_OBSERVE_BRIDGE, bridge);
23495
+ return bridge;
23496
+ }
23497
+ function isFlueObserveBridge(value) {
23498
+ return isObjectLike(value) && typeof Reflect.get(value, "handle") === "function" && typeof Reflect.get(value, "reset") === "function";
23499
+ }
23500
+ function isAutoState(value) {
23501
+ return isObjectLike(value) && Reflect.get(value, "contexts") instanceof WeakSet && typeof Reflect.get(value, "refCount") === "number";
23502
+ }
23503
+ function releaseAutoState(state) {
23504
+ state.refCount -= 1;
23505
+ if (state.refCount > 0) {
23506
+ return;
23540
23507
  }
23541
- const originalSession = target.session.bind(target);
23542
23508
  try {
23543
- Object.defineProperty(target, WRAPPED_FLUE_HARNESS, {
23544
- configurable: false,
23545
- enumerable: false,
23546
- value: true
23547
- });
23548
- Object.defineProperty(target, "session", {
23549
- configurable: true,
23550
- value: async function wrappedFlueHarnessSession(name, options) {
23551
- const session = await originalSession(name, options);
23552
- return patchFlueSessionInPlace(session);
23553
- },
23554
- writable: true
23555
- });
23556
- const sessions = target.sessions;
23557
- if (sessions && typeof sessions === "object") {
23558
- patchFlueSessionFactory(sessions, "get");
23559
- patchFlueSessionFactory(sessions, "create");
23509
+ if (state.channel && state.handlers) {
23510
+ state.channel.unsubscribe(state.handlers);
23560
23511
  }
23561
- } catch {
23512
+ } finally {
23513
+ Reflect.deleteProperty(globalThis, FLUE_AUTO_STATE);
23562
23514
  }
23563
- return harness;
23564
23515
  }
23565
- function patchFlueSessionInPlace(session) {
23566
- if (session[WRAPPED_FLUE_SESSION]) {
23567
- return session;
23516
+ function subscribeToFlueContext(value, state) {
23517
+ if (!isObservableFlueContext(value) || state.contexts.has(value)) {
23518
+ return;
23568
23519
  }
23520
+ const ctx = flueContextFromUnknown(value);
23521
+ let released = false;
23522
+ let unsubscribe;
23523
+ const release = () => {
23524
+ if (released) {
23525
+ return;
23526
+ }
23527
+ released = true;
23528
+ try {
23529
+ unsubscribe?.();
23530
+ } catch (error) {
23531
+ logInstrumentationError3("Flue context unsubscribe", error);
23532
+ }
23533
+ };
23569
23534
  try {
23570
- Object.defineProperty(session, WRAPPED_FLUE_SESSION, {
23571
- configurable: false,
23572
- enumerable: false,
23573
- value: true
23535
+ unsubscribe = value.subscribeEvent((event) => {
23536
+ if (state.refCount <= 0) {
23537
+ release();
23538
+ return;
23539
+ }
23540
+ braintrustFlueObserver(event, ctx);
23541
+ if (isAutoContextTerminalEvent(event, ctx)) {
23542
+ release();
23543
+ }
23574
23544
  });
23575
- patchCallHandleMethod(session, "prompt", flueChannels.prompt);
23576
- patchCallHandleMethod(session, "skill", flueChannels.skill);
23577
- patchCallHandleMethod(session, "task", flueChannels.task);
23578
- patchCompact(session);
23579
- } catch {
23545
+ state.contexts.add(value);
23546
+ } catch (error) {
23547
+ logInstrumentationError3("Flue context subscription", error);
23580
23548
  }
23581
- return session;
23582
23549
  }
23583
- function patchFlueSessionFactory(sessions, method) {
23584
- const original = sessions[method];
23585
- if (typeof original !== "function") {
23586
- return;
23550
+ function isAutoContextTerminalEvent(event, ctx) {
23551
+ if (!isObjectLike(event)) {
23552
+ return false;
23587
23553
  }
23588
- const bound = original.bind(sessions);
23589
- Object.defineProperty(sessions, method, {
23590
- configurable: true,
23591
- value: async function wrappedFlueSessionFactory(name, options) {
23592
- const session = await bound(name, options);
23593
- return patchFlueSessionInPlace(session);
23594
- },
23595
- writable: true
23596
- });
23597
- }
23598
- function patchCallHandleMethod(session, method, channel2) {
23599
- const original = session[method];
23600
- if (typeof original !== "function") {
23601
- return;
23554
+ const type = Reflect.get(event, "type");
23555
+ if (type === "run_end") {
23556
+ return true;
23602
23557
  }
23603
- const bound = original.bind(session);
23604
- Object.defineProperty(session, method, {
23605
- configurable: true,
23606
- value(input, options) {
23607
- const args = [input, options];
23608
- const { originalResult, traced: traced2 } = traceFlueOperation(channel2, {
23609
- context: {
23610
- arguments: args,
23611
- operation: method,
23612
- session
23613
- },
23614
- run: () => bound(input, options)
23615
- });
23616
- return preserveCallHandle(originalResult, traced2);
23617
- },
23618
- writable: true
23619
- });
23620
- }
23621
- function patchCompact(session) {
23622
- const original = session.compact;
23623
- if (typeof original !== "function") {
23624
- return;
23558
+ if (type !== "operation") {
23559
+ return false;
23625
23560
  }
23626
- const bound = original.bind(session);
23627
- Object.defineProperty(session, "compact", {
23628
- configurable: true,
23629
- value() {
23630
- const context = {
23631
- arguments: [],
23632
- operation: "compact",
23633
- session
23634
- };
23635
- return flueChannels.compact.tracePromise(() => bound(), context);
23636
- },
23637
- writable: true
23638
- });
23561
+ return !ctx?.runId && typeof Reflect.get(event, "runId") !== "string";
23639
23562
  }
23640
- function traceFlueOperation(channel2, args) {
23641
- const tracingChannel2 = channel2.tracingChannel();
23642
- const context = args.context;
23643
- let originalResult;
23644
- let traced2;
23645
- const run = () => {
23646
- try {
23647
- originalResult = args.run();
23648
- tracingChannel2.end?.publish(context);
23649
- } catch (error) {
23650
- context.error = normalizeError3(error);
23651
- tracingChannel2.error?.publish(context);
23652
- tracingChannel2.end?.publish(context);
23653
- throw error;
23654
- }
23655
- traced2 = Promise.resolve(originalResult).then(
23656
- (result) => {
23657
- context.result = result;
23658
- tracingChannel2.asyncStart?.publish(context);
23659
- tracingChannel2.asyncEnd?.publish(context);
23660
- return result;
23661
- },
23662
- (error) => {
23663
- context.error = normalizeError3(error);
23664
- tracingChannel2.error?.publish(context);
23665
- tracingChannel2.asyncStart?.publish(context);
23666
- tracingChannel2.asyncEnd?.publish(context);
23667
- throw error;
23668
- }
23669
- );
23670
- };
23671
- if (tracingChannel2.start?.runStores) {
23672
- tracingChannel2.start.runStores(context, run);
23673
- } else {
23674
- tracingChannel2.start?.publish(context);
23675
- run();
23676
- }
23677
- return { originalResult, traced: traced2 };
23563
+ function isObservableFlueContext(value) {
23564
+ return isObjectLike(value) && typeof Reflect.get(value, "subscribeEvent") === "function";
23678
23565
  }
23679
- function normalizeError3(error) {
23680
- return error instanceof Error ? error : new Error(String(error));
23566
+ function isFlueEvent(event) {
23567
+ const type = Reflect.get(event, "type");
23568
+ return type === "run_start" || type === "run_end" || type === "operation_start" || type === "operation" || type === "turn_request" || type === "turn" || type === "tool_start" || type === "tool_call" || type === "task_start" || type === "task" || type === "compaction_start" || type === "compaction";
23681
23569
  }
23682
- function preserveCallHandle(originalHandle, traced2) {
23683
- if (!isFlueCallHandle(originalHandle)) {
23684
- return traced2;
23570
+ function flueContextFromUnknown(ctx) {
23571
+ if (!isObjectLike(ctx)) {
23572
+ return void 0;
23685
23573
  }
23686
- const handle = originalHandle;
23687
- const wrapped = {
23688
- get signal() {
23689
- return handle.signal;
23690
- },
23691
- abort(reason) {
23692
- return handle.abort(reason);
23693
- },
23694
- then(onfulfilled, onrejected) {
23695
- return traced2.then(onfulfilled, onrejected);
23696
- }
23574
+ const id = Reflect.get(ctx, "id");
23575
+ const runId = Reflect.get(ctx, "runId");
23576
+ return {
23577
+ ...typeof id === "string" ? { id } : {},
23578
+ ...typeof runId === "string" ? { runId } : {}
23697
23579
  };
23698
- return wrapped;
23699
- }
23700
- function isPlausibleFlueHarness(value) {
23701
- return !!value && typeof value === "object" && typeof value.session === "function";
23702
23580
  }
23703
- function isFlueCallHandle(value) {
23704
- return !!value && typeof value === "object" && typeof value.then === "function" && typeof value.abort === "function" && "signal" in value;
23581
+ function isObjectLike(value) {
23582
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23705
23583
  }
23706
-
23707
- // src/instrumentation/plugins/flue-plugin.ts
23708
- var FluePlugin = class extends BasePlugin {
23709
- activeOperationsById = /* @__PURE__ */ new Map();
23710
- activeOperationsByScope = /* @__PURE__ */ new Map();
23711
- compactionsByScope = /* @__PURE__ */ new Map();
23712
- pendingOperationsByKey = /* @__PURE__ */ new Map();
23584
+ var FlueObserveBridge = class {
23585
+ compactionsByKey = /* @__PURE__ */ new Map();
23586
+ operationsById = /* @__PURE__ */ new Map();
23587
+ runsById = /* @__PURE__ */ new Map();
23588
+ seenEvents = /* @__PURE__ */ new WeakSet();
23713
23589
  tasksById = /* @__PURE__ */ new Map();
23714
- toolsById = /* @__PURE__ */ new Map();
23715
- turnsByScope = /* @__PURE__ */ new Map();
23716
- onEnable() {
23717
- this.subscribeToContextCreation();
23718
- this.subscribeToSessionCreation();
23719
- this.subscribeToContextEvents();
23720
- this.subscribeToSessionOperations();
23721
- }
23722
- onDisable() {
23723
- for (const unsubscribe of this.unsubscribers) {
23724
- unsubscribe();
23725
- }
23726
- this.unsubscribers = [];
23727
- this.activeOperationsById.clear();
23728
- this.activeOperationsByScope.clear();
23729
- this.compactionsByScope.clear();
23730
- this.pendingOperationsByKey.clear();
23731
- this.tasksById.clear();
23732
- this.toolsById.clear();
23733
- this.turnsByScope.clear();
23734
- }
23735
- subscribeToContextCreation() {
23736
- const channel2 = flueChannels.createContext.tracingChannel();
23737
- const handlers = {
23738
- end: (event) => {
23739
- const ctx = event.result;
23740
- if (!ctx) {
23741
- return;
23742
- }
23743
- subscribeFlueContextEvents(ctx, { captureTurnSpans: false });
23744
- patchFlueContextInPlace(ctx);
23745
- },
23746
- error: () => {
23747
- }
23748
- };
23749
- channel2.subscribe(handlers);
23750
- this.unsubscribers.push(() => {
23751
- channel2.unsubscribe(handlers);
23752
- });
23753
- }
23754
- subscribeToSessionCreation() {
23755
- const channel2 = flueChannels.openSession.tracingChannel();
23756
- const handlers = {
23757
- asyncEnd: (event) => {
23758
- if (event.result) {
23759
- patchFlueSessionInPlace(
23760
- event.result
23761
- );
23762
- }
23763
- if (event.harness) {
23764
- wrapFlueHarness(event.harness);
23765
- }
23766
- },
23767
- error: () => {
23768
- }
23769
- };
23770
- channel2.subscribe(handlers);
23771
- this.unsubscribers.push(() => {
23772
- channel2.unsubscribe(handlers);
23773
- });
23774
- }
23775
- subscribeToSessionOperations() {
23776
- this.subscribeToSessionOperation(flueChannels.prompt);
23777
- this.subscribeToSessionOperation(flueChannels.skill);
23778
- this.subscribeToSessionOperation(flueChannels.task);
23779
- this.subscribeToCompact();
23780
- }
23781
- subscribeToSessionOperation(channel2) {
23782
- const tracingChannel2 = channel2.tracingChannel();
23783
- const states = /* @__PURE__ */ new WeakMap();
23784
- const ensureState2 = (event) => {
23785
- const existing = states.get(event);
23786
- if (existing) {
23787
- return existing;
23788
- }
23789
- const state = this.startOperationState({
23790
- args: event.arguments,
23791
- moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
23792
- operation: event.operation,
23793
- session: event.session
23794
- });
23795
- states.set(event, state);
23796
- return state;
23797
- };
23798
- const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
23799
- tracingChannel2,
23800
- ensureState2
23801
- );
23802
- const handlers = {
23803
- start: (event) => {
23804
- ensureState2(event);
23805
- },
23806
- asyncEnd: (event) => {
23807
- this.endOperationState(states.get(event), event.result);
23808
- states.delete(event);
23809
- },
23810
- error: (event) => {
23811
- const state = states.get(event);
23812
- if (state && event.error) {
23813
- safeLog3(state.span, { error: errorToString(event.error) });
23814
- this.finishOperationState(state);
23815
- }
23816
- states.delete(event);
23817
- }
23818
- };
23819
- tracingChannel2.subscribe(handlers);
23820
- this.unsubscribers.push(() => {
23821
- unbindCurrentSpanStore?.();
23822
- tracingChannel2.unsubscribe(handlers);
23823
- });
23824
- }
23825
- subscribeToCompact() {
23826
- const tracingChannel2 = flueChannels.compact.tracingChannel();
23827
- const states = /* @__PURE__ */ new WeakMap();
23828
- const ensureState2 = (event) => {
23829
- const existing = states.get(event);
23830
- if (existing) {
23831
- return existing;
23832
- }
23833
- const state = this.startOperationState({
23834
- args: [],
23835
- moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
23836
- operation: event.operation,
23837
- session: event.session
23838
- });
23839
- states.set(event, state);
23840
- return state;
23841
- };
23842
- const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
23843
- tracingChannel2,
23844
- ensureState2
23845
- );
23846
- const handlers = {
23847
- start: (event) => {
23848
- ensureState2(event);
23849
- },
23850
- asyncEnd: (event) => {
23851
- this.endOperationState(states.get(event), void 0);
23852
- states.delete(event);
23853
- },
23854
- error: (event) => {
23855
- const state = states.get(event);
23856
- if (state && event.error) {
23857
- safeLog3(state.span, { error: errorToString(event.error) });
23858
- this.finishOperationState(state);
23859
- }
23860
- states.delete(event);
23861
- }
23862
- };
23863
- tracingChannel2.subscribe(handlers);
23864
- this.unsubscribers.push(() => {
23865
- unbindCurrentSpanStore?.();
23866
- tracingChannel2.unsubscribe(handlers);
23867
- });
23868
- }
23869
- subscribeToContextEvents() {
23870
- const channel2 = flueChannels.contextEvent.tracingChannel();
23871
- const handlers = {
23872
- start: (event) => {
23873
- const flueEvent = event.arguments[0];
23874
- if (!flueEvent) {
23875
- return;
23876
- }
23877
- try {
23878
- this.handleFlueEvent(flueEvent, {
23879
- captureTurnSpans: event.captureTurnSpans !== false
23880
- });
23881
- } catch (error) {
23882
- logInstrumentationError3("Flue event", error);
23883
- }
23884
- },
23885
- error: () => {
23886
- }
23887
- };
23888
- channel2.subscribe(handlers);
23889
- this.unsubscribers.push(() => {
23890
- channel2.unsubscribe(handlers);
23891
- });
23892
- }
23893
- bindCurrentSpanStoreToOperationStart(tracingChannel2, ensureState2) {
23894
- const state = _internalGetGlobalState();
23895
- const startChannel = tracingChannel2.start;
23896
- const contextManager = state?.contextManager;
23897
- const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
23898
- if (!currentSpanStore || !startChannel) {
23899
- return void 0;
23590
+ toolsByKey = /* @__PURE__ */ new Map();
23591
+ turnsByKey = /* @__PURE__ */ new Map();
23592
+ handle(event, ctx) {
23593
+ if (!isObjectLike(event) || !isFlueEvent(event)) {
23594
+ return;
23900
23595
  }
23901
- startChannel.bindStore(currentSpanStore, (event) => {
23902
- const operationState = ensureState2(event);
23903
- return contextManager.wrapSpanForStore(operationState.span);
23904
- });
23905
- return () => {
23906
- startChannel.unbindStore(currentSpanStore);
23907
- };
23908
- }
23909
- startOperationState(args) {
23910
- const sessionName = getSessionName(args.session);
23911
- const metadata = {
23912
- ...extractOperationInputMetadata(args.operation, args.args),
23913
- ...extractSessionMetadata(args.session),
23914
- "flue.operation": args.operation,
23915
- provider: "flue",
23916
- ...args.moduleVersion ? { "flue.version": args.moduleVersion } : {}
23917
- };
23918
- const span = startSpan({
23919
- name: `flue.session.${args.operation}`,
23920
- spanAttributes: { type: "task" /* TASK */ }
23921
- });
23922
- const state = {
23923
- metadata,
23924
- operation: args.operation,
23925
- sessionName,
23926
- span,
23927
- startTime: getCurrentUnixTimestamp()
23928
- };
23929
- safeLog3(span, {
23930
- input: extractOperationInput(args.operation, args.args),
23931
- metadata
23932
- });
23933
- this.pendingOperationQueue(operationKey(sessionName, args.operation)).push(
23934
- state
23935
- );
23936
- addOperationToScope(
23937
- this.activeOperationsByScope,
23938
- sessionName ?? "unknown",
23939
- state
23940
- );
23941
- return state;
23942
- }
23943
- endOperationState(state, result) {
23944
- if (!state) {
23596
+ if (this.seenEvents.has(event)) {
23945
23597
  return;
23946
23598
  }
23947
- const metadata = {
23948
- ...state.metadata,
23949
- ...extractPromptResponseMetadata(result)
23950
- };
23951
- const metrics = {
23952
- ...buildDurationMetrics3(state.startTime),
23953
- ...metricsFromUsage(result?.usage)
23954
- };
23955
- safeLog3(state.span, {
23956
- metadata,
23957
- metrics,
23958
- output: extractOperationOutput(result)
23959
- });
23960
- this.finishCompactionsForOperation(state);
23961
- this.finishOperationState(state);
23962
- }
23963
- finishOperationState(state) {
23964
- removePendingOperation(this.pendingOperationsByKey, state);
23965
- if (state.operationId) {
23966
- this.activeOperationsById.delete(state.operationId);
23599
+ this.seenEvents.add(event);
23600
+ try {
23601
+ this.handleEvent(event, flueContextFromUnknown(ctx));
23602
+ } catch (error) {
23603
+ logInstrumentationError3("Flue observe", error);
23967
23604
  }
23968
- removeScopedOperation(this.activeOperationsByScope, state);
23969
- state.span.end();
23970
23605
  }
23971
- handleFlueEvent(event, options) {
23606
+ reset() {
23607
+ this.compactionsByKey.clear();
23608
+ this.operationsById.clear();
23609
+ this.runsById.clear();
23610
+ this.seenEvents = /* @__PURE__ */ new WeakSet();
23611
+ this.tasksById.clear();
23612
+ this.toolsByKey.clear();
23613
+ this.turnsByKey.clear();
23614
+ }
23615
+ handleEvent(event, ctx) {
23972
23616
  switch (event.type) {
23617
+ case "run_start":
23618
+ this.handleRunStart(event, ctx);
23619
+ return;
23620
+ case "run_end":
23621
+ this.handleRunEnd(event);
23622
+ return;
23973
23623
  case "operation_start":
23974
23624
  this.handleOperationStart(event);
23975
23625
  return;
23976
23626
  case "operation":
23977
23627
  this.handleOperation(event);
23978
23628
  return;
23979
- case "text_delta":
23980
- if (!options.captureTurnSpans) {
23981
- return;
23982
- }
23983
- this.ensureTurnState(event).text.push(
23984
- typeof event.text === "string" ? event.text : ""
23985
- );
23986
- return;
23987
- case "thinking_start":
23988
- if (!options.captureTurnSpans) {
23989
- return;
23990
- }
23991
- this.handleThinkingStart(event);
23992
- return;
23993
- case "thinking_delta":
23994
- if (!options.captureTurnSpans) {
23995
- return;
23996
- }
23997
- this.handleThinkingDelta(event);
23998
- return;
23999
- case "thinking_end":
24000
- if (!options.captureTurnSpans) {
24001
- return;
24002
- }
24003
- this.handleThinkingEnd(event);
23629
+ case "turn_request":
23630
+ this.handleTurnRequest(event);
24004
23631
  return;
24005
23632
  case "turn":
24006
- if (!options.captureTurnSpans) {
24007
- return;
24008
- }
24009
23633
  this.handleTurn(event);
24010
23634
  return;
24011
23635
  case "tool_start":
24012
- this.handleToolStart(event, options);
23636
+ this.handleToolStart(event);
24013
23637
  return;
24014
23638
  case "tool_call":
24015
23639
  this.handleToolCall(event);
@@ -24030,203 +23654,216 @@ var FluePlugin = class extends BasePlugin {
24030
23654
  return;
24031
23655
  }
24032
23656
  }
24033
- handleOperationStart(event) {
24034
- if (!isInstrumentedOperation(event.operationKind)) {
23657
+ handleRunStart(event, ctx) {
23658
+ if (!event.runId) {
24035
23659
  return;
24036
23660
  }
24037
- const state = this.takePendingOperationForEvent(event);
24038
- if (!state) {
23661
+ const workflowName = event.workflowName ?? event.owner?.workflowName ?? (typeof ctx?.id === "string" ? ctx.id : "unknown");
23662
+ const metadata = {
23663
+ ...extractPayloadMetadata(event.payload),
23664
+ ...extractEventMetadata(event, ctx),
23665
+ ...workflowName ? { "flue.workflow_name": workflowName } : {},
23666
+ provider: "flue"
23667
+ };
23668
+ const span = startSpan({
23669
+ name: `workflow:${workflowName}`,
23670
+ spanAttributes: { type: "task" /* TASK */ },
23671
+ startTime: eventTime(event.startedAt ?? event.timestamp),
23672
+ event: {
23673
+ input: event.payload,
23674
+ metadata
23675
+ }
23676
+ });
23677
+ this.runsById.set(event.runId, { metadata, span });
23678
+ }
23679
+ handleRunEnd(event) {
23680
+ const state = this.runsById.get(event.runId);
23681
+ this.finishPendingSpansForRun(event);
23682
+ if (state) {
23683
+ safeLog3(state.span, {
23684
+ ...event.isError ? { error: errorToString(event.error) } : {},
23685
+ metadata: {
23686
+ ...state.metadata,
23687
+ ...extractEventMetadata(event),
23688
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
23689
+ },
23690
+ metrics: durationMetrics2(event.durationMs),
23691
+ output: event.result
23692
+ });
23693
+ safeEnd(state.span, eventTime(event.timestamp));
23694
+ this.runsById.delete(event.runId);
23695
+ }
23696
+ void flush().catch((error) => {
23697
+ logInstrumentationError3("Flue flush", error);
23698
+ });
23699
+ }
23700
+ handleOperationStart(event) {
23701
+ if (!event.operationId || !isInstrumentedOperation(event.operationKind)) {
24039
23702
  return;
24040
23703
  }
24041
- state.operationId = event.operationId;
24042
- this.activeOperationsById.set(event.operationId, state);
24043
- addScopedOperation(this.activeOperationsByScope, event, state);
24044
- state.metadata = {
24045
- ...state.metadata,
23704
+ const metadata = {
24046
23705
  ...extractEventMetadata(event),
24047
- "flue.operation_id": event.operationId
23706
+ "flue.operation": event.operationKind,
23707
+ provider: "flue"
24048
23708
  };
24049
- safeLog3(state.span, { metadata: state.metadata });
23709
+ const parent = this.parentSpanForEvent(event);
23710
+ const span = startFlueSpan(parent, {
23711
+ name: `flue.${event.operationKind}`,
23712
+ spanAttributes: { type: "task" /* TASK */ },
23713
+ startTime: eventTime(event.timestamp),
23714
+ event: { metadata }
23715
+ });
23716
+ this.operationsById.set(event.operationId, { metadata, span });
24050
23717
  }
24051
23718
  handleOperation(event) {
24052
- const state = event.operationId ? this.activeOperationsById.get(event.operationId) : void 0;
24053
- if (!state) {
23719
+ if (!isInstrumentedOperation(event.operationKind)) {
24054
23720
  return;
24055
23721
  }
23722
+ const state = this.operationsById.get(event.operationId) ?? this.startSyntheticOperation(event);
23723
+ const output = operationOutput(event);
24056
23724
  const metadata = {
24057
23725
  ...state.metadata,
24058
23726
  ...extractEventMetadata(event),
24059
- ...typeof event.durationMs === "number" ? { "flue.duration_ms": event.durationMs } : {},
24060
- ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
23727
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
23728
+ ...event.usage ? { "flue.usage": event.usage } : {}
24061
23729
  };
24062
- const metrics = metricsFromUsage(event.usage);
23730
+ this.finishPendingChildrenForOperation(event, output);
24063
23731
  safeLog3(state.span, {
24064
- ...event.error ? { error: errorToString(event.error) } : {},
23732
+ ...event.isError ? { error: errorToString(event.error) } : {},
24065
23733
  metadata,
24066
- ...Object.keys(metrics).length ? { metrics } : {}
23734
+ metrics: durationMetrics2(event.durationMs),
23735
+ output
24067
23736
  });
23737
+ safeEnd(state.span, eventTime(event.timestamp));
23738
+ this.operationsById.delete(event.operationId);
24068
23739
  }
24069
- ensureTurnState(event) {
24070
- const scope = scopeKey(event);
24071
- const existing = this.turnsByScope.get(scope);
24072
- if (existing) {
24073
- return existing;
23740
+ handleTurnRequest(event) {
23741
+ const key = turnKey(event);
23742
+ if (!key) {
23743
+ return;
24074
23744
  }
24075
- const parent = this.parentSpanForEvent(event);
24076
23745
  const metadata = {
24077
23746
  ...extractEventMetadata(event),
24078
- provider: "flue"
23747
+ ...event.api ? { "flue.api": event.api } : {},
23748
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
23749
+ ...event.provider ? { provider: event.provider } : { provider: "flue" },
23750
+ ...event.provider ? { "flue.provider": event.provider } : {},
23751
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
23752
+ ...event.reasoning ? { reasoning: event.reasoning } : {},
23753
+ ...event.input?.systemPrompt ? { "flue.system_prompt": event.input.systemPrompt } : {},
23754
+ ...event.input?.tools ? { tools: event.input.tools } : {}
24079
23755
  };
23756
+ const parent = this.parentSpanForTurn(event);
24080
23757
  const span = startFlueSpan(parent, {
24081
- name: "flue.turn",
24082
- spanAttributes: { type: "llm" /* LLM */ }
23758
+ name: `llm:${event.model ?? event.purpose ?? "unknown"}`,
23759
+ spanAttributes: { type: "llm" /* LLM */ },
23760
+ startTime: eventTime(event.timestamp),
23761
+ event: {
23762
+ input: event.input?.messages,
23763
+ metadata
23764
+ }
24083
23765
  });
24084
- const state = {
24085
- metadata,
24086
- span,
24087
- hasThinking: false,
24088
- startTime: getCurrentUnixTimestamp(),
24089
- text: [],
24090
- thinking: [],
24091
- toolCalls: []
24092
- };
24093
- safeLog3(span, { metadata });
24094
- this.turnsByScope.set(scope, state);
24095
- return state;
23766
+ this.logOperationInput(
23767
+ event.operationId,
23768
+ event.input?.messages ?? event.input
23769
+ );
23770
+ this.turnsByKey.set(key, { metadata, span });
24096
23771
  }
24097
23772
  handleTurn(event) {
24098
- const scope = scopeKey(event);
24099
- const state = this.ensureTurnState(event);
24100
- const text = state.text.join("");
24101
- const reasoning = state.finalThinking ?? state.thinking.join("");
24102
- const outputReasoning = reasoning || (state.hasThinking ? "[reasoning stream present; content unavailable]" : void 0);
23773
+ const key = turnKey(event);
23774
+ if (!key) {
23775
+ return;
23776
+ }
23777
+ const state = this.turnsByKey.get(key) ?? this.startSyntheticTurn(event);
24103
23778
  const metadata = {
24104
23779
  ...state.metadata,
24105
23780
  ...extractEventMetadata(event),
23781
+ ...event.api ? { "flue.api": event.api } : {},
24106
23782
  ...event.model ? { model: event.model, "flue.model": event.model } : {},
23783
+ ...event.provider ? { provider: event.provider } : {},
23784
+ ...event.provider ? { "flue.provider": event.provider } : {},
23785
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
24107
23786
  ...event.stopReason ? { "flue.stop_reason": event.stopReason } : {},
24108
- ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
24109
- provider: "flue"
23787
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24110
23788
  };
24111
23789
  safeLog3(state.span, {
24112
- ...event.error ? { error: errorToString(event.error) } : {},
23790
+ ...event.isError ? { error: errorToString(event.error) } : {},
24113
23791
  metadata,
24114
23792
  metrics: {
24115
- ...durationMsMetrics(event.durationMs),
23793
+ ...durationMetrics2(event.durationMs),
24116
23794
  ...metricsFromUsage(event.usage)
24117
23795
  },
24118
- output: toAssistantOutput(
24119
- text,
24120
- event.stopReason,
24121
- outputReasoning,
24122
- state.toolCalls
24123
- )
23796
+ output: event.output
24124
23797
  });
24125
- state.span.end();
24126
- this.turnsByScope.delete(scope);
24127
- }
24128
- handleThinkingDelta(event) {
24129
- const delta = event.delta;
24130
- if (typeof delta !== "string" || !delta) {
24131
- return;
24132
- }
24133
- const state = this.ensureTurnState(event);
24134
- state.hasThinking = true;
24135
- state.metadata["flue.thinking"] = true;
24136
- state.thinking.push(delta);
24137
- }
24138
- handleThinkingStart(event) {
24139
- const state = this.ensureTurnState(event);
24140
- state.hasThinking = true;
24141
- state.metadata["flue.thinking"] = true;
24142
- }
24143
- handleThinkingEnd(event) {
24144
- const state = this.ensureTurnState(event);
24145
- state.hasThinking = true;
24146
- state.metadata["flue.thinking"] = true;
24147
- if (typeof event.content === "string" && event.content) {
24148
- state.finalThinking = event.content;
24149
- }
23798
+ safeEnd(state.span, eventTime(event.timestamp));
23799
+ this.turnsByKey.delete(key);
24150
23800
  }
24151
- handleToolStart(event, options) {
24152
- const toolCallId = event.toolCallId;
24153
- if (!toolCallId) {
23801
+ handleToolStart(event) {
23802
+ if (!event.toolCallId) {
24154
23803
  return;
24155
23804
  }
24156
- const parent = this.parentSpanForEvent(event);
24157
- const scope = scopeKey(event);
24158
- let turnState = this.turnsByScope.get(scope);
24159
- if (!turnState && parent && options.captureTurnSpans) {
24160
- turnState = this.ensureTurnState(event);
24161
- }
24162
23805
  const metadata = {
24163
23806
  ...extractEventMetadata(event),
24164
23807
  ...event.toolName ? { "flue.tool_name": event.toolName } : {},
24165
- "flue.tool_call_id": toolCallId,
23808
+ "flue.tool_call_id": event.toolCallId,
24166
23809
  provider: "flue"
24167
23810
  };
23811
+ const parent = this.parentSpanForTool(event);
24168
23812
  const span = startFlueSpan(parent, {
24169
- name: `tool: ${event.toolName ?? "unknown"}`,
24170
- spanAttributes: { type: "tool" /* TOOL */ }
24171
- });
24172
- if (turnState) {
24173
- turnState.toolCalls.push({
24174
- args: event.args,
24175
- toolCallId,
24176
- toolName: event.toolName
24177
- });
24178
- }
24179
- safeLog3(span, {
24180
- input: event.args,
24181
- metadata
24182
- });
24183
- this.toolsById.set(toolKey(event), {
24184
- metadata,
24185
- span,
24186
- startTime: getCurrentUnixTimestamp()
23813
+ name: `tool:${event.toolName ?? "unknown"}`,
23814
+ spanAttributes: { type: "tool" /* TOOL */ },
23815
+ startTime: eventTime(event.timestamp),
23816
+ event: {
23817
+ input: event.args,
23818
+ metadata
23819
+ }
24187
23820
  });
23821
+ this.toolsByKey.set(toolKey(event), { metadata, span });
24188
23822
  }
24189
23823
  handleToolCall(event) {
23824
+ if (!event.toolCallId) {
23825
+ return;
23826
+ }
24190
23827
  const key = toolKey(event);
24191
- const state = this.toolsById.get(key) ?? this.startSyntheticToolState(event, event.toolName ?? "unknown");
23828
+ const state = this.toolsByKey.get(key) ?? this.startSyntheticTool(event);
24192
23829
  const metadata = {
24193
23830
  ...state.metadata,
24194
23831
  ...extractEventMetadata(event),
24195
23832
  ...event.toolName ? { "flue.tool_name": event.toolName } : {},
24196
- ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
23833
+ "flue.tool_call_id": event.toolCallId,
24197
23834
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24198
23835
  };
24199
23836
  safeLog3(state.span, {
24200
23837
  ...event.isError ? { error: errorToString(event.result) } : {},
24201
23838
  metadata,
24202
- metrics: durationMsMetrics(event.durationMs),
23839
+ metrics: durationMetrics2(event.durationMs),
24203
23840
  output: event.result
24204
23841
  });
24205
- state.span.end();
24206
- this.toolsById.delete(key);
23842
+ safeEnd(state.span, eventTime(event.timestamp));
23843
+ this.toolsByKey.delete(key);
24207
23844
  }
24208
23845
  handleTaskStart(event) {
24209
- const parent = this.parentSpanForEvent(event);
23846
+ if (!event.taskId) {
23847
+ return;
23848
+ }
24210
23849
  const metadata = {
24211
23850
  ...extractEventMetadata(event),
24212
- ...event.role ? { "flue.role": event.role } : {},
23851
+ ...event.agent ? { "flue.agent": event.agent } : {},
24213
23852
  ...event.cwd ? { "flue.cwd": event.cwd } : {},
24214
23853
  "flue.task_id": event.taskId,
24215
23854
  provider: "flue"
24216
23855
  };
23856
+ const parent = this.parentSpanForEvent(event);
24217
23857
  const span = startFlueSpan(parent, {
24218
- name: "flue.task",
24219
- spanAttributes: { type: "task" /* TASK */ }
24220
- });
24221
- safeLog3(span, {
24222
- input: event.prompt,
24223
- metadata
24224
- });
24225
- this.tasksById.set(event.taskId, {
24226
- metadata,
24227
- span,
24228
- startTime: getCurrentUnixTimestamp()
23858
+ name: event.agent ? `task:${event.agent}` : "flue.task",
23859
+ spanAttributes: { type: "task" /* TASK */ },
23860
+ startTime: eventTime(event.timestamp),
23861
+ event: {
23862
+ input: event.prompt,
23863
+ metadata
23864
+ }
24229
23865
  });
23866
+ this.tasksById.set(event.taskId, { metadata, span });
24230
23867
  }
24231
23868
  handleTask(event) {
24232
23869
  const state = this.tasksById.get(event.taskId);
@@ -24238,426 +23875,372 @@ var FluePlugin = class extends BasePlugin {
24238
23875
  metadata: {
24239
23876
  ...state.metadata,
24240
23877
  ...extractEventMetadata(event),
23878
+ ...event.agent ? { "flue.agent": event.agent } : {},
24241
23879
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24242
23880
  },
24243
- metrics: durationMsMetrics(event.durationMs),
23881
+ metrics: durationMetrics2(event.durationMs),
24244
23882
  output: event.result
24245
23883
  });
24246
- state.span.end();
23884
+ safeEnd(state.span, eventTime(event.timestamp));
24247
23885
  this.tasksById.delete(event.taskId);
24248
23886
  }
24249
23887
  handleCompactionStart(event) {
24250
- const operationState = this.operationStateForEvent(event);
24251
- const parent = operationState?.span ?? this.parentSpanForEvent(event);
23888
+ const key = compactionKey(event);
23889
+ const input = {
23890
+ ...event.estimatedTokens !== void 0 ? { estimatedTokens: event.estimatedTokens } : {},
23891
+ ...event.reason ? { reason: event.reason } : {}
23892
+ };
24252
23893
  const metadata = {
24253
23894
  ...extractEventMetadata(event),
24254
23895
  ...event.reason ? { "flue.compaction_reason": event.reason } : {},
24255
23896
  provider: "flue"
24256
23897
  };
24257
- const input = {
24258
- ...typeof event.estimatedTokens === "number" ? { estimatedTokens: event.estimatedTokens } : {},
24259
- ...event.reason ? { reason: event.reason } : {}
24260
- };
23898
+ const parent = this.parentSpanForEvent(event);
24261
23899
  const span = startFlueSpan(parent, {
24262
- name: "flue.compaction",
24263
- spanAttributes: { type: "task" /* TASK */ }
24264
- });
24265
- safeLog3(span, {
24266
- input,
24267
- metadata
24268
- });
24269
- this.compactionsByScope.set(scopeKey(event), {
24270
- input,
24271
- metadata,
24272
- operationState,
24273
- span,
24274
- startTime: getCurrentUnixTimestamp()
23900
+ name: `compaction:${event.reason ?? "unknown"}`,
23901
+ spanAttributes: { type: "task" /* TASK */ },
23902
+ startTime: eventTime(event.timestamp),
23903
+ event: {
23904
+ input,
23905
+ metadata
23906
+ }
24275
23907
  });
23908
+ this.logOperationInput(event.operationId, input);
23909
+ this.compactionsByKey.set(key, { metadata, span });
24276
23910
  }
24277
23911
  handleCompaction(event) {
24278
- const key = scopeKey(event);
24279
- const state = this.compactionsByScope.get(key) ?? this.findCompactionState(event);
24280
- if (!state) {
24281
- return;
24282
- }
23912
+ const key = compactionKey(event);
23913
+ const state = this.compactionsByKey.get(key) ?? this.startSyntheticCompaction(event);
23914
+ const metadata = {
23915
+ ...state.metadata,
23916
+ ...extractEventMetadata(event),
23917
+ ...event.usage ? { "flue.usage": event.usage } : {}
23918
+ };
24283
23919
  safeLog3(state.span, {
24284
- metadata: {
24285
- ...state.metadata,
24286
- ...extractEventMetadata(event),
24287
- ...typeof event.messagesBefore === "number" ? { "flue.messages_before": event.messagesBefore } : {},
24288
- ...typeof event.messagesAfter === "number" ? { "flue.messages_after": event.messagesAfter } : {}
24289
- },
23920
+ metadata,
24290
23921
  metrics: {
24291
- ...durationMsMetrics(event.durationMs),
24292
- ...metricsFromUsage(event.usage)
23922
+ ...durationMetrics2(event.durationMs),
23923
+ ...typeof event.messagesBefore === "number" ? { messages_before: event.messagesBefore } : {},
23924
+ ...typeof event.messagesAfter === "number" ? { messages_after: event.messagesAfter } : {}
24293
23925
  },
24294
23926
  output: {
24295
23927
  messagesAfter: event.messagesAfter,
24296
23928
  messagesBefore: event.messagesBefore
24297
23929
  }
24298
23930
  });
24299
- state.span.end();
24300
- this.deleteCompactionState(state);
23931
+ safeEnd(state.span, eventTime(event.timestamp));
23932
+ this.compactionsByKey.delete(key);
24301
23933
  }
24302
- findCompactionState(event) {
24303
- const operationState = this.operationStateForEvent(event);
24304
- for (const state of this.compactionsByScope.values()) {
24305
- if (operationState && state.operationState === operationState) {
24306
- return state;
23934
+ parentSpanForTurn(event) {
23935
+ if (event.purpose === "compaction" || event.purpose === "compaction_prefix") {
23936
+ const compaction = this.compactionsByKey.get(compactionKey(event));
23937
+ if (compaction) {
23938
+ return compaction.span;
24307
23939
  }
24308
23940
  }
24309
- return void 0;
23941
+ return this.parentSpanForEvent(event);
24310
23942
  }
24311
- finishCompactionsForOperation(operationState) {
24312
- for (const state of [...this.compactionsByScope.values()]) {
24313
- if (state.operationState !== operationState) {
24314
- continue;
23943
+ parentSpanForEvent(event) {
23944
+ const turn = turnKey(event);
23945
+ if (turn) {
23946
+ const turnState = this.turnsByKey.get(turn);
23947
+ if (turnState) {
23948
+ return turnState.span;
24315
23949
  }
24316
- safeLog3(state.span, {
24317
- input: state.input,
24318
- metadata: state.metadata,
24319
- metrics: {
24320
- ...buildDurationMetrics3(state.startTime)
24321
- },
24322
- output: { completed: true }
24323
- });
24324
- state.span.end();
24325
- this.deleteCompactionState(state);
24326
23950
  }
24327
- }
24328
- deleteCompactionState(state) {
24329
- for (const [key, candidate] of this.compactionsByScope) {
24330
- if (candidate !== state) {
24331
- continue;
23951
+ if (event.taskId) {
23952
+ const task = this.tasksById.get(event.taskId);
23953
+ if (task) {
23954
+ return task.span;
24332
23955
  }
24333
- this.compactionsByScope.delete(key);
24334
- return;
24335
23956
  }
24336
- }
24337
- startSyntheticToolState(event, toolName) {
24338
- const parent = this.parentSpanForEvent(event);
24339
- const metadata = {
24340
- ...extractEventMetadata(event),
24341
- ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
24342
- "flue.tool_name": toolName,
24343
- provider: "flue"
24344
- };
24345
- const span = startFlueSpan(parent, {
24346
- name: `tool: ${toolName}`,
24347
- spanAttributes: { type: "tool" /* TOOL */ }
24348
- });
24349
- safeLog3(span, { metadata });
24350
- return { metadata, span, startTime: getCurrentUnixTimestamp() };
24351
- }
24352
- operationStateForEvent(event) {
24353
23957
  if (event.operationId) {
24354
- const operation = this.activeOperationsById.get(event.operationId) ?? this.promotePendingOperationForEvent(event);
23958
+ const operation = this.operationsById.get(event.operationId);
24355
23959
  if (operation) {
24356
- return operation;
23960
+ return operation.span;
24357
23961
  }
24358
23962
  }
24359
- return this.activeOperationForEventScope(event) ?? this.pendingOperationForEventScope(event);
23963
+ if (event.runId) {
23964
+ return this.runsById.get(event.runId)?.span;
23965
+ }
23966
+ return void 0;
24360
23967
  }
24361
- parentSpanForEvent(event) {
23968
+ parentSpanForTool(event) {
23969
+ if (event.taskId) {
23970
+ const task = this.tasksById.get(event.taskId);
23971
+ if (task) {
23972
+ return task.span;
23973
+ }
23974
+ }
24362
23975
  if (event.operationId) {
24363
- const operation = this.operationStateForEvent(event);
23976
+ const operation = this.operationsById.get(event.operationId);
24364
23977
  if (operation) {
24365
23978
  return operation.span;
24366
23979
  }
24367
23980
  }
24368
- if (event.taskId) {
24369
- return this.tasksById.get(event.taskId)?.span;
23981
+ if (event.runId) {
23982
+ return this.runsById.get(event.runId)?.span;
24370
23983
  }
24371
- return this.operationStateForEvent(event)?.span;
23984
+ return void 0;
24372
23985
  }
24373
- promotePendingOperationForEvent(event) {
24374
- if (!event.operationId) {
24375
- return void 0;
23986
+ logOperationInput(operationId, input) {
23987
+ if (!operationId || input === void 0) {
23988
+ return;
23989
+ }
23990
+ const operation = this.operationsById.get(operationId);
23991
+ if (!operation || operation.loggedInput) {
23992
+ return;
23993
+ }
23994
+ safeLog3(operation.span, { input });
23995
+ operation.loggedInput = true;
23996
+ }
23997
+ startSyntheticOperation(event) {
23998
+ const metadata = {
23999
+ ...extractEventMetadata(event),
24000
+ "flue.operation": event.operationKind,
24001
+ provider: "flue"
24002
+ };
24003
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
24004
+ name: `flue.${event.operationKind}`,
24005
+ spanAttributes: { type: "task" /* TASK */ },
24006
+ startTime: eventTime(event.timestamp),
24007
+ event: { metadata }
24008
+ });
24009
+ return { metadata, span };
24010
+ }
24011
+ startSyntheticTurn(event) {
24012
+ const metadata = {
24013
+ ...extractEventMetadata(event),
24014
+ ...event.api ? { "flue.api": event.api } : {},
24015
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
24016
+ ...event.provider ? { provider: event.provider } : { provider: "flue" },
24017
+ ...event.provider ? { "flue.provider": event.provider } : {},
24018
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {}
24019
+ };
24020
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
24021
+ name: `llm:${event.model ?? event.purpose ?? "unknown"}`,
24022
+ spanAttributes: { type: "llm" /* LLM */ },
24023
+ startTime: eventTime(event.timestamp),
24024
+ event: { metadata }
24025
+ });
24026
+ return { metadata, span };
24027
+ }
24028
+ startSyntheticTool(event) {
24029
+ const metadata = {
24030
+ ...extractEventMetadata(event),
24031
+ ...event.toolName ? { "flue.tool_name": event.toolName } : {},
24032
+ "flue.tool_call_id": event.toolCallId,
24033
+ provider: "flue"
24034
+ };
24035
+ const span = startFlueSpan(this.parentSpanForTool(event), {
24036
+ name: `tool:${event.toolName ?? "unknown"}`,
24037
+ spanAttributes: { type: "tool" /* TOOL */ },
24038
+ startTime: eventTime(event.timestamp),
24039
+ event: { metadata }
24040
+ });
24041
+ return { metadata, span };
24042
+ }
24043
+ startSyntheticCompaction(event) {
24044
+ const metadata = {
24045
+ ...extractEventMetadata(event),
24046
+ provider: "flue"
24047
+ };
24048
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
24049
+ name: "compaction:unknown",
24050
+ spanAttributes: { type: "task" /* TASK */ },
24051
+ startTime: eventTime(event.timestamp),
24052
+ event: { metadata }
24053
+ });
24054
+ return { metadata, span };
24055
+ }
24056
+ finishPendingChildrenForOperation(event, operationOutput2) {
24057
+ const endTime = eventTime(event.timestamp);
24058
+ const usage = event.usage ?? usageFromOperationResult(event.result);
24059
+ const turnEntries = [...this.turnsByKey].filter(
24060
+ ([, state]) => stateMatchesOperation(state, event.operationId)
24061
+ );
24062
+ turnEntries.forEach(([key, state], index) => {
24063
+ const shouldLogOperationOutput = (event.operationKind === "prompt" || event.operationKind === "skill") && index === turnEntries.length - 1 && operationOutput2 !== void 0;
24064
+ safeLog3(state.span, {
24065
+ metadata: state.metadata,
24066
+ metrics: metricsFromUsage(usage),
24067
+ ...shouldLogOperationOutput ? { output: operationOutput2 } : {}
24068
+ });
24069
+ safeEnd(state.span, endTime);
24070
+ this.turnsByKey.delete(key);
24071
+ });
24072
+ for (const [key, state] of this.toolsByKey) {
24073
+ if (!stateMatchesOperation(state, event.operationId)) {
24074
+ continue;
24075
+ }
24076
+ safeEnd(state.span, endTime);
24077
+ this.toolsByKey.delete(key);
24376
24078
  }
24377
- const scopePrefixes = operationScopePrefixes(event);
24378
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
24379
- if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
24079
+ for (const [key, state] of this.tasksById) {
24080
+ if (!stateMatchesOperation(state, event.operationId)) {
24380
24081
  continue;
24381
24082
  }
24382
- const state = candidateQueue.shift();
24383
- if (!state) {
24384
- return void 0;
24083
+ safeEnd(state.span, endTime);
24084
+ this.tasksById.delete(key);
24085
+ }
24086
+ for (const [key, state] of this.compactionsByKey) {
24087
+ if (!stateMatchesOperation(state, event.operationId)) {
24088
+ continue;
24385
24089
  }
24386
- state.operationId = event.operationId;
24387
- this.activeOperationsById.set(event.operationId, state);
24388
- addScopedOperation(this.activeOperationsByScope, event, state);
24389
- state.metadata = {
24390
- ...state.metadata,
24391
- ...extractEventMetadata(event),
24392
- "flue.operation_id": event.operationId
24393
- };
24394
- safeLog3(state.span, { metadata: state.metadata });
24395
- return state;
24090
+ safeLog3(state.span, {
24091
+ metadata: state.metadata,
24092
+ metrics: durationMetrics2(event.durationMs),
24093
+ output: { completed: true }
24094
+ });
24095
+ safeEnd(state.span, eventTime(event.timestamp));
24096
+ this.compactionsByKey.delete(key);
24396
24097
  }
24397
- return void 0;
24398
24098
  }
24399
- activeOperationForEventScope(event) {
24400
- for (const scope of operationScopeNames(event)) {
24401
- const operations = this.activeOperationsByScope.get(scope);
24402
- if (operations?.length) {
24403
- return operations[operations.length - 1];
24099
+ finishPendingSpansForRun(event) {
24100
+ const endTime = eventTime(event.timestamp);
24101
+ for (const [key, state] of this.toolsByKey) {
24102
+ if (!stateMatchesRun(state, event.runId)) {
24103
+ continue;
24404
24104
  }
24105
+ safeEnd(state.span, endTime);
24106
+ this.toolsByKey.delete(key);
24405
24107
  }
24406
- return void 0;
24407
- }
24408
- pendingOperationForEventScope(event) {
24409
- const scopePrefixes = operationScopePrefixes(event);
24410
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
24411
- if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
24108
+ for (const [key, state] of this.turnsByKey) {
24109
+ if (!stateMatchesRun(state, event.runId)) {
24412
24110
  continue;
24413
24111
  }
24414
- return candidateQueue[0];
24112
+ safeEnd(state.span, endTime);
24113
+ this.turnsByKey.delete(key);
24415
24114
  }
24416
- return void 0;
24417
- }
24418
- takePendingOperationForEvent(event) {
24419
- const key = operationKey(event.session, event.operationKind);
24420
- const queue2 = this.pendingOperationsByKey.get(key);
24421
- if (queue2?.length) {
24422
- return queue2.shift();
24115
+ for (const [key, state] of this.tasksById) {
24116
+ if (!stateMatchesRun(state, event.runId)) {
24117
+ continue;
24118
+ }
24119
+ safeEnd(state.span, endTime);
24120
+ this.tasksById.delete(key);
24423
24121
  }
24424
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
24425
- if (candidateKey.endsWith(`::${event.operationKind}`) && candidateQueue.length) {
24426
- return candidateQueue.shift();
24122
+ for (const [key, state] of this.compactionsByKey) {
24123
+ if (!stateMatchesRun(state, event.runId)) {
24124
+ continue;
24427
24125
  }
24126
+ safeLog3(state.span, {
24127
+ metadata: state.metadata,
24128
+ output: { completed: true }
24129
+ });
24130
+ safeEnd(state.span, endTime);
24131
+ this.compactionsByKey.delete(key);
24428
24132
  }
24429
- return void 0;
24430
- }
24431
- pendingOperationQueue(key) {
24432
- const existing = this.pendingOperationsByKey.get(key);
24433
- if (existing) {
24434
- return existing;
24133
+ for (const [key, state] of this.operationsById) {
24134
+ if (!stateMatchesRun(state, event.runId)) {
24135
+ continue;
24136
+ }
24137
+ safeLog3(state.span, {
24138
+ metadata: state.metadata,
24139
+ ...state.metadata["flue.operation"] === "compact" ? { output: { completed: true } } : {}
24140
+ });
24141
+ safeEnd(state.span, endTime);
24142
+ this.operationsById.delete(key);
24435
24143
  }
24436
- const queue2 = [];
24437
- this.pendingOperationsByKey.set(key, queue2);
24438
- return queue2;
24439
24144
  }
24440
24145
  };
24441
24146
  function isInstrumentedOperation(operation) {
24442
- return operation === "prompt" || operation === "skill" || operation === "task" || operation === "compact";
24443
- }
24444
- function getSessionName(session) {
24445
- return typeof session?.name === "string" ? session.name : void 0;
24446
- }
24447
- function operationKey(sessionName, operation) {
24448
- return `${sessionName ?? "unknown"}::${operation}`;
24449
- }
24450
- function operationScopePrefixes(event) {
24451
- const scopes = /* @__PURE__ */ new Set();
24452
- for (const scope of operationScopeNames(event)) {
24453
- scopes.add(`${scope}::`);
24454
- }
24455
- return scopes;
24456
- }
24457
- function operationKeyMatchesScopes(key, scopes) {
24458
- for (const scope of scopes) {
24459
- if (key.startsWith(scope)) {
24460
- return true;
24461
- }
24462
- }
24463
- return false;
24464
- }
24465
- function operationScopeNames(event) {
24466
- const scopes = /* @__PURE__ */ new Set();
24467
- if (event.session) {
24468
- scopes.add(event.session);
24469
- }
24470
- if (event.parentSession) {
24471
- scopes.add(event.parentSession);
24472
- }
24473
- if (!scopes.size) {
24474
- scopes.add("unknown");
24475
- }
24476
- return scopes;
24477
- }
24478
- function addScopedOperation(operationsByScope, event, state) {
24479
- for (const scope of operationScopeNames(event)) {
24480
- addOperationToScope(operationsByScope, scope, state);
24481
- }
24482
- }
24483
- function addOperationToScope(operationsByScope, scope, state) {
24484
- const operations = operationsByScope.get(scope);
24485
- if (operations) {
24486
- if (!operations.includes(state)) {
24487
- operations.push(state);
24488
- }
24489
- } else {
24490
- operationsByScope.set(scope, [state]);
24491
- }
24492
- }
24493
- function removeScopedOperation(operationsByScope, state) {
24494
- for (const [scope, operations] of operationsByScope) {
24495
- const index = operations.indexOf(state);
24496
- if (index === -1) {
24497
- continue;
24498
- }
24499
- operations.splice(index, 1);
24500
- if (operations.length === 0) {
24501
- operationsByScope.delete(scope);
24502
- }
24503
- }
24504
- }
24505
- function removePendingOperation(pendingOperationsByKey, state) {
24506
- for (const [key, queue2] of pendingOperationsByKey) {
24507
- const index = queue2.indexOf(state);
24508
- if (index === -1) {
24509
- continue;
24510
- }
24511
- queue2.splice(index, 1);
24512
- if (queue2.length === 0) {
24513
- pendingOperationsByKey.delete(key);
24514
- }
24515
- return;
24516
- }
24147
+ return operation === "prompt" || operation === "skill" || operation === "compact";
24517
24148
  }
24518
- function extractSessionMetadata(session) {
24519
- const sessionName = getSessionName(session);
24520
- return sessionName ? { "flue.session": sessionName } : {};
24521
- }
24522
- function extractEventMetadata(event) {
24149
+ function extractEventMetadata(event, ctx) {
24523
24150
  return {
24524
24151
  ...event.runId ? { "flue.run_id": event.runId } : {},
24152
+ ...event.instanceId ? { "flue.instance_id": event.instanceId } : {},
24153
+ ...event.dispatchId ? { "flue.dispatch_id": event.dispatchId } : {},
24525
24154
  ...typeof event.eventIndex === "number" ? { "flue.event_index": event.eventIndex } : {},
24526
24155
  ...event.session ? { "flue.session": event.session } : {},
24527
24156
  ...event.parentSession ? { "flue.parent_session": event.parentSession } : {},
24528
24157
  ...event.harness ? { "flue.harness": event.harness } : {},
24529
24158
  ...event.taskId ? { "flue.task_id": event.taskId } : {},
24530
- ...event.operationId ? { "flue.operation_id": event.operationId } : {}
24159
+ ...event.operationId ? { "flue.operation_id": event.operationId } : {},
24160
+ ...event.turnId ? { "flue.turn_id": event.turnId } : {},
24161
+ ...typeof ctx?.id === "string" ? { "flue.context_id": ctx.id } : {},
24162
+ ...typeof ctx?.runId === "string" ? { "flue.context_run_id": ctx.runId } : {}
24531
24163
  };
24532
24164
  }
24533
- function extractOperationInput(operation, args) {
24534
- switch (operation) {
24535
- case "prompt":
24536
- case "task":
24537
- return args[0];
24538
- case "skill":
24539
- return {
24540
- args: getOptionObject(args[1])?.args,
24541
- name: args[0]
24542
- };
24543
- case "compact":
24544
- return void 0;
24165
+ function extractPayloadMetadata(payload) {
24166
+ if (!isObjectLike(payload)) {
24167
+ return {};
24545
24168
  }
24169
+ const metadata = Reflect.get(payload, "metadata");
24170
+ if (!isObjectLike(metadata)) {
24171
+ return {};
24172
+ }
24173
+ return Object.fromEntries(Object.entries(metadata));
24546
24174
  }
24547
- function extractOperationInputMetadata(operation, args) {
24548
- const options = getOptionObject(args[1]);
24549
- return {
24550
- ...operation === "skill" && typeof args[0] === "string" ? { "flue.skill_name": args[0] } : {},
24551
- ...options?.model ? { model: options.model, "flue.model": options.model } : {},
24552
- ...options?.role ? { "flue.role": options.role } : {},
24553
- ...options?.thinkingLevel ? { "flue.thinking_level": options.thinkingLevel } : {},
24554
- ...typeof options?.cwd === "string" ? { "flue.cwd": options.cwd } : {},
24555
- ...Array.isArray(options?.tools) ? {
24556
- "flue.tools_count": options.tools.length,
24557
- tools: summarizeTools(options.tools)
24558
- } : {},
24559
- ...Array.isArray(options?.images) ? { "flue.images_count": options.images.length } : {},
24560
- ...options?.result || options?.schema ? { "flue.result_schema": true } : {}
24561
- };
24562
- }
24563
- function getOptionObject(value) {
24564
- return isObject(value) ? value : void 0;
24565
- }
24566
- function summarizeTools(tools) {
24567
- return tools.flatMap((tool) => {
24568
- if (!isObject(tool)) {
24569
- return [];
24570
- }
24571
- const name = typeof tool.name === "string" ? tool.name : void 0;
24572
- if (!name) {
24573
- return [];
24574
- }
24575
- return [
24576
- {
24577
- function: {
24578
- description: typeof tool.description === "string" ? tool.description : void 0,
24579
- name,
24580
- parameters: tool.parameters
24581
- },
24582
- type: "function"
24583
- }
24584
- ];
24585
- });
24175
+ function operationOutput(event) {
24176
+ if (event.operationKind === "prompt" || event.operationKind === "skill") {
24177
+ return llmResultFromOperationResult(event.result);
24178
+ }
24179
+ return event.result ?? (event.operationKind === "compact" ? { completed: true } : void 0);
24586
24180
  }
24587
- function extractPromptResponseMetadata(result) {
24588
- const modelId = result?.model && typeof result.model.id === "string" ? result.model.id : void 0;
24589
- return modelId ? {
24590
- model: modelId,
24591
- "flue.model": modelId
24592
- } : {};
24181
+ function llmResultFromOperationResult(result) {
24182
+ if (!isObjectLike(result)) {
24183
+ return result;
24184
+ }
24185
+ const text = Reflect.get(result, "text");
24186
+ return text === void 0 ? result : text;
24593
24187
  }
24594
- function extractOperationOutput(result) {
24595
- if (!result) {
24188
+ function usageFromOperationResult(result) {
24189
+ if (!isObjectLike(result)) {
24596
24190
  return void 0;
24597
24191
  }
24598
- if ("data" in result) {
24599
- return result.data;
24600
- }
24601
- if ("text" in result) {
24602
- return result.text;
24603
- }
24604
- return result;
24192
+ return Reflect.get(result, "usage");
24605
24193
  }
24606
24194
  function metricsFromUsage(usage) {
24195
+ if (!isObjectLike(usage)) {
24196
+ return {};
24197
+ }
24198
+ const cacheRead = Reflect.get(usage, "cacheRead");
24199
+ const cacheWrite = Reflect.get(usage, "cacheWrite");
24200
+ const cost = Reflect.get(usage, "cost");
24201
+ const input = Reflect.get(usage, "input");
24202
+ const output = Reflect.get(usage, "output");
24203
+ const totalTokens = Reflect.get(usage, "totalTokens");
24204
+ const totalCost = isObjectLike(cost) ? Reflect.get(cost, "total") : void 0;
24607
24205
  return {
24608
- ...typeof usage?.input === "number" ? { prompt_tokens: usage.input } : {},
24609
- ...typeof usage?.output === "number" ? { completion_tokens: usage.output } : {},
24610
- ...typeof usage?.cacheRead === "number" ? { prompt_cached_tokens: usage.cacheRead } : {},
24611
- ...typeof usage?.cacheWrite === "number" ? { prompt_cache_creation_tokens: usage.cacheWrite } : {},
24612
- ...typeof usage?.totalTokens === "number" ? { tokens: usage.totalTokens } : {},
24613
- ...typeof usage?.cost?.total === "number" ? { estimated_cost: usage.cost.total } : {}
24614
- };
24615
- }
24616
- function buildDurationMetrics3(startTime) {
24617
- return {
24618
- duration_ms: Math.max(0, (getCurrentUnixTimestamp() - startTime) * 1e3)
24206
+ ...typeof input === "number" ? { prompt_tokens: input } : {},
24207
+ ...typeof output === "number" ? { completion_tokens: output } : {},
24208
+ ...typeof cacheRead === "number" ? { prompt_cached_tokens: cacheRead } : {},
24209
+ ...typeof cacheWrite === "number" ? { prompt_cache_creation_tokens: cacheWrite } : {},
24210
+ ...typeof totalTokens === "number" ? { tokens: totalTokens } : {},
24211
+ ...typeof totalCost === "number" ? { estimated_cost: totalCost } : {}
24619
24212
  };
24620
24213
  }
24621
- function durationMsMetrics(durationMs) {
24214
+ function durationMetrics2(durationMs) {
24622
24215
  return typeof durationMs === "number" ? { duration_ms: durationMs } : {};
24623
24216
  }
24624
- function scopeKey(event) {
24625
- if (event.operationId) {
24626
- return `operation:${event.operationId}`;
24627
- }
24628
- if (event.taskId) {
24629
- return `task:${event.taskId}`;
24630
- }
24631
- if (event.session) {
24632
- return `session:${event.session}`;
24217
+ function eventTime(value) {
24218
+ if (typeof value !== "string") {
24219
+ return void 0;
24633
24220
  }
24634
- return "flue:unknown";
24221
+ const timestamp = Date.parse(value);
24222
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
24223
+ }
24224
+ function turnKey(event) {
24225
+ return event.turnId;
24635
24226
  }
24636
24227
  function toolKey(event) {
24637
- return `${scopeKey(event)}::tool:${event.toolCallId ?? "unknown"}`;
24228
+ return `${event.turnId ?? event.operationId ?? event.taskId ?? event.runId ?? "unknown"}:${event.toolCallId ?? "unknown"}`;
24638
24229
  }
24639
- function toAssistantOutput(text, finishReason, reasoning, toolCalls) {
24230
+ function compactionKey(event) {
24640
24231
  return [
24641
- {
24642
- finish_reason: finishReason ?? "stop",
24643
- index: 0,
24644
- message: {
24645
- content: text,
24646
- ...reasoning ? { reasoning } : {},
24647
- role: "assistant",
24648
- ...toolCalls?.length ? {
24649
- tool_calls: toolCalls.map((toolCall) => ({
24650
- function: {
24651
- arguments: toolCall.args === void 0 ? "{}" : JSON.stringify(toolCall.args),
24652
- name: toolCall.toolName ?? "unknown"
24653
- },
24654
- ...toolCall.toolCallId ? { id: toolCall.toolCallId } : {},
24655
- type: "function"
24656
- }))
24657
- } : {}
24658
- }
24659
- }
24660
- ];
24232
+ event.instanceId ?? "",
24233
+ event.runId ?? "",
24234
+ event.session ?? "",
24235
+ event.operationId ?? "",
24236
+ event.taskId ?? ""
24237
+ ].join(":");
24238
+ }
24239
+ function stateMatchesOperation(state, operationId) {
24240
+ return state.metadata["flue.operation_id"] === operationId;
24241
+ }
24242
+ function stateMatchesRun(state, runId) {
24243
+ return state.metadata["flue.run_id"] === runId;
24661
24244
  }
24662
24245
  function startFlueSpan(parent, args) {
24663
24246
  return parent ? withCurrent(parent, () => startSpan(args)) : startSpan(args);
@@ -24669,6 +24252,13 @@ function safeLog3(span, event) {
24669
24252
  logInstrumentationError3("Flue span log", error);
24670
24253
  }
24671
24254
  }
24255
+ function safeEnd(span, endTime) {
24256
+ try {
24257
+ span.end(endTime === void 0 ? void 0 : { endTime });
24258
+ } catch (error) {
24259
+ logInstrumentationError3("Flue span end", error);
24260
+ }
24261
+ }
24672
24262
  function errorToString(error) {
24673
24263
  if (error instanceof Error) {
24674
24264
  return error.message;