@firstflow/core 0.0.6 → 0.0.9

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.
@@ -0,0 +1,1661 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/langchain.ts
21
+ var langchain_exports = {};
22
+ __export(langchain_exports, {
23
+ FirstflowCallbackHandler: () => FirstflowCallbackHandler,
24
+ firstflowCallback: () => firstflowCallback,
25
+ withFirstflow: () => withFirstflow
26
+ });
27
+ module.exports = __toCommonJS(langchain_exports);
28
+
29
+ // node_modules/tsup/assets/cjs_shims.js
30
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
31
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
32
+
33
+ // src/langchain.ts
34
+ var import_base = require("@langchain/core/callbacks/base");
35
+
36
+ // src/runtime.ts
37
+ var import_sdk_node = require("@opentelemetry/sdk-node");
38
+ var import_resources = require("@opentelemetry/resources");
39
+ var import_instrumentation_openai = require("@opentelemetry/instrumentation-openai");
40
+ var import_instrumentation_anthropic = require("@traceloop/instrumentation-anthropic");
41
+
42
+ // node_modules/@opentelemetry/api/build/esm/version.js
43
+ var VERSION = "1.9.1";
44
+
45
+ // node_modules/@opentelemetry/api/build/esm/internal/semver.js
46
+ var re = /^(\d+)\.(\d+)\.(\d+)(-(.+))?$/;
47
+ function _makeCompatibilityCheck(ownVersion) {
48
+ const acceptedVersions = /* @__PURE__ */ new Set([ownVersion]);
49
+ const rejectedVersions = /* @__PURE__ */ new Set();
50
+ const myVersionMatch = ownVersion.match(re);
51
+ if (!myVersionMatch) {
52
+ return () => false;
53
+ }
54
+ const ownVersionParsed = {
55
+ major: +myVersionMatch[1],
56
+ minor: +myVersionMatch[2],
57
+ patch: +myVersionMatch[3],
58
+ prerelease: myVersionMatch[4]
59
+ };
60
+ if (ownVersionParsed.prerelease != null) {
61
+ return function isExactmatch(globalVersion) {
62
+ return globalVersion === ownVersion;
63
+ };
64
+ }
65
+ function _reject(v) {
66
+ rejectedVersions.add(v);
67
+ return false;
68
+ }
69
+ function _accept(v) {
70
+ acceptedVersions.add(v);
71
+ return true;
72
+ }
73
+ return function isCompatible2(globalVersion) {
74
+ if (acceptedVersions.has(globalVersion)) {
75
+ return true;
76
+ }
77
+ if (rejectedVersions.has(globalVersion)) {
78
+ return false;
79
+ }
80
+ const globalVersionMatch = globalVersion.match(re);
81
+ if (!globalVersionMatch) {
82
+ return _reject(globalVersion);
83
+ }
84
+ const globalVersionParsed = {
85
+ major: +globalVersionMatch[1],
86
+ minor: +globalVersionMatch[2],
87
+ patch: +globalVersionMatch[3],
88
+ prerelease: globalVersionMatch[4]
89
+ };
90
+ if (globalVersionParsed.prerelease != null) {
91
+ return _reject(globalVersion);
92
+ }
93
+ if (ownVersionParsed.major !== globalVersionParsed.major) {
94
+ return _reject(globalVersion);
95
+ }
96
+ if (ownVersionParsed.major === 0) {
97
+ if (ownVersionParsed.minor === globalVersionParsed.minor && ownVersionParsed.patch <= globalVersionParsed.patch) {
98
+ return _accept(globalVersion);
99
+ }
100
+ return _reject(globalVersion);
101
+ }
102
+ if (ownVersionParsed.minor <= globalVersionParsed.minor) {
103
+ return _accept(globalVersion);
104
+ }
105
+ return _reject(globalVersion);
106
+ };
107
+ }
108
+ var isCompatible = _makeCompatibilityCheck(VERSION);
109
+
110
+ // node_modules/@opentelemetry/api/build/esm/internal/global-utils.js
111
+ var major = VERSION.split(".")[0];
112
+ var GLOBAL_OPENTELEMETRY_API_KEY = /* @__PURE__ */ Symbol.for(`opentelemetry.js.api.${major}`);
113
+ var _global = typeof globalThis === "object" ? globalThis : typeof self === "object" ? self : typeof window === "object" ? window : typeof global === "object" ? global : {};
114
+ function registerGlobal(type, instance, diag2, allowOverride = false) {
115
+ var _a;
116
+ const api = _global[GLOBAL_OPENTELEMETRY_API_KEY] = (_a = _global[GLOBAL_OPENTELEMETRY_API_KEY]) !== null && _a !== void 0 ? _a : {
117
+ version: VERSION
118
+ };
119
+ if (!allowOverride && api[type]) {
120
+ const err = new Error(`@opentelemetry/api: Attempted duplicate registration of API: ${type}`);
121
+ diag2.error(err.stack || err.message);
122
+ return false;
123
+ }
124
+ if (api.version !== VERSION) {
125
+ const err = new Error(`@opentelemetry/api: Registration of version v${api.version} for ${type} does not match previously registered API v${VERSION}`);
126
+ diag2.error(err.stack || err.message);
127
+ return false;
128
+ }
129
+ api[type] = instance;
130
+ diag2.debug(`@opentelemetry/api: Registered a global for ${type} v${VERSION}.`);
131
+ return true;
132
+ }
133
+ function getGlobal(type) {
134
+ var _a, _b;
135
+ const globalVersion = (_a = _global[GLOBAL_OPENTELEMETRY_API_KEY]) === null || _a === void 0 ? void 0 : _a.version;
136
+ if (!globalVersion || !isCompatible(globalVersion)) {
137
+ return;
138
+ }
139
+ return (_b = _global[GLOBAL_OPENTELEMETRY_API_KEY]) === null || _b === void 0 ? void 0 : _b[type];
140
+ }
141
+ function unregisterGlobal(type, diag2) {
142
+ diag2.debug(`@opentelemetry/api: Unregistering a global for ${type} v${VERSION}.`);
143
+ const api = _global[GLOBAL_OPENTELEMETRY_API_KEY];
144
+ if (api) {
145
+ delete api[type];
146
+ }
147
+ }
148
+
149
+ // node_modules/@opentelemetry/api/build/esm/diag/ComponentLogger.js
150
+ var DiagComponentLogger = class {
151
+ constructor(props) {
152
+ this._namespace = props.namespace || "DiagComponentLogger";
153
+ }
154
+ debug(...args) {
155
+ return logProxy("debug", this._namespace, args);
156
+ }
157
+ error(...args) {
158
+ return logProxy("error", this._namespace, args);
159
+ }
160
+ info(...args) {
161
+ return logProxy("info", this._namespace, args);
162
+ }
163
+ warn(...args) {
164
+ return logProxy("warn", this._namespace, args);
165
+ }
166
+ verbose(...args) {
167
+ return logProxy("verbose", this._namespace, args);
168
+ }
169
+ };
170
+ function logProxy(funcName, namespace, args) {
171
+ const logger = getGlobal("diag");
172
+ if (!logger) {
173
+ return;
174
+ }
175
+ return logger[funcName](namespace, ...args);
176
+ }
177
+
178
+ // node_modules/@opentelemetry/api/build/esm/diag/types.js
179
+ var DiagLogLevel;
180
+ (function(DiagLogLevel2) {
181
+ DiagLogLevel2[DiagLogLevel2["NONE"] = 0] = "NONE";
182
+ DiagLogLevel2[DiagLogLevel2["ERROR"] = 30] = "ERROR";
183
+ DiagLogLevel2[DiagLogLevel2["WARN"] = 50] = "WARN";
184
+ DiagLogLevel2[DiagLogLevel2["INFO"] = 60] = "INFO";
185
+ DiagLogLevel2[DiagLogLevel2["DEBUG"] = 70] = "DEBUG";
186
+ DiagLogLevel2[DiagLogLevel2["VERBOSE"] = 80] = "VERBOSE";
187
+ DiagLogLevel2[DiagLogLevel2["ALL"] = 9999] = "ALL";
188
+ })(DiagLogLevel || (DiagLogLevel = {}));
189
+
190
+ // node_modules/@opentelemetry/api/build/esm/diag/internal/logLevelLogger.js
191
+ function createLogLevelDiagLogger(maxLevel, logger) {
192
+ if (maxLevel < DiagLogLevel.NONE) {
193
+ maxLevel = DiagLogLevel.NONE;
194
+ } else if (maxLevel > DiagLogLevel.ALL) {
195
+ maxLevel = DiagLogLevel.ALL;
196
+ }
197
+ logger = logger || {};
198
+ function _filterFunc(funcName, theLevel) {
199
+ const theFunc = logger[funcName];
200
+ if (typeof theFunc === "function" && maxLevel >= theLevel) {
201
+ return theFunc.bind(logger);
202
+ }
203
+ return function() {
204
+ };
205
+ }
206
+ return {
207
+ error: _filterFunc("error", DiagLogLevel.ERROR),
208
+ warn: _filterFunc("warn", DiagLogLevel.WARN),
209
+ info: _filterFunc("info", DiagLogLevel.INFO),
210
+ debug: _filterFunc("debug", DiagLogLevel.DEBUG),
211
+ verbose: _filterFunc("verbose", DiagLogLevel.VERBOSE)
212
+ };
213
+ }
214
+
215
+ // node_modules/@opentelemetry/api/build/esm/api/diag.js
216
+ var API_NAME = "diag";
217
+ var DiagAPI = class _DiagAPI {
218
+ /** Get the singleton instance of the DiagAPI API */
219
+ static instance() {
220
+ if (!this._instance) {
221
+ this._instance = new _DiagAPI();
222
+ }
223
+ return this._instance;
224
+ }
225
+ /**
226
+ * Private internal constructor
227
+ * @private
228
+ */
229
+ constructor() {
230
+ function _logProxy(funcName) {
231
+ return function(...args) {
232
+ const logger = getGlobal("diag");
233
+ if (!logger)
234
+ return;
235
+ return logger[funcName](...args);
236
+ };
237
+ }
238
+ const self2 = this;
239
+ const setLogger = (logger, optionsOrLogLevel = { logLevel: DiagLogLevel.INFO }) => {
240
+ var _a, _b, _c;
241
+ if (logger === self2) {
242
+ const err = new Error("Cannot use diag as the logger for itself. Please use a DiagLogger implementation like ConsoleDiagLogger or a custom implementation");
243
+ self2.error((_a = err.stack) !== null && _a !== void 0 ? _a : err.message);
244
+ return false;
245
+ }
246
+ if (typeof optionsOrLogLevel === "number") {
247
+ optionsOrLogLevel = {
248
+ logLevel: optionsOrLogLevel
249
+ };
250
+ }
251
+ const oldLogger = getGlobal("diag");
252
+ const newLogger = createLogLevelDiagLogger((_b = optionsOrLogLevel.logLevel) !== null && _b !== void 0 ? _b : DiagLogLevel.INFO, logger);
253
+ if (oldLogger && !optionsOrLogLevel.suppressOverrideMessage) {
254
+ const stack = (_c = new Error().stack) !== null && _c !== void 0 ? _c : "<failed to generate stacktrace>";
255
+ oldLogger.warn(`Current logger will be overwritten from ${stack}`);
256
+ newLogger.warn(`Current logger will overwrite one already registered from ${stack}`);
257
+ }
258
+ return registerGlobal("diag", newLogger, self2, true);
259
+ };
260
+ self2.setLogger = setLogger;
261
+ self2.disable = () => {
262
+ unregisterGlobal(API_NAME, self2);
263
+ };
264
+ self2.createComponentLogger = (options) => {
265
+ return new DiagComponentLogger(options);
266
+ };
267
+ self2.verbose = _logProxy("verbose");
268
+ self2.debug = _logProxy("debug");
269
+ self2.info = _logProxy("info");
270
+ self2.warn = _logProxy("warn");
271
+ self2.error = _logProxy("error");
272
+ }
273
+ };
274
+
275
+ // node_modules/@opentelemetry/api/build/esm/baggage/internal/baggage-impl.js
276
+ var BaggageImpl = class _BaggageImpl {
277
+ constructor(entries) {
278
+ this._entries = entries ? new Map(entries) : /* @__PURE__ */ new Map();
279
+ }
280
+ getEntry(key) {
281
+ const entry = this._entries.get(key);
282
+ if (!entry) {
283
+ return void 0;
284
+ }
285
+ return Object.assign({}, entry);
286
+ }
287
+ getAllEntries() {
288
+ return Array.from(this._entries.entries());
289
+ }
290
+ setEntry(key, entry) {
291
+ const newBaggage = new _BaggageImpl(this._entries);
292
+ newBaggage._entries.set(key, entry);
293
+ return newBaggage;
294
+ }
295
+ removeEntry(key) {
296
+ const newBaggage = new _BaggageImpl(this._entries);
297
+ newBaggage._entries.delete(key);
298
+ return newBaggage;
299
+ }
300
+ removeEntries(...keys) {
301
+ const newBaggage = new _BaggageImpl(this._entries);
302
+ for (const key of keys) {
303
+ newBaggage._entries.delete(key);
304
+ }
305
+ return newBaggage;
306
+ }
307
+ clear() {
308
+ return new _BaggageImpl();
309
+ }
310
+ };
311
+
312
+ // node_modules/@opentelemetry/api/build/esm/baggage/utils.js
313
+ var diag = DiagAPI.instance();
314
+ function createBaggage(entries = {}) {
315
+ return new BaggageImpl(new Map(Object.entries(entries)));
316
+ }
317
+
318
+ // node_modules/@opentelemetry/api/build/esm/context/context.js
319
+ function createContextKey(description) {
320
+ return Symbol.for(description);
321
+ }
322
+ var BaseContext = class _BaseContext {
323
+ /**
324
+ * Construct a new context which inherits values from an optional parent context.
325
+ *
326
+ * @param parentContext a context from which to inherit values
327
+ */
328
+ constructor(parentContext) {
329
+ const self2 = this;
330
+ self2._currentContext = parentContext ? new Map(parentContext) : /* @__PURE__ */ new Map();
331
+ self2.getValue = (key) => self2._currentContext.get(key);
332
+ self2.setValue = (key, value) => {
333
+ const context = new _BaseContext(self2._currentContext);
334
+ context._currentContext.set(key, value);
335
+ return context;
336
+ };
337
+ self2.deleteValue = (key) => {
338
+ const context = new _BaseContext(self2._currentContext);
339
+ context._currentContext.delete(key);
340
+ return context;
341
+ };
342
+ }
343
+ };
344
+ var ROOT_CONTEXT = new BaseContext();
345
+
346
+ // node_modules/@opentelemetry/api/build/esm/propagation/TextMapPropagator.js
347
+ var defaultTextMapGetter = {
348
+ get(carrier, key) {
349
+ if (carrier == null) {
350
+ return void 0;
351
+ }
352
+ return carrier[key];
353
+ },
354
+ keys(carrier) {
355
+ if (carrier == null) {
356
+ return [];
357
+ }
358
+ return Object.keys(carrier);
359
+ }
360
+ };
361
+ var defaultTextMapSetter = {
362
+ set(carrier, key, value) {
363
+ if (carrier == null) {
364
+ return;
365
+ }
366
+ carrier[key] = value;
367
+ }
368
+ };
369
+
370
+ // node_modules/@opentelemetry/api/build/esm/context/NoopContextManager.js
371
+ var NoopContextManager = class {
372
+ active() {
373
+ return ROOT_CONTEXT;
374
+ }
375
+ with(_context, fn, thisArg, ...args) {
376
+ return fn.call(thisArg, ...args);
377
+ }
378
+ bind(_context, target) {
379
+ return target;
380
+ }
381
+ enable() {
382
+ return this;
383
+ }
384
+ disable() {
385
+ return this;
386
+ }
387
+ };
388
+
389
+ // node_modules/@opentelemetry/api/build/esm/api/context.js
390
+ var API_NAME2 = "context";
391
+ var NOOP_CONTEXT_MANAGER = new NoopContextManager();
392
+ var ContextAPI = class _ContextAPI {
393
+ /** Empty private constructor prevents end users from constructing a new instance of the API */
394
+ constructor() {
395
+ }
396
+ /** Get the singleton instance of the Context API */
397
+ static getInstance() {
398
+ if (!this._instance) {
399
+ this._instance = new _ContextAPI();
400
+ }
401
+ return this._instance;
402
+ }
403
+ /**
404
+ * Set the current context manager.
405
+ *
406
+ * @returns true if the context manager was successfully registered, else false
407
+ */
408
+ setGlobalContextManager(contextManager) {
409
+ return registerGlobal(API_NAME2, contextManager, DiagAPI.instance());
410
+ }
411
+ /**
412
+ * Get the currently active context
413
+ */
414
+ active() {
415
+ return this._getContextManager().active();
416
+ }
417
+ /**
418
+ * Execute a function with an active context
419
+ *
420
+ * @param context context to be active during function execution
421
+ * @param fn function to execute in a context
422
+ * @param thisArg optional receiver to be used for calling fn
423
+ * @param args optional arguments forwarded to fn
424
+ */
425
+ with(context, fn, thisArg, ...args) {
426
+ return this._getContextManager().with(context, fn, thisArg, ...args);
427
+ }
428
+ /**
429
+ * Bind a context to a target function or event emitter
430
+ *
431
+ * @param context context to bind to the event emitter or function. Defaults to the currently active context
432
+ * @param target function or event emitter to bind
433
+ */
434
+ bind(context, target) {
435
+ return this._getContextManager().bind(context, target);
436
+ }
437
+ _getContextManager() {
438
+ return getGlobal(API_NAME2) || NOOP_CONTEXT_MANAGER;
439
+ }
440
+ /** Disable and remove the global context manager */
441
+ disable() {
442
+ this._getContextManager().disable();
443
+ unregisterGlobal(API_NAME2, DiagAPI.instance());
444
+ }
445
+ };
446
+
447
+ // node_modules/@opentelemetry/api/build/esm/propagation/NoopTextMapPropagator.js
448
+ var NoopTextMapPropagator = class {
449
+ /** Noop inject function does nothing */
450
+ inject(_context, _carrier) {
451
+ }
452
+ /** Noop extract function does nothing and returns the input context */
453
+ extract(context, _carrier) {
454
+ return context;
455
+ }
456
+ fields() {
457
+ return [];
458
+ }
459
+ };
460
+
461
+ // node_modules/@opentelemetry/api/build/esm/baggage/context-helpers.js
462
+ var BAGGAGE_KEY = createContextKey("OpenTelemetry Baggage Key");
463
+ function getBaggage(context) {
464
+ return context.getValue(BAGGAGE_KEY) || void 0;
465
+ }
466
+ function getActiveBaggage() {
467
+ return getBaggage(ContextAPI.getInstance().active());
468
+ }
469
+ function setBaggage(context, baggage) {
470
+ return context.setValue(BAGGAGE_KEY, baggage);
471
+ }
472
+ function deleteBaggage(context) {
473
+ return context.deleteValue(BAGGAGE_KEY);
474
+ }
475
+
476
+ // node_modules/@opentelemetry/api/build/esm/api/propagation.js
477
+ var API_NAME3 = "propagation";
478
+ var NOOP_TEXT_MAP_PROPAGATOR = new NoopTextMapPropagator();
479
+ var PropagationAPI = class _PropagationAPI {
480
+ /** Empty private constructor prevents end users from constructing a new instance of the API */
481
+ constructor() {
482
+ this.createBaggage = createBaggage;
483
+ this.getBaggage = getBaggage;
484
+ this.getActiveBaggage = getActiveBaggage;
485
+ this.setBaggage = setBaggage;
486
+ this.deleteBaggage = deleteBaggage;
487
+ }
488
+ /** Get the singleton instance of the Propagator API */
489
+ static getInstance() {
490
+ if (!this._instance) {
491
+ this._instance = new _PropagationAPI();
492
+ }
493
+ return this._instance;
494
+ }
495
+ /**
496
+ * Set the current propagator.
497
+ *
498
+ * @returns true if the propagator was successfully registered, else false
499
+ */
500
+ setGlobalPropagator(propagator) {
501
+ return registerGlobal(API_NAME3, propagator, DiagAPI.instance());
502
+ }
503
+ /**
504
+ * Inject context into a carrier to be propagated inter-process
505
+ *
506
+ * @param context Context carrying tracing data to inject
507
+ * @param carrier carrier to inject context into
508
+ * @param setter Function used to set values on the carrier
509
+ */
510
+ inject(context, carrier, setter = defaultTextMapSetter) {
511
+ return this._getGlobalPropagator().inject(context, carrier, setter);
512
+ }
513
+ /**
514
+ * Extract context from a carrier
515
+ *
516
+ * @param context Context which the newly created context will inherit from
517
+ * @param carrier Carrier to extract context from
518
+ * @param getter Function used to extract keys from a carrier
519
+ */
520
+ extract(context, carrier, getter = defaultTextMapGetter) {
521
+ return this._getGlobalPropagator().extract(context, carrier, getter);
522
+ }
523
+ /**
524
+ * Return a list of all fields which may be used by the propagator.
525
+ */
526
+ fields() {
527
+ return this._getGlobalPropagator().fields();
528
+ }
529
+ /** Remove the global propagator */
530
+ disable() {
531
+ unregisterGlobal(API_NAME3, DiagAPI.instance());
532
+ }
533
+ _getGlobalPropagator() {
534
+ return getGlobal(API_NAME3) || NOOP_TEXT_MAP_PROPAGATOR;
535
+ }
536
+ };
537
+
538
+ // node_modules/@opentelemetry/api/build/esm/propagation-api.js
539
+ var propagation = PropagationAPI.getInstance();
540
+
541
+ // src/firstflow-span-processor.ts
542
+ var FLUSH_SIZE = 20;
543
+ var FLUSH_INTERVAL_MS = 2e3;
544
+ var REQUEST_TIMEOUT_MS = 1e4;
545
+ var MAX_QUEUE_SIZE = 500;
546
+ var AGENT_ID_ATTR = "firstflow.agent_id";
547
+ function spanToOtlpJson(span) {
548
+ const attrs = span.attributes;
549
+ const otlpAttrs = [];
550
+ for (const [key, val] of Object.entries(attrs)) {
551
+ if (val === void 0) continue;
552
+ if (key === AGENT_ID_ATTR) continue;
553
+ if (typeof val === "string") {
554
+ otlpAttrs.push({ key, value: { stringValue: val } });
555
+ } else if (typeof val === "number") {
556
+ if (Number.isInteger(val)) {
557
+ otlpAttrs.push({ key, value: { intValue: val } });
558
+ } else {
559
+ otlpAttrs.push({ key, value: { doubleValue: val } });
560
+ }
561
+ } else if (typeof val === "boolean") {
562
+ otlpAttrs.push({ key, value: { boolValue: val } });
563
+ }
564
+ }
565
+ const startNs = BigInt(span.startTime[0]) * BigInt(1e9) + BigInt(span.startTime[1]);
566
+ const endNs = BigInt(span.endTime[0]) * BigInt(1e9) + BigInt(span.endTime[1]);
567
+ const parentId = span.parentSpanId;
568
+ return {
569
+ traceId: span.spanContext().traceId,
570
+ spanId: span.spanContext().spanId,
571
+ parentSpanId: parentId || void 0,
572
+ name: span.name,
573
+ startTimeUnixNano: startNs.toString(),
574
+ endTimeUnixNano: endNs.toString(),
575
+ status: span.status ? { code: span.status.code, message: span.status.message } : void 0,
576
+ attributes: otlpAttrs
577
+ };
578
+ }
579
+ var FirstflowSpanProcessor = class {
580
+ constructor(opts) {
581
+ this.opts = opts;
582
+ }
583
+ opts;
584
+ /** Spans partitioned by agent id — partitioning happens at ingest, not flush. */
585
+ buffers = /* @__PURE__ */ new Map();
586
+ bufferedCount = 0;
587
+ flushTimer;
588
+ onStart(span, parentContext) {
589
+ const agentId = propagation.getBaggage(parentContext)?.getEntry(AGENT_ID_ATTR)?.value;
590
+ if (agentId) span.setAttribute(AGENT_ID_ATTR, agentId);
591
+ }
592
+ onEnd(span) {
593
+ const genAi = span.attributes["gen_ai.system"];
594
+ if (!genAi) return;
595
+ const agentId = span.attributes[AGENT_ID_ATTR];
596
+ if (typeof agentId !== "string" || !agentId) return;
597
+ let bucket = this.buffers.get(agentId);
598
+ if (!bucket) {
599
+ bucket = [];
600
+ this.buffers.set(agentId, bucket);
601
+ }
602
+ if (this.bufferedCount >= MAX_QUEUE_SIZE) {
603
+ const firstKey = this.buffers.keys().next().value;
604
+ if (firstKey !== void 0) {
605
+ const b = this.buffers.get(firstKey);
606
+ b.shift();
607
+ if (b.length === 0) this.buffers.delete(firstKey);
608
+ this.bufferedCount--;
609
+ }
610
+ }
611
+ bucket.push(span);
612
+ this.bufferedCount++;
613
+ if (this.bufferedCount >= FLUSH_SIZE) {
614
+ void this.flush();
615
+ } else {
616
+ this.scheduleFlush();
617
+ }
618
+ }
619
+ async shutdown() {
620
+ if (this.flushTimer != null) {
621
+ clearTimeout(this.flushTimer);
622
+ this.flushTimer = void 0;
623
+ }
624
+ await this.flush();
625
+ }
626
+ async forceFlush() {
627
+ await this.flush();
628
+ }
629
+ scheduleFlush() {
630
+ if (this.flushTimer != null) return;
631
+ this.flushTimer = setTimeout(() => {
632
+ this.flushTimer = void 0;
633
+ void this.flush();
634
+ }, FLUSH_INTERVAL_MS);
635
+ }
636
+ async flush() {
637
+ if (this.flushTimer != null) {
638
+ clearTimeout(this.flushTimer);
639
+ this.flushTimer = void 0;
640
+ }
641
+ if (this.bufferedCount === 0) return;
642
+ const batches = [...this.buffers.entries()];
643
+ this.buffers.clear();
644
+ this.bufferedCount = 0;
645
+ await Promise.all(batches.map(([agentId, spans]) => this.post(agentId, spans)));
646
+ }
647
+ async post(agentId, spans) {
648
+ if (spans.length === 0) return;
649
+ const otlpSpans = spans.map(spanToOtlpJson);
650
+ const body = JSON.stringify({
651
+ resourceSpans: [
652
+ {
653
+ resource: {
654
+ attributes: [
655
+ { key: "service.name", value: { stringValue: "firstflow-server" } }
656
+ ]
657
+ },
658
+ scopeSpans: [{ spans: otlpSpans }]
659
+ }
660
+ ]
661
+ });
662
+ const url = `${this.opts.baseUrl}/v1/agents/${encodeURIComponent(agentId)}/traces`;
663
+ const ac = new AbortController();
664
+ const timer = setTimeout(() => ac.abort(), REQUEST_TIMEOUT_MS);
665
+ try {
666
+ await fetch(url, {
667
+ method: "POST",
668
+ headers: {
669
+ Authorization: `Bearer ${this.opts.apiKey}`,
670
+ "Content-Type": "application/json"
671
+ },
672
+ body,
673
+ signal: ac.signal
674
+ });
675
+ } catch {
676
+ } finally {
677
+ clearTimeout(timer);
678
+ }
679
+ }
680
+ };
681
+
682
+ // src/sanitize.ts
683
+ var SENSITIVE_KEY_RE = /(password|secret|token|api[-_]?key|cookie|auth)/i;
684
+ var DEFAULT_MAX_DEPTH = 4;
685
+ var DEFAULT_MAX_STRING_LEN = 1024;
686
+ var RESERVED_KEYS = /* @__PURE__ */ new Set([
687
+ "token",
688
+ "api_key",
689
+ "distinct_id",
690
+ "$anon_distinct_id",
691
+ "$device_id",
692
+ "$session_id",
693
+ "$user_id",
694
+ "$set",
695
+ "$set_once",
696
+ "groups",
697
+ "$groups",
698
+ "event",
699
+ "timestamp",
700
+ "$insert_id",
701
+ "$lib",
702
+ "$lib_version",
703
+ "$current_url",
704
+ "$host",
705
+ "$pathname",
706
+ "$referrer",
707
+ "$referring_domain"
708
+ ]);
709
+ function isReservedKey(key) {
710
+ return RESERVED_KEYS.has(key) || key.startsWith("$");
711
+ }
712
+ function sanitizeValueInternal(value, depth, maxDepth, maxStringLen) {
713
+ if (value === null || value === void 0) return value;
714
+ if (typeof value === "string") {
715
+ if (value.length <= maxStringLen) return value;
716
+ return `${value.slice(0, maxStringLen)}\u2026truncated`;
717
+ }
718
+ if (typeof value === "number" || typeof value === "boolean") return value;
719
+ if (typeof value !== "object") return void 0;
720
+ if (depth >= maxDepth) return void 0;
721
+ if (Array.isArray(value)) {
722
+ const next = [];
723
+ for (const entry of value) {
724
+ next.push(sanitizeValueInternal(entry, depth + 1, maxDepth, maxStringLen));
725
+ }
726
+ return next;
727
+ }
728
+ const obj = value;
729
+ const out = {};
730
+ for (const [k, v] of Object.entries(obj)) {
731
+ const keyStr = String(k);
732
+ if (!isReservedKey(keyStr) && SENSITIVE_KEY_RE.test(keyStr)) continue;
733
+ const sv = sanitizeValueInternal(v, depth + 1, maxDepth, maxStringLen);
734
+ if (sv !== void 0) out[keyStr] = sv;
735
+ }
736
+ return Object.keys(out).length > 0 ? out : {};
737
+ }
738
+ function sanitizeAnalyticsProperties(properties, opts = {}) {
739
+ try {
740
+ const maxDepth = typeof opts.maxDepth === "number" && opts.maxDepth >= 1 ? opts.maxDepth : DEFAULT_MAX_DEPTH;
741
+ const maxStringLen = typeof opts.maxStringLen === "number" && opts.maxStringLen >= 1 ? opts.maxStringLen : DEFAULT_MAX_STRING_LEN;
742
+ const sanitized = sanitizeValueInternal(properties, 0, maxDepth, maxStringLen);
743
+ return sanitized && typeof sanitized === "object" && !Array.isArray(sanitized) ? sanitized : {};
744
+ } catch {
745
+ return {};
746
+ }
747
+ }
748
+
749
+ // src/analytics-client.ts
750
+ var FLUSH_SIZE2 = 20;
751
+ var FLUSH_INTERVAL_MS2 = 1e3;
752
+ var MAX_QUEUE_SIZE2 = 1e3;
753
+ var REQUEST_TIMEOUT_MS2 = 5e3;
754
+ var MAX_BACKOFF_MS = 8e3;
755
+ var FirstflowAnalyticsClient = class {
756
+ constructor(opts) {
757
+ this.opts = opts;
758
+ }
759
+ opts;
760
+ buffer = [];
761
+ flushTimer;
762
+ backoffMs = 1e3;
763
+ closed = false;
764
+ track(userId, event, properties) {
765
+ if (this.closed) return;
766
+ this.enqueue({
767
+ event,
768
+ message_id: crypto.randomUUID(),
769
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
770
+ session_id: "",
771
+ anonymous_id: "",
772
+ user_id: userId,
773
+ sdk_version: this.opts.sdkVersion,
774
+ ...properties && Object.keys(properties).length > 0 ? { props: sanitizeAnalyticsProperties(properties) } : {}
775
+ });
776
+ }
777
+ identify(userId, traits) {
778
+ if (this.closed) return;
779
+ this.enqueue({
780
+ event: "user_identified",
781
+ message_id: crypto.randomUUID(),
782
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
783
+ session_id: "",
784
+ anonymous_id: "",
785
+ user_id: userId,
786
+ sdk_version: this.opts.sdkVersion,
787
+ ...traits && Object.keys(traits).length > 0 ? { props: sanitizeAnalyticsProperties(traits) } : {}
788
+ });
789
+ }
790
+ async shutdown() {
791
+ if (this.closed) return;
792
+ this.closed = true;
793
+ if (this.flushTimer != null) {
794
+ clearTimeout(this.flushTimer);
795
+ this.flushTimer = void 0;
796
+ }
797
+ await this.flush();
798
+ }
799
+ enqueue(event) {
800
+ if (this.buffer.length >= MAX_QUEUE_SIZE2) {
801
+ this.buffer.shift();
802
+ }
803
+ this.buffer.push(event);
804
+ if (this.buffer.length >= FLUSH_SIZE2) {
805
+ void this.flush();
806
+ } else {
807
+ this.scheduleFlush();
808
+ }
809
+ }
810
+ scheduleFlush() {
811
+ if (this.flushTimer != null) return;
812
+ this.flushTimer = setTimeout(() => {
813
+ this.flushTimer = void 0;
814
+ void this.flush();
815
+ }, FLUSH_INTERVAL_MS2);
816
+ }
817
+ async flush() {
818
+ if (this.flushTimer != null) {
819
+ clearTimeout(this.flushTimer);
820
+ this.flushTimer = void 0;
821
+ }
822
+ if (this.buffer.length === 0) return;
823
+ const batch = this.buffer.splice(0, this.buffer.length);
824
+ const url = `${this.opts.baseUrl}/agents/${encodeURIComponent(this.opts.agentId)}/analytics/events`;
825
+ const ac = new AbortController();
826
+ const timer = setTimeout(() => ac.abort(), REQUEST_TIMEOUT_MS2);
827
+ try {
828
+ const res = await fetch(url, {
829
+ method: "POST",
830
+ headers: {
831
+ Authorization: `Bearer ${this.opts.apiKey}`,
832
+ "Content-Type": "application/json"
833
+ },
834
+ body: JSON.stringify({ events: batch }),
835
+ signal: ac.signal
836
+ });
837
+ if (res.ok) {
838
+ this.backoffMs = 1e3;
839
+ } else {
840
+ this.requeueWithBackoff(batch);
841
+ }
842
+ } catch {
843
+ this.requeueWithBackoff(batch);
844
+ } finally {
845
+ clearTimeout(timer);
846
+ }
847
+ }
848
+ requeueWithBackoff(events) {
849
+ if (this.closed) return;
850
+ const space = MAX_QUEUE_SIZE2 - this.buffer.length;
851
+ if (space > 0) {
852
+ this.buffer.unshift(...events.slice(0, space));
853
+ }
854
+ const wait = Math.min(this.backoffMs, MAX_BACKOFF_MS);
855
+ this.backoffMs = Math.min(this.backoffMs * 2, MAX_BACKOFF_MS);
856
+ this.flushTimer = setTimeout(() => {
857
+ this.flushTimer = void 0;
858
+ void this.flush();
859
+ }, wait);
860
+ }
861
+ };
862
+
863
+ // src/llm-content-redactor.ts
864
+ function stripGenAiContentAttributes(attrs) {
865
+ if (!attrs) return attrs;
866
+ const out = { ...attrs };
867
+ for (const key of Object.keys(out)) {
868
+ if (key.startsWith("gen_ai.prompt") || key.startsWith("gen_ai.completion") || key.startsWith("gen_ai.input.") || key.startsWith("gen_ai.output.")) {
869
+ delete out[key];
870
+ }
871
+ }
872
+ return out;
873
+ }
874
+ function withRedactedAttributes(span) {
875
+ return new Proxy(span, {
876
+ get(target, prop, receiver) {
877
+ if (prop === "attributes") {
878
+ const raw = Reflect.get(target, "attributes", receiver);
879
+ return stripGenAiContentAttributes(raw) ?? raw;
880
+ }
881
+ return Reflect.get(target, prop, receiver);
882
+ }
883
+ });
884
+ }
885
+ function createLlmContentRedactor(captureLlmContent, inner) {
886
+ return {
887
+ onStart(span, parentContext) {
888
+ inner.onStart(span, parentContext);
889
+ },
890
+ onEnd(span) {
891
+ if (captureLlmContent) {
892
+ inner.onEnd(span);
893
+ return;
894
+ }
895
+ inner.onEnd(withRedactedAttributes(span));
896
+ },
897
+ shutdown() {
898
+ return inner.shutdown();
899
+ },
900
+ forceFlush() {
901
+ return inner.forceFlush();
902
+ }
903
+ };
904
+ }
905
+
906
+ // src/forwarder.ts
907
+ var FORWARD_TIMEOUT_MS = 2e3;
908
+ function forwardConversationMessage(args, failureBuffer) {
909
+ const url = `${args.baseUrl.replace(/\/$/, "")}/v1/conversations/${encodeURIComponent(
910
+ args.conversationId
911
+ )}/messages`;
912
+ const body = JSON.stringify({
913
+ agentId: args.agentId,
914
+ conversationId: args.conversationId,
915
+ userId: args.userId,
916
+ role: args.role,
917
+ content: args.content,
918
+ ...args.recentMessages && args.recentMessages.length ? { recentMessages: args.recentMessages } : {},
919
+ ...args.model !== void 0 ? { model: args.model } : {},
920
+ ...args.provider !== void 0 ? { provider: args.provider } : {},
921
+ ...args.inputTokens !== void 0 ? { inputTokens: args.inputTokens } : {},
922
+ ...args.outputTokens !== void 0 ? { outputTokens: args.outputTokens } : {},
923
+ ...args.cacheReadTokens !== void 0 ? { cacheReadTokens: args.cacheReadTokens } : {},
924
+ ...args.cacheCreationTokens !== void 0 ? { cacheCreationTokens: args.cacheCreationTokens } : {},
925
+ ...args.latencyMs !== void 0 ? { latencyMs: args.latencyMs } : {},
926
+ ...args.timeToFirstTokenMs !== void 0 ? { timeToFirstTokenMs: args.timeToFirstTokenMs } : {},
927
+ ...args.finishReason !== void 0 ? { finishReason: args.finishReason } : {},
928
+ ...args.isError !== void 0 ? { isError: args.isError } : {},
929
+ ...args.error !== void 0 ? { error: args.error } : {},
930
+ ...args.inputCostUsd !== void 0 ? { inputCostUsd: args.inputCostUsd } : {},
931
+ ...args.outputCostUsd !== void 0 ? { outputCostUsd: args.outputCostUsd } : {},
932
+ ...args.totalCostUsd !== void 0 ? { totalCostUsd: args.totalCostUsd } : {},
933
+ ...args.httpStatus !== void 0 ? { httpStatus: args.httpStatus } : {},
934
+ ...args.properties !== void 0 ? { properties: args.properties } : {}
935
+ });
936
+ void (async () => {
937
+ const ac = new AbortController();
938
+ const timer = setTimeout(() => ac.abort(), FORWARD_TIMEOUT_MS);
939
+ try {
940
+ const res = await fetch(url, {
941
+ method: "POST",
942
+ headers: {
943
+ Authorization: `Bearer ${args.apiKey}`,
944
+ "Content-Type": "application/json"
945
+ },
946
+ body,
947
+ signal: ac.signal
948
+ });
949
+ if (!res.ok) {
950
+ failureBuffer?.push(args);
951
+ }
952
+ } catch {
953
+ failureBuffer?.push(args);
954
+ } finally {
955
+ clearTimeout(timer);
956
+ }
957
+ })();
958
+ }
959
+
960
+ // src/trace-forwarder.ts
961
+ var TRACE_FORWARD_TIMEOUT_MS = 2e3;
962
+ function toIso(d) {
963
+ if (d === void 0 || d === null) return void 0;
964
+ return d instanceof Date ? d.toISOString() : d;
965
+ }
966
+ function serializeSpan(s) {
967
+ return {
968
+ ...s.spanId !== void 0 ? { spanId: s.spanId } : {},
969
+ ...s.parentSpanId !== void 0 ? { parentSpanId: s.parentSpanId } : {},
970
+ ...s.type !== void 0 ? { type: s.type } : {},
971
+ ...s.name !== void 0 ? { name: s.name } : {},
972
+ ...s.model !== void 0 ? { model: s.model } : {},
973
+ ...s.provider !== void 0 ? { provider: s.provider } : {},
974
+ ...s.input !== void 0 ? { input: s.input } : {},
975
+ ...s.output !== void 0 ? { output: s.output } : {},
976
+ ...s.inputTokens !== void 0 ? { inputTokens: s.inputTokens } : {},
977
+ ...s.outputTokens !== void 0 ? { outputTokens: s.outputTokens } : {},
978
+ ...s.cacheReadTokens !== void 0 ? { cacheReadTokens: s.cacheReadTokens } : {},
979
+ ...s.cacheCreationTokens !== void 0 ? { cacheCreationTokens: s.cacheCreationTokens } : {},
980
+ ...s.latencyMs !== void 0 ? { latencyMs: s.latencyMs } : {},
981
+ ...s.timeToFirstTokenMs !== void 0 ? { timeToFirstTokenMs: s.timeToFirstTokenMs } : {},
982
+ ...s.finishReason !== void 0 ? { finishReason: s.finishReason } : {},
983
+ ...s.isError !== void 0 ? { isError: s.isError } : {},
984
+ ...s.error !== void 0 ? { error: s.error } : {},
985
+ ...s.inputCostUsd !== void 0 ? { inputCostUsd: s.inputCostUsd } : {},
986
+ ...s.outputCostUsd !== void 0 ? { outputCostUsd: s.outputCostUsd } : {},
987
+ ...s.totalCostUsd !== void 0 ? { totalCostUsd: s.totalCostUsd } : {},
988
+ ...s.toolsCalled !== void 0 ? { toolsCalled: s.toolsCalled } : {},
989
+ ...s.toolCallCount !== void 0 ? { toolCallCount: s.toolCallCount } : {},
990
+ ...s.inputState !== void 0 ? { inputState: s.inputState } : {},
991
+ ...s.outputState !== void 0 ? { outputState: s.outputState } : {},
992
+ ...s.httpStatus !== void 0 ? { httpStatus: s.httpStatus } : {},
993
+ ...s.properties !== void 0 ? { properties: s.properties } : {},
994
+ ...s.startedAt !== void 0 ? { startedAt: toIso(s.startedAt) } : {},
995
+ ...s.endedAt !== void 0 ? { endedAt: toIso(s.endedAt) } : {}
996
+ };
997
+ }
998
+ function serializeTrace(t) {
999
+ return {
1000
+ ...t.traceId !== void 0 ? { traceId: t.traceId } : {},
1001
+ ...t.sessionId !== void 0 ? { sessionId: t.sessionId } : {},
1002
+ ...t.userId !== void 0 ? { userId: t.userId } : {},
1003
+ ...t.name !== void 0 ? { name: t.name } : {},
1004
+ ...t.inputState !== void 0 ? { inputState: t.inputState } : {},
1005
+ ...t.outputState !== void 0 ? { outputState: t.outputState } : {},
1006
+ ...t.latencyMs !== void 0 ? { latencyMs: t.latencyMs } : {},
1007
+ ...t.isError !== void 0 ? { isError: t.isError } : {},
1008
+ ...t.error !== void 0 ? { error: t.error } : {},
1009
+ ...t.properties !== void 0 ? { properties: t.properties } : {},
1010
+ ...t.startedAt !== void 0 ? { startedAt: toIso(t.startedAt) } : {},
1011
+ ...t.endedAt !== void 0 ? { endedAt: toIso(t.endedAt) } : {},
1012
+ ...t.spans && t.spans.length > 0 ? { spans: t.spans.map(serializeSpan) } : {}
1013
+ };
1014
+ }
1015
+ function forwardTrace(args) {
1016
+ const url = `${args.baseUrl.replace(/\/$/, "")}/v1/agents/${encodeURIComponent(args.agentId)}/traces`;
1017
+ const body = JSON.stringify(serializeTrace(args.trace));
1018
+ void (async () => {
1019
+ const ac = new AbortController();
1020
+ const timer = setTimeout(() => ac.abort(), TRACE_FORWARD_TIMEOUT_MS);
1021
+ try {
1022
+ await fetch(url, {
1023
+ method: "POST",
1024
+ headers: {
1025
+ Authorization: `Bearer ${args.apiKey}`,
1026
+ "Content-Type": "application/json"
1027
+ },
1028
+ body,
1029
+ signal: ac.signal
1030
+ });
1031
+ } catch {
1032
+ } finally {
1033
+ clearTimeout(timer);
1034
+ }
1035
+ })();
1036
+ }
1037
+
1038
+ // src/outcome-forwarder.ts
1039
+ var OUTCOME_FORWARD_TIMEOUT_MS = 2e3;
1040
+ function forwardOutcome(args) {
1041
+ const url = `${args.baseUrl.replace(/\/$/, "")}/v1/conversations/${encodeURIComponent(args.conversationId)}/signal`;
1042
+ const payload = {};
1043
+ if (args.outcome !== void 0) payload.outcome = args.outcome;
1044
+ if (args.intent !== void 0) payload.intent = args.intent;
1045
+ if (args.userId !== void 0) payload.userId = args.userId;
1046
+ const body = JSON.stringify(payload);
1047
+ void (async () => {
1048
+ const ac = new AbortController();
1049
+ const timer = setTimeout(() => ac.abort(), OUTCOME_FORWARD_TIMEOUT_MS);
1050
+ try {
1051
+ await fetch(url, {
1052
+ method: "PATCH",
1053
+ headers: {
1054
+ Authorization: `Bearer ${args.apiKey}`,
1055
+ "Content-Type": "application/json"
1056
+ },
1057
+ body,
1058
+ signal: ac.signal
1059
+ });
1060
+ } catch {
1061
+ } finally {
1062
+ clearTimeout(timer);
1063
+ }
1064
+ })();
1065
+ }
1066
+
1067
+ // src/feedback-forwarder.ts
1068
+ var FEEDBACK_FORWARD_TIMEOUT_MS = 2e3;
1069
+ function forwardFeedback(args) {
1070
+ const url = `${args.baseUrl.replace(/\/$/, "")}/v1/conversations/${encodeURIComponent(args.conversationId)}/feedback`;
1071
+ const payload = { rating: args.rating };
1072
+ if (args.comment !== void 0) payload.comment = args.comment;
1073
+ if (args.messageId !== void 0) payload.messageId = args.messageId;
1074
+ if (args.userId !== void 0) payload.userId = args.userId;
1075
+ const body = JSON.stringify(payload);
1076
+ void (async () => {
1077
+ const ac = new AbortController();
1078
+ const timer = setTimeout(() => ac.abort(), FEEDBACK_FORWARD_TIMEOUT_MS);
1079
+ try {
1080
+ await fetch(url, {
1081
+ method: "POST",
1082
+ headers: {
1083
+ Authorization: `Bearer ${args.apiKey}`,
1084
+ "Content-Type": "application/json"
1085
+ },
1086
+ body,
1087
+ signal: ac.signal
1088
+ });
1089
+ } catch {
1090
+ } finally {
1091
+ clearTimeout(timer);
1092
+ }
1093
+ })();
1094
+ }
1095
+
1096
+ // src/ring-buffer.ts
1097
+ var DEFAULT_MAX = 1e3;
1098
+ var RingBuffer = class {
1099
+ items = [];
1100
+ max;
1101
+ constructor(maxSize = DEFAULT_MAX) {
1102
+ this.max = maxSize;
1103
+ }
1104
+ push(item) {
1105
+ if (this.items.length >= this.max) {
1106
+ this.items.shift();
1107
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
1108
+ console.warn("[@firstflow/core] ring buffer overflow \u2014 dropped oldest entry");
1109
+ }
1110
+ }
1111
+ this.items.push(item);
1112
+ }
1113
+ drainAll() {
1114
+ const out = this.items.slice();
1115
+ this.items.length = 0;
1116
+ return out;
1117
+ }
1118
+ size() {
1119
+ return this.items.length;
1120
+ }
1121
+ };
1122
+
1123
+ // src/model-pricing.ts
1124
+ var PRICING = {
1125
+ // -------------------------------------------------------------------------
1126
+ // OpenAI
1127
+ // -------------------------------------------------------------------------
1128
+ "gpt-4o": { inputPer1M: 2.5, outputPer1M: 10 },
1129
+ "gpt-4o-mini": { inputPer1M: 0.15, outputPer1M: 0.6 },
1130
+ "gpt-4o-audio-preview": { inputPer1M: 2.5, outputPer1M: 10 },
1131
+ "gpt-4-turbo": { inputPer1M: 10, outputPer1M: 30 },
1132
+ "gpt-4": { inputPer1M: 30, outputPer1M: 60 },
1133
+ "gpt-3.5-turbo": { inputPer1M: 0.5, outputPer1M: 1.5 },
1134
+ "o1": { inputPer1M: 15, outputPer1M: 60 },
1135
+ "o1-mini": { inputPer1M: 1.1, outputPer1M: 4.4 },
1136
+ "o1-preview": { inputPer1M: 15, outputPer1M: 60 },
1137
+ "o3": { inputPer1M: 10, outputPer1M: 40 },
1138
+ "o3-mini": { inputPer1M: 1.1, outputPer1M: 4.4 },
1139
+ "o4-mini": { inputPer1M: 1.1, outputPer1M: 4.4 },
1140
+ // -------------------------------------------------------------------------
1141
+ // Anthropic
1142
+ // -------------------------------------------------------------------------
1143
+ "claude-opus-4-5": { inputPer1M: 15, outputPer1M: 75 },
1144
+ "claude-opus-4-7": { inputPer1M: 15, outputPer1M: 75 },
1145
+ "claude-sonnet-4-5": { inputPer1M: 3, outputPer1M: 15 },
1146
+ "claude-sonnet-4-6": { inputPer1M: 3, outputPer1M: 15 },
1147
+ "claude-haiku-4-5": { inputPer1M: 0.8, outputPer1M: 4 },
1148
+ "claude-3-5-sonnet-20241022": { inputPer1M: 3, outputPer1M: 15 },
1149
+ "claude-3-5-sonnet-20240620": { inputPer1M: 3, outputPer1M: 15 },
1150
+ "claude-3-5-haiku-20241022": { inputPer1M: 0.8, outputPer1M: 4 },
1151
+ "claude-3-opus-20240229": { inputPer1M: 15, outputPer1M: 75 },
1152
+ "claude-3-sonnet-20240229": { inputPer1M: 3, outputPer1M: 15 },
1153
+ "claude-3-haiku-20240307": { inputPer1M: 0.25, outputPer1M: 1.25 }
1154
+ };
1155
+ function findByPrefix(model) {
1156
+ let candidate = model;
1157
+ while (candidate.includes("-")) {
1158
+ candidate = candidate.replace(/-[^-]+$/, "");
1159
+ const match = Object.keys(PRICING).find((k) => k.startsWith(candidate));
1160
+ if (match) return PRICING[match];
1161
+ }
1162
+ return void 0;
1163
+ }
1164
+ function calculateCost(model, inputTokens, outputTokens) {
1165
+ const pricing = PRICING[model] ?? findByPrefix(model);
1166
+ if (!pricing) return null;
1167
+ const inputCostUsd = inputTokens / 1e6 * pricing.inputPer1M;
1168
+ const outputCostUsd = outputTokens / 1e6 * pricing.outputPer1M;
1169
+ return { inputCostUsd, outputCostUsd, totalCostUsd: inputCostUsd + outputCostUsd };
1170
+ }
1171
+
1172
+ // src/constants.ts
1173
+ var DEFAULT_FIRSTFLOW_BASE_URL = "https://api.firstflow.app";
1174
+ var FIRSTFLOW_SERVER_PACKAGE_VERSION = "0.0.1-alpha.0";
1175
+
1176
+ // src/peer-deps.ts
1177
+ var import_node_module = require("module");
1178
+ var nodeRequire = (0, import_node_module.createRequire)(importMetaUrl);
1179
+ function requirePeer(moduleName) {
1180
+ return nodeRequire(moduleName);
1181
+ }
1182
+ function isPeerPreloaded(moduleName) {
1183
+ try {
1184
+ const resolved = nodeRequire.resolve(moduleName);
1185
+ const cached = nodeRequire.cache?.[resolved];
1186
+ return cached !== void 0 && cached.exports !== void 0;
1187
+ } catch {
1188
+ return false;
1189
+ }
1190
+ }
1191
+
1192
+ // src/runtime.ts
1193
+ function enrichSpanCosts(span) {
1194
+ if (span.inputCostUsd !== void 0 && span.outputCostUsd !== void 0) return span;
1195
+ if (!span.model || typeof span.inputTokens !== "number" || typeof span.outputTokens !== "number") {
1196
+ return span;
1197
+ }
1198
+ const cost = calculateCost(span.model, span.inputTokens, span.outputTokens);
1199
+ if (!cost) return span;
1200
+ return {
1201
+ ...span,
1202
+ inputCostUsd: span.inputCostUsd ?? cost.inputCostUsd,
1203
+ outputCostUsd: span.outputCostUsd ?? cost.outputCostUsd,
1204
+ totalCostUsd: span.totalCostUsd ?? cost.totalCostUsd
1205
+ };
1206
+ }
1207
+ function readCaptureLlmContent() {
1208
+ return process.env.FIRSTFLOW_CAPTURE_LLM_CONTENT?.trim().toLowerCase() === "true";
1209
+ }
1210
+ var Runtime = class {
1211
+ apiKey;
1212
+ baseUrl;
1213
+ forwardFailures = new RingBuffer(1e3);
1214
+ analyticsByAgent = /* @__PURE__ */ new Map();
1215
+ otelSdk;
1216
+ closed = false;
1217
+ constructor(apiKey, baseUrl) {
1218
+ this.apiKey = apiKey;
1219
+ this.baseUrl = baseUrl;
1220
+ this.startOpenTelemetry();
1221
+ process.once("beforeExit", () => {
1222
+ void this.shutdown();
1223
+ });
1224
+ }
1225
+ startOpenTelemetry() {
1226
+ const captureLlmContent = readCaptureLlmContent();
1227
+ const inner = new FirstflowSpanProcessor({ apiKey: this.apiKey, baseUrl: this.baseUrl });
1228
+ const chain = createLlmContentRedactor(captureLlmContent, inner);
1229
+ const resource = (0, import_resources.resourceFromAttributes)({ "service.name": "firstflow-server" });
1230
+ const anthropicInstrumentation = new import_instrumentation_anthropic.AnthropicInstrumentation({ traceContent: captureLlmContent });
1231
+ const anthropicPreloaded = isPeerPreloaded("@anthropic-ai/sdk");
1232
+ this.otelSdk = new import_sdk_node.NodeSDK({
1233
+ resource,
1234
+ spanProcessors: [chain],
1235
+ instrumentations: [
1236
+ new import_instrumentation_openai.OpenAIInstrumentation({ captureMessageContent: captureLlmContent }),
1237
+ anthropicInstrumentation
1238
+ ]
1239
+ });
1240
+ this.otelSdk.start();
1241
+ if (anthropicPreloaded) {
1242
+ try {
1243
+ anthropicInstrumentation.manuallyInstrument(requirePeer("@anthropic-ai/sdk"));
1244
+ } catch {
1245
+ }
1246
+ }
1247
+ }
1248
+ analyticsFor(agentId) {
1249
+ let client = this.analyticsByAgent.get(agentId);
1250
+ if (!client) {
1251
+ client = new FirstflowAnalyticsClient({
1252
+ apiKey: this.apiKey,
1253
+ baseUrl: this.baseUrl,
1254
+ agentId,
1255
+ sdkVersion: FIRSTFLOW_SERVER_PACKAGE_VERSION
1256
+ });
1257
+ this.analyticsByAgent.set(agentId, client);
1258
+ }
1259
+ return client;
1260
+ }
1261
+ /**
1262
+ * Build a request-agnostic `WrapContext` for the proxy. The context carries
1263
+ * only infrastructure hooks — the per-request agent/user/session are parsed
1264
+ * from each call and flow through `ObserveArgs`, never stored here.
1265
+ */
1266
+ wrapContext() {
1267
+ return {
1268
+ apiKey: this.apiKey,
1269
+ baseUrl: this.baseUrl,
1270
+ onAssistantComplete: (a) => this.observe(a),
1271
+ onUserMessage: (a) => this.observe(a),
1272
+ onError: (a) => {
1273
+ if (!a.conversationId) return;
1274
+ this.observe({
1275
+ agentId: a.agentId,
1276
+ conversationId: a.conversationId,
1277
+ userId: a.userId,
1278
+ role: "assistant",
1279
+ content: "",
1280
+ isError: true,
1281
+ error: a.error,
1282
+ model: a.model,
1283
+ provider: a.provider,
1284
+ latencyMs: a.latencyMs,
1285
+ httpStatus: a.httpStatus
1286
+ });
1287
+ }
1288
+ };
1289
+ }
1290
+ observe(args) {
1291
+ let { inputCostUsd, outputCostUsd, totalCostUsd } = args;
1292
+ if (totalCostUsd === void 0 && args.model && typeof args.inputTokens === "number" && typeof args.outputTokens === "number") {
1293
+ const cost = calculateCost(args.model, args.inputTokens, args.outputTokens);
1294
+ if (cost) {
1295
+ inputCostUsd = cost.inputCostUsd;
1296
+ outputCostUsd = cost.outputCostUsd;
1297
+ totalCostUsd = cost.totalCostUsd;
1298
+ }
1299
+ }
1300
+ forwardConversationMessage(
1301
+ {
1302
+ baseUrl: this.baseUrl,
1303
+ apiKey: this.apiKey,
1304
+ agentId: args.agentId,
1305
+ conversationId: args.conversationId,
1306
+ userId: args.userId,
1307
+ role: args.role,
1308
+ content: args.content,
1309
+ model: args.model,
1310
+ provider: args.provider,
1311
+ inputTokens: args.inputTokens,
1312
+ outputTokens: args.outputTokens,
1313
+ cacheReadTokens: args.cacheReadTokens,
1314
+ cacheCreationTokens: args.cacheCreationTokens,
1315
+ latencyMs: args.latencyMs,
1316
+ timeToFirstTokenMs: args.timeToFirstTokenMs,
1317
+ finishReason: args.finishReason,
1318
+ isError: args.isError,
1319
+ error: args.error,
1320
+ inputCostUsd,
1321
+ outputCostUsd,
1322
+ totalCostUsd,
1323
+ httpStatus: args.httpStatus,
1324
+ ...args.recentMessages ? { recentMessages: args.recentMessages } : {},
1325
+ ...args.properties ? { properties: args.properties } : {}
1326
+ },
1327
+ this.forwardFailures
1328
+ );
1329
+ this.analyticsFor(args.agentId).track(args.userId, "conversation_message", {
1330
+ conversation_id: args.conversationId,
1331
+ role: args.role,
1332
+ has_content: args.content.length > 0
1333
+ });
1334
+ }
1335
+ trace(agentId, input) {
1336
+ const enriched = input.spans ? { ...input, spans: input.spans.map(enrichSpanCosts) } : input;
1337
+ forwardTrace({ baseUrl: this.baseUrl, apiKey: this.apiKey, agentId, trace: enriched });
1338
+ }
1339
+ outcome(args) {
1340
+ forwardOutcome({ baseUrl: this.baseUrl, apiKey: this.apiKey, ...args });
1341
+ }
1342
+ feedback(args) {
1343
+ forwardFeedback({ baseUrl: this.baseUrl, apiKey: this.apiKey, ...args });
1344
+ }
1345
+ track(agentId, userId, event, properties) {
1346
+ this.analyticsFor(agentId).track(
1347
+ userId,
1348
+ event,
1349
+ properties ? sanitizeAnalyticsProperties({ ...properties }) : void 0
1350
+ );
1351
+ }
1352
+ identify(agentId, userId, traits) {
1353
+ this.analyticsFor(agentId).identify(
1354
+ userId,
1355
+ traits ? sanitizeAnalyticsProperties({ ...traits }) : void 0
1356
+ );
1357
+ }
1358
+ async shutdown() {
1359
+ if (this.closed) return;
1360
+ this.closed = true;
1361
+ if (this.otelSdk) {
1362
+ await this.otelSdk.shutdown().catch(() => {
1363
+ });
1364
+ this.otelSdk = void 0;
1365
+ }
1366
+ for (const client of this.analyticsByAgent.values()) {
1367
+ await client.shutdown().catch(() => {
1368
+ });
1369
+ }
1370
+ this.analyticsByAgent.clear();
1371
+ const pending = this.forwardFailures.drainAll();
1372
+ for (const p of pending) forwardConversationMessage(p, void 0);
1373
+ }
1374
+ };
1375
+ var _runtime;
1376
+ function getRuntime() {
1377
+ if (_runtime) return _runtime;
1378
+ const apiKey = (process.env.FIRSTFLOW_API_KEY ?? "").trim();
1379
+ if (!apiKey) {
1380
+ throw new Error(
1381
+ "[Firstflow] FIRSTFLOW_API_KEY is required. Set it in your server environment."
1382
+ );
1383
+ }
1384
+ const baseUrl = (process.env.FIRSTFLOW_API_BASE_URL ?? DEFAULT_FIRSTFLOW_BASE_URL).replace(/\/$/, "");
1385
+ _runtime = new Runtime(apiKey, baseUrl);
1386
+ return _runtime;
1387
+ }
1388
+
1389
+ // src/langchain.ts
1390
+ function asString(v) {
1391
+ return typeof v === "string" ? v.trim() : "";
1392
+ }
1393
+ var warnedIncompleteTagging = false;
1394
+ function warnIncompleteOnce() {
1395
+ if (warnedIncompleteTagging) return;
1396
+ warnedIncompleteTagging = true;
1397
+ console.warn(
1398
+ "[@firstflow/core/langchain] A LangChain run was tagged with some but not all of `firstflowAgentId`, `sessionId`, `userId` \u2014 the turn was NOT observed. Provide all three in the run metadata to track it."
1399
+ );
1400
+ }
1401
+ function readIdentity(metadata) {
1402
+ if (!metadata) return void 0;
1403
+ const nested = typeof metadata.firstflow === "object" && metadata.firstflow !== null ? metadata.firstflow : {};
1404
+ const agentId = asString(metadata.firstflowAgentId ?? nested.agentId);
1405
+ const sessionId = asString(metadata.sessionId ?? nested.sessionId);
1406
+ const userId = asString(metadata.userId ?? nested.userId);
1407
+ if (!agentId && !sessionId && !userId) return void 0;
1408
+ if (!agentId || !sessionId || !userId) {
1409
+ warnIncompleteOnce();
1410
+ return void 0;
1411
+ }
1412
+ return { agentId, sessionId, userId };
1413
+ }
1414
+ var KNOWN_PROVIDERS = /* @__PURE__ */ new Set(["openai", "anthropic"]);
1415
+ function roleOf(msg) {
1416
+ if (!msg || typeof msg !== "object") return void 0;
1417
+ const m = msg;
1418
+ let t;
1419
+ if (typeof m._getType === "function") t = m._getType();
1420
+ else if (typeof m.getType === "function") t = m.getType();
1421
+ else if (typeof m.role === "string") t = m.role;
1422
+ else if (typeof m.type === "string") t = m.type;
1423
+ switch (t) {
1424
+ case "human":
1425
+ case "user":
1426
+ return "user";
1427
+ case "ai":
1428
+ case "assistant":
1429
+ return "assistant";
1430
+ case "tool":
1431
+ case "function":
1432
+ return "tool";
1433
+ default:
1434
+ return void 0;
1435
+ }
1436
+ }
1437
+ function contentToText(content) {
1438
+ if (typeof content === "string") return content;
1439
+ if (!Array.isArray(content)) return "";
1440
+ let acc = "";
1441
+ for (const part of content) {
1442
+ if (typeof part === "string") {
1443
+ acc += part;
1444
+ } else if (part && typeof part === "object") {
1445
+ const p = part;
1446
+ if (p.type === "text" && typeof p.text === "string") acc += p.text;
1447
+ }
1448
+ }
1449
+ return acc;
1450
+ }
1451
+ function toRecentMessages(messages) {
1452
+ if (!Array.isArray(messages)) return [];
1453
+ const out = [];
1454
+ for (const msg of messages) {
1455
+ const role = roleOf(msg);
1456
+ if (role !== "user" && role !== "assistant") continue;
1457
+ const content = contentToText(msg.content).trim();
1458
+ if (!content) continue;
1459
+ out.push({ role, content });
1460
+ }
1461
+ return out;
1462
+ }
1463
+ function turnStartUserMessage(messages) {
1464
+ if (!Array.isArray(messages) || messages.length === 0) return void 0;
1465
+ const last = messages[messages.length - 1];
1466
+ if (roleOf(last) !== "user") return void 0;
1467
+ const text = contentToText(last.content).trim();
1468
+ return text || void 0;
1469
+ }
1470
+ function extractModel(extraParams, metadata) {
1471
+ const inv = extraParams?.invocation_params ?? {};
1472
+ const fromInv = inv.model ?? inv.model_name ?? inv.modelName;
1473
+ if (typeof fromInv === "string" && fromInv) return fromInv;
1474
+ const fromMeta = metadata?.ls_model_name;
1475
+ return typeof fromMeta === "string" && fromMeta ? fromMeta : void 0;
1476
+ }
1477
+ function extractProvider(metadata) {
1478
+ const p = metadata?.ls_provider;
1479
+ return typeof p === "string" && KNOWN_PROVIDERS.has(p) ? p : void 0;
1480
+ }
1481
+ function extractEndMeta(output) {
1482
+ const out = output ?? {};
1483
+ const generations = out.generations;
1484
+ let text = "";
1485
+ const meta = {};
1486
+ if (Array.isArray(generations)) {
1487
+ for (const promptGens of generations) {
1488
+ if (!Array.isArray(promptGens)) continue;
1489
+ for (const gen of promptGens) {
1490
+ if (!gen || typeof gen !== "object") continue;
1491
+ const g = gen;
1492
+ if (typeof g.text === "string" && g.text) text += g.text;
1493
+ const message = g.message;
1494
+ if (message) {
1495
+ if (!text) text += contentToText(message.content);
1496
+ const usage = message.usage_metadata;
1497
+ if (usage) {
1498
+ if (typeof usage.input_tokens === "number") meta.inputTokens = usage.input_tokens;
1499
+ if (typeof usage.output_tokens === "number") meta.outputTokens = usage.output_tokens;
1500
+ const inDetails = usage.input_token_details;
1501
+ if (typeof inDetails?.cache_read === "number") meta.cacheReadTokens = inDetails.cache_read;
1502
+ if (typeof inDetails?.cache_creation === "number")
1503
+ meta.cacheCreationTokens = inDetails.cache_creation;
1504
+ }
1505
+ const respMeta = message.response_metadata;
1506
+ const stop = respMeta?.stop_reason ?? respMeta?.finish_reason;
1507
+ if (typeof stop === "string" && meta.finishReason === void 0) meta.finishReason = stop;
1508
+ }
1509
+ const genInfo = g.generationInfo;
1510
+ const fr = genInfo?.finish_reason ?? genInfo?.finishReason;
1511
+ if (typeof fr === "string" && meta.finishReason === void 0) meta.finishReason = fr;
1512
+ }
1513
+ }
1514
+ }
1515
+ if (meta.inputTokens === void 0 || meta.outputTokens === void 0) {
1516
+ const llmOutput = out.llmOutput ?? {};
1517
+ const usage = llmOutput.tokenUsage ?? llmOutput.usage ?? {};
1518
+ const input = usage.promptTokens ?? usage.input_tokens ?? usage.prompt_tokens;
1519
+ const outp = usage.completionTokens ?? usage.output_tokens ?? usage.completion_tokens;
1520
+ if (meta.inputTokens === void 0 && typeof input === "number") meta.inputTokens = input;
1521
+ if (meta.outputTokens === void 0 && typeof outp === "number") meta.outputTokens = outp;
1522
+ }
1523
+ return { text, meta };
1524
+ }
1525
+ function safeObserve(fn) {
1526
+ try {
1527
+ fn();
1528
+ } catch (err) {
1529
+ warnRuntimeOnce(err);
1530
+ }
1531
+ }
1532
+ var warnedRuntime = false;
1533
+ function warnRuntimeOnce(err) {
1534
+ if (warnedRuntime) return;
1535
+ warnedRuntime = true;
1536
+ console.warn(
1537
+ `[@firstflow/core/langchain] Telemetry disabled: ${err instanceof Error ? err.message : String(err)}`
1538
+ );
1539
+ }
1540
+ var FirstflowCallbackHandler = class extends import_base.BaseCallbackHandler {
1541
+ name = "firstflow";
1542
+ runs = /* @__PURE__ */ new Map();
1543
+ // -- starts ----------------------------------------------------------------
1544
+ handleChatModelStart(_llm, messages, runId, _parentRunId, extraParams, _tags, metadata) {
1545
+ const identity = readIdentity(metadata);
1546
+ if (!identity) return;
1547
+ const prompt = Array.isArray(messages) ? messages[0] : messages;
1548
+ this.begin(runId, identity, prompt, extraParams, metadata);
1549
+ }
1550
+ handleLLMStart(_llm, prompts, runId, _parentRunId, extraParams, _tags, metadata) {
1551
+ const identity = readIdentity(metadata);
1552
+ if (!identity) return;
1553
+ const list = Array.isArray(prompts) ? prompts : [];
1554
+ const last = list.length ? list[list.length - 1] : void 0;
1555
+ const pseudo = typeof last === "string" ? [{ role: "user", content: last }] : [];
1556
+ this.begin(runId, identity, pseudo, extraParams, metadata);
1557
+ }
1558
+ begin(runId, identity, prompt, extraParams, metadata) {
1559
+ const recentMessages = toRecentMessages(prompt);
1560
+ const model = extractModel(extraParams, metadata);
1561
+ const provider = extractProvider(metadata);
1562
+ this.runs.set(runId, {
1563
+ identity,
1564
+ startTime: Date.now(),
1565
+ model,
1566
+ provider,
1567
+ recentMessages
1568
+ });
1569
+ const userContent = turnStartUserMessage(prompt);
1570
+ if (!userContent) return;
1571
+ safeObserve(
1572
+ () => getRuntime().observe({
1573
+ agentId: identity.agentId,
1574
+ conversationId: identity.sessionId,
1575
+ userId: identity.userId,
1576
+ role: "user",
1577
+ content: userContent,
1578
+ model,
1579
+ provider,
1580
+ ...recentMessages.length ? { recentMessages } : {}
1581
+ })
1582
+ );
1583
+ }
1584
+ // -- streaming -------------------------------------------------------------
1585
+ handleLLMNewToken(_token, _idx, runId) {
1586
+ const state = this.runs.get(runId);
1587
+ if (state && state.firstTokenTime === void 0) state.firstTokenTime = Date.now();
1588
+ }
1589
+ // -- ends ------------------------------------------------------------------
1590
+ handleLLMEnd(output, runId) {
1591
+ const state = this.runs.get(runId);
1592
+ if (!state) return;
1593
+ this.runs.delete(runId);
1594
+ const { text, meta } = extractEndMeta(output);
1595
+ const { identity, startTime, firstTokenTime, model, provider, recentMessages } = state;
1596
+ safeObserve(
1597
+ () => getRuntime().observe({
1598
+ agentId: identity.agentId,
1599
+ conversationId: identity.sessionId,
1600
+ userId: identity.userId,
1601
+ role: "assistant",
1602
+ content: text,
1603
+ model,
1604
+ provider,
1605
+ ...meta,
1606
+ latencyMs: Date.now() - startTime,
1607
+ ...firstTokenTime !== void 0 ? { timeToFirstTokenMs: firstTokenTime - startTime } : {},
1608
+ httpStatus: 200,
1609
+ ...recentMessages.length ? { recentMessages } : {}
1610
+ })
1611
+ );
1612
+ }
1613
+ handleLLMError(err, runId) {
1614
+ const state = this.runs.get(runId);
1615
+ if (!state) return;
1616
+ this.runs.delete(runId);
1617
+ const { identity, startTime, model, provider } = state;
1618
+ const status = err?.status;
1619
+ safeObserve(
1620
+ () => getRuntime().observe({
1621
+ agentId: identity.agentId,
1622
+ conversationId: identity.sessionId,
1623
+ userId: identity.userId,
1624
+ role: "assistant",
1625
+ content: "",
1626
+ isError: true,
1627
+ error: err instanceof Error ? err.message : String(err),
1628
+ model,
1629
+ provider,
1630
+ latencyMs: Date.now() - startTime,
1631
+ httpStatus: typeof status === "number" ? status : 500
1632
+ })
1633
+ );
1634
+ }
1635
+ };
1636
+ var _shared;
1637
+ function firstflowCallback() {
1638
+ if (!_shared) _shared = new FirstflowCallbackHandler();
1639
+ return _shared;
1640
+ }
1641
+ function withFirstflow(identity, config) {
1642
+ const handler = firstflowCallback();
1643
+ const existing = Array.isArray(config?.callbacks) ? config.callbacks : config?.callbacks ? [config.callbacks] : [];
1644
+ const callbacks = existing.includes(handler) ? existing : [...existing, handler];
1645
+ return {
1646
+ ...config,
1647
+ callbacks,
1648
+ metadata: {
1649
+ ...config?.metadata ?? {},
1650
+ firstflowAgentId: identity.agentId,
1651
+ sessionId: identity.sessionId,
1652
+ userId: identity.userId
1653
+ }
1654
+ };
1655
+ }
1656
+ // Annotate the CommonJS export names for ESM import in node:
1657
+ 0 && (module.exports = {
1658
+ FirstflowCallbackHandler,
1659
+ firstflowCallback,
1660
+ withFirstflow
1661
+ });