@firstflow/core 0.0.5

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