@raindrop-ai/langchain 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/callback-handler.ts
2
2
  import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
3
3
 
4
- // ../core/dist/chunk-FOHDGBT5.js
4
+ // ../core/dist/chunk-U5HUTMR5.js
5
5
  function getCrypto() {
6
6
  const c = globalThis.crypto;
7
7
  return c;
@@ -56,6 +56,8 @@ function base64Encode(bytes) {
56
56
  }
57
57
  return out;
58
58
  }
59
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
60
+ var MAX_RETRY_DELAY_MS = 3e4;
59
61
  function wait(ms) {
60
62
  return new Promise((resolve) => setTimeout(resolve, ms));
61
63
  }
@@ -63,6 +65,46 @@ function formatEndpoint(endpoint) {
63
65
  if (!endpoint) return void 0;
64
66
  return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
65
67
  }
68
+ function redactUrlForLog(url) {
69
+ try {
70
+ const parsed = new URL(url);
71
+ parsed.username = "";
72
+ parsed.password = "";
73
+ parsed.search = "";
74
+ parsed.hash = "";
75
+ return parsed.toString();
76
+ } catch (e) {
77
+ return "<unparseable-url>";
78
+ }
79
+ }
80
+ var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
81
+ var rateLimitedLogLast = /* @__PURE__ */ new Map();
82
+ function rateLimitedLog(key, log) {
83
+ const now = Date.now();
84
+ const last = rateLimitedLogLast.get(key);
85
+ if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
86
+ return false;
87
+ }
88
+ rateLimitedLogLast.set(key, now);
89
+ log();
90
+ return true;
91
+ }
92
+ async function raceWithTimeout(promise, timeoutMs) {
93
+ let timer;
94
+ const settledInTime = await Promise.race([
95
+ promise.then(
96
+ () => true,
97
+ () => true
98
+ ),
99
+ new Promise((resolve) => {
100
+ var _a;
101
+ timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
102
+ (_a = timer.unref) == null ? void 0 : _a.call(timer);
103
+ })
104
+ ]);
105
+ if (timer) clearTimeout(timer);
106
+ return settledInTime;
107
+ }
66
108
  function parseRetryAfter(headers) {
67
109
  var _a;
68
110
  const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
@@ -79,12 +121,12 @@ function parseRetryAfter(headers) {
79
121
  function getRetryDelayMs(attemptNumber, previousError) {
80
122
  if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
81
123
  const v = previousError.retryAfterMs;
82
- if (typeof v === "number") return Math.max(0, v);
124
+ if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
83
125
  }
84
126
  if (attemptNumber <= 1) return 0;
85
127
  const base = 500;
86
128
  const factor = Math.pow(2, attemptNumber - 2);
87
- return base * factor;
129
+ return Math.min(base * factor, MAX_RETRY_DELAY_MS);
88
130
  }
89
131
  async function withRetry(operation, opName, opts) {
90
132
  const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
@@ -119,7 +161,9 @@ async function withRetry(operation, opName, opts) {
119
161
  throw lastError instanceof Error ? lastError : new Error(String(lastError));
120
162
  }
121
163
  async function postJson(url, body, headers, opts) {
122
- const opName = `POST ${url}`;
164
+ var _a;
165
+ const opName = `POST ${redactUrlForLog(url)}`;
166
+ const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
123
167
  await withRetry(
124
168
  async () => {
125
169
  const resp = await fetch(url, {
@@ -128,7 +172,8 @@ async function postJson(url, body, headers, opts) {
128
172
  "Content-Type": "application/json",
129
173
  ...headers
130
174
  },
131
- body: JSON.stringify(body)
175
+ body: JSON.stringify(body),
176
+ signal: AbortSignal.timeout(timeoutMs)
132
177
  });
133
178
  if (!resp.ok) {
134
179
  const text = await resp.text().catch(() => "");
@@ -145,6 +190,27 @@ async function postJson(url, body, headers, opts) {
145
190
  opts
146
191
  );
147
192
  }
193
+ var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
194
+ var TRUNCATION_MARKER = "...[truncated by raindrop]";
195
+ var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
196
+ function resolveMaxTextFieldChars(value) {
197
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
198
+ return Math.floor(value);
199
+ }
200
+ return currentDefaultMaxTextFieldChars;
201
+ }
202
+ function truncateToLimit(text, limit) {
203
+ if (limit > TRUNCATION_MARKER.length) {
204
+ return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
205
+ }
206
+ return text.slice(0, Math.max(0, limit));
207
+ }
208
+ function capText(value, limit) {
209
+ if (typeof value !== "string") return value;
210
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
211
+ if (value.length <= max) return value;
212
+ return truncateToLimit(value, max);
213
+ }
148
214
  var SpanStatusCode = {
149
215
  UNSET: 0,
150
216
  OK: 1,
@@ -202,6 +268,93 @@ function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", se
202
268
  ]
203
269
  };
204
270
  }
271
+ var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
272
+ var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
273
+ var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
274
+ function readEnvVar(name) {
275
+ var _a;
276
+ try {
277
+ const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
278
+ if (env && typeof env[name] === "string" && env[name].length > 0) {
279
+ return env[name];
280
+ }
281
+ } catch (e) {
282
+ }
283
+ return void 0;
284
+ }
285
+ function readWorkshopEnv() {
286
+ const raw = readEnvVar(WORKSHOP_ENV_VAR);
287
+ if (raw === void 0) return void 0;
288
+ const trimmed = raw.trim();
289
+ if (trimmed.length === 0) return void 0;
290
+ if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
291
+ if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
292
+ if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
293
+ return void 0;
294
+ }
295
+ function isLocalDevHost(hostname) {
296
+ if (!hostname) return false;
297
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
298
+ return true;
299
+ }
300
+ if (hostname.endsWith(".localhost")) return true;
301
+ return false;
302
+ }
303
+ function readRuntimeHostname() {
304
+ try {
305
+ const loc = globalThis == null ? void 0 : globalThis.location;
306
+ if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
307
+ return loc.hostname;
308
+ }
309
+ } catch (e) {
310
+ }
311
+ return void 0;
312
+ }
313
+ function shouldAutoEnableLocalWorkshop() {
314
+ if (isLocalDevHost(readRuntimeHostname())) return true;
315
+ if (readEnvVar("NODE_ENV") === "development") return true;
316
+ return false;
317
+ }
318
+ function resolveLocalDebuggerBaseUrl(baseUrl) {
319
+ var _a, _b, _c;
320
+ if (baseUrl === null) return null;
321
+ if (typeof baseUrl === "string" && baseUrl.length > 0) {
322
+ return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
323
+ }
324
+ const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
325
+ if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
326
+ const workshopEnv = readWorkshopEnv();
327
+ if (workshopEnv === "disable") return null;
328
+ if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
329
+ if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
330
+ if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
331
+ return null;
332
+ }
333
+ function mirrorTraceExportToLocalDebugger(body, options = {}) {
334
+ var _a;
335
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
336
+ if (!baseUrl) return;
337
+ void postJson(`${baseUrl}traces`, body, {}, {
338
+ maxAttempts: 1,
339
+ debug: (_a = options.debug) != null ? _a : false,
340
+ sdkName: options.sdkName
341
+ }).catch(() => {
342
+ });
343
+ }
344
+ function mirrorPartialEventToLocalDebugger(event, options = {}) {
345
+ var _a;
346
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
347
+ if (!baseUrl) return;
348
+ const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
349
+ void postJson(`${baseUrl}events/track_partial`, event, headers, {
350
+ maxAttempts: 1,
351
+ debug: (_a = options.debug) != null ? _a : false,
352
+ sdkName: options.sdkName
353
+ }).catch(() => {
354
+ });
355
+ }
356
+ var SHUTDOWN_DEADLINE_MS = 1e4;
357
+ var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
205
358
  function mergePatches(target, source) {
206
359
  var _a, _b, _c, _d;
207
360
  const out = { ...target, ...source };
@@ -219,7 +372,8 @@ var EventShipper = class {
219
372
  this.sticky = /* @__PURE__ */ new Map();
220
373
  this.timers = /* @__PURE__ */ new Map();
221
374
  this.inFlight = /* @__PURE__ */ new Set();
222
- var _a, _b, _c, _d, _e, _f, _g;
375
+ this.hasShutdown = false;
376
+ var _a, _b, _c, _d, _e, _f, _g, _h;
223
377
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
224
378
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
225
379
  this.enabled = opts.enabled !== false;
@@ -228,11 +382,16 @@ var EventShipper = class {
228
382
  this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
229
383
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
230
384
  this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
385
+ this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
386
+ this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
387
+ if (this.debug && this.localDebuggerUrl) {
388
+ console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
389
+ }
231
390
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
232
391
  this.context = {
233
392
  library: {
234
- name: (_f = opts.libraryName) != null ? _f : "@raindrop-ai/core",
235
- version: (_g = opts.libraryVersion) != null ? _g : "0.0.0"
393
+ name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
394
+ version: (_h = opts.libraryVersion) != null ? _h : "0.0.0"
236
395
  },
237
396
  metadata: {
238
397
  jsRuntime: isNode ? "node" : "web",
@@ -246,10 +405,52 @@ var EventShipper = class {
246
405
  authHeaders() {
247
406
  return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
248
407
  }
408
+ /**
409
+ * Build the retry/timeout options for one POST, honoring the shutdown
410
+ * deadline. Returns `null` when the shutdown drain window is exhausted —
411
+ * the caller must drop the payload (with a rate-limited warning) instead
412
+ * of issuing a request that could outlive process exit.
413
+ *
414
+ * Checked fresh on EVERY send, so a shutdown that begins while the flush
415
+ * path is mid-drain takes effect immediately: no further retries, and the
416
+ * per-attempt timeout is clamped to the remaining window. After
417
+ * `shutdown()` returns (deadline cleared, `hasShutdown` still set),
418
+ * sends — late callers, or flush work the deadline abandoned mid-drain —
419
+ * run as a single short attempt rather than regaining the full retry
420
+ * schedule.
421
+ */
422
+ requestOpts() {
423
+ if (this.shutdownDeadlineAt !== void 0) {
424
+ const remainingMs = this.shutdownDeadlineAt - Date.now();
425
+ if (remainingMs <= 0) return null;
426
+ return {
427
+ maxAttempts: 1,
428
+ debug: this.debug,
429
+ sdkName: this.sdkName,
430
+ timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
431
+ };
432
+ }
433
+ if (this.hasShutdown) {
434
+ return {
435
+ maxAttempts: 1,
436
+ debug: this.debug,
437
+ sdkName: this.sdkName,
438
+ timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
439
+ };
440
+ }
441
+ return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
442
+ }
249
443
  async patch(eventId, patch) {
250
444
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
251
445
  if (!this.enabled) return;
252
446
  if (!eventId || !eventId.trim()) return;
447
+ const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
448
+ if (typeof patch.input === "string" && patch.input.length > maxChars) {
449
+ patch = { ...patch, input: capText(patch.input, maxChars) };
450
+ }
451
+ if (typeof patch.output === "string" && patch.output.length > maxChars) {
452
+ patch = { ...patch, output: capText(patch.output, maxChars) };
453
+ }
253
454
  if (this.debug) {
254
455
  console.log(`${this.prefix} queue patch`, {
255
456
  eventId,
@@ -296,9 +497,16 @@ var EventShipper = class {
296
497
  })));
297
498
  }
298
499
  async shutdown() {
299
- for (const t of this.timers.values()) clearTimeout(t);
300
- this.timers.clear();
301
- await this.flush();
500
+ this.hasShutdown = true;
501
+ this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
502
+ try {
503
+ for (const t of this.timers.values()) clearTimeout(t);
504
+ this.timers.clear();
505
+ const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
506
+ if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
507
+ } finally {
508
+ this.shutdownDeadlineAt = void 0;
509
+ }
302
510
  }
303
511
  async trackSignal(signal) {
304
512
  var _a, _b;
@@ -318,16 +526,21 @@ var EventShipper = class {
318
526
  }
319
527
  }
320
528
  ];
529
+ if (!this.writeKey) return;
321
530
  const url = `${this.baseUrl}signals/track`;
531
+ const opts = this.requestOpts();
532
+ if (!opts) {
533
+ this.warnShutdownDrop("signal");
534
+ return;
535
+ }
322
536
  try {
323
- await postJson(url, body, this.authHeaders(), {
324
- maxAttempts: 3,
325
- debug: this.debug,
326
- sdkName: this.sdkName
327
- });
537
+ await postJson(url, body, this.authHeaders(), opts);
328
538
  } catch (err) {
329
539
  const msg = err instanceof Error ? err.message : String(err);
330
- console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`);
540
+ rateLimitedLog(
541
+ `${this.prefix}.send_signal_failed`,
542
+ () => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
543
+ );
331
544
  }
332
545
  }
333
546
  async identify(users) {
@@ -348,19 +561,30 @@ var EventShipper = class {
348
561
  traits: (_a = user.traits) != null ? _a : {}
349
562
  };
350
563
  });
564
+ if (!this.writeKey) return;
351
565
  if (body.length === 0) return;
352
566
  const url = `${this.baseUrl}users/identify`;
567
+ const opts = this.requestOpts();
568
+ if (!opts) {
569
+ this.warnShutdownDrop("identify");
570
+ return;
571
+ }
353
572
  try {
354
- await postJson(url, body, this.authHeaders(), {
355
- maxAttempts: 3,
356
- debug: this.debug,
357
- sdkName: this.sdkName
358
- });
573
+ await postJson(url, body, this.authHeaders(), opts);
359
574
  } catch (err) {
360
575
  const msg = err instanceof Error ? err.message : String(err);
361
- console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`);
576
+ rateLimitedLog(
577
+ `${this.prefix}.send_identify_failed`,
578
+ () => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
579
+ );
362
580
  }
363
581
  }
582
+ warnShutdownDrop(what) {
583
+ rateLimitedLog(
584
+ `${this.prefix}.shutdown_deadline`,
585
+ () => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
586
+ );
587
+ }
364
588
  async flushOne(eventId) {
365
589
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
366
590
  if (!this.enabled) return;
@@ -424,11 +648,25 @@ var EventShipper = class {
424
648
  endpoint: url
425
649
  });
426
650
  }
427
- const p = postJson(url, payload, this.authHeaders(), {
428
- maxAttempts: 3,
429
- debug: this.debug,
430
- sdkName: this.sdkName
431
- });
651
+ if (this.localDebuggerUrl) {
652
+ mirrorPartialEventToLocalDebugger(payload, {
653
+ baseUrl: this.localDebuggerUrl,
654
+ writeKey: this.writeKey,
655
+ debug: this.debug,
656
+ sdkName: this.sdkName
657
+ });
658
+ }
659
+ if (!this.writeKey) {
660
+ if (!isPending) this.sticky.delete(eventId);
661
+ return;
662
+ }
663
+ const opts = this.requestOpts();
664
+ if (!opts) {
665
+ this.warnShutdownDrop(`track_partial ${eventId}`);
666
+ if (!isPending) this.sticky.delete(eventId);
667
+ return;
668
+ }
669
+ const p = postJson(url, payload, this.authHeaders(), opts);
432
670
  this.inFlight.add(p);
433
671
  try {
434
672
  try {
@@ -438,7 +676,10 @@ var EventShipper = class {
438
676
  }
439
677
  } catch (err) {
440
678
  const msg = err instanceof Error ? err.message : String(err);
441
- console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`);
679
+ rateLimitedLog(
680
+ `${this.prefix}.send_track_partial_failed`,
681
+ () => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
682
+ );
442
683
  }
443
684
  } finally {
444
685
  this.inFlight.delete(p);
@@ -448,11 +689,114 @@ var EventShipper = class {
448
689
  }
449
690
  }
450
691
  };
692
+ var DEFAULT_SECRET_KEY_NAMES = [
693
+ "apikey",
694
+ "apisecret",
695
+ "apitoken",
696
+ "secretaccesskey",
697
+ "sessiontoken",
698
+ "privatekey",
699
+ "privatekeyid",
700
+ "clientsecret",
701
+ "accesstoken",
702
+ "refreshtoken",
703
+ "oauthtoken",
704
+ "bearertoken",
705
+ "authorization",
706
+ "password",
707
+ "passphrase"
708
+ ];
709
+ var REDACTED_PLACEHOLDER = "[REDACTED]";
710
+ function normalizeKeyName(name) {
711
+ return name.toLowerCase().replace(/[-_.]/g, "");
712
+ }
713
+ function redactSecretsInObject(value, options) {
714
+ var _a, _b;
715
+ const normalizedSecretSet = buildSecretSet((_a = options == null ? void 0 : options.secretKeyNames) != null ? _a : DEFAULT_SECRET_KEY_NAMES);
716
+ const placeholder = (_b = options == null ? void 0 : options.placeholder) != null ? _b : REDACTED_PLACEHOLDER;
717
+ const seen = /* @__PURE__ */ new WeakSet();
718
+ const walk = (node) => {
719
+ if (node === null || typeof node !== "object") return node;
720
+ if (seen.has(node)) return "[CIRCULAR]";
721
+ seen.add(node);
722
+ if (Array.isArray(node)) {
723
+ return node.map((item) => walk(item));
724
+ }
725
+ const out = {};
726
+ for (const [k, v] of Object.entries(node)) {
727
+ if (normalizedSecretSet.has(normalizeKeyName(k))) {
728
+ out[k] = placeholder;
729
+ } else {
730
+ out[k] = walk(v);
731
+ }
732
+ }
733
+ return out;
734
+ };
735
+ return walk(value);
736
+ }
737
+ function buildSecretSet(names) {
738
+ const set = /* @__PURE__ */ new Set();
739
+ for (const name of names) set.add(normalizeKeyName(name));
740
+ return set;
741
+ }
742
+ var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
743
+ "ai.request.providerOptions",
744
+ "ai.response.providerMetadata"
745
+ ];
746
+ function defaultTransformSpan(span) {
747
+ const attrs = span.attributes;
748
+ if (!attrs || attrs.length === 0) return span;
749
+ let nextAttrs;
750
+ for (let i = 0; i < attrs.length; i++) {
751
+ const attr = attrs[i];
752
+ const redacted = redactJsonAttributeValue(attr.key, attr.value);
753
+ if (redacted === void 0) continue;
754
+ if (!nextAttrs) nextAttrs = attrs.slice();
755
+ nextAttrs[i] = { key: attr.key, value: redacted };
756
+ }
757
+ if (!nextAttrs) return span;
758
+ return { ...span, attributes: nextAttrs };
759
+ }
760
+ var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
761
+ function redactJsonAttributeValue(key, value) {
762
+ if (!REDACT_JSON_ATTRIBUTE_KEYS.has(key)) return void 0;
763
+ const json = value.stringValue;
764
+ if (typeof json !== "string" || json.length === 0) return void 0;
765
+ let parsed;
766
+ try {
767
+ parsed = JSON.parse(json);
768
+ } catch (e) {
769
+ return void 0;
770
+ }
771
+ const scrubbed = redactSecretsInObject(parsed);
772
+ let scrubbedJson;
773
+ try {
774
+ scrubbedJson = JSON.stringify(scrubbed);
775
+ } catch (e) {
776
+ return void 0;
777
+ }
778
+ if (scrubbedJson === json) return void 0;
779
+ return { stringValue: scrubbedJson };
780
+ }
781
+ function applyOtelSpanAttributeLimit(limit) {
782
+ var _a, _b;
783
+ try {
784
+ const raw = (_b = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env) == null ? void 0 : _b.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
785
+ if (!raw) return limit;
786
+ const parsed = Number.parseInt(raw, 10);
787
+ if (Number.isFinite(parsed) && parsed > 0) {
788
+ return Math.min(limit, parsed);
789
+ }
790
+ } catch (e) {
791
+ }
792
+ return limit;
793
+ }
451
794
  var TraceShipper = class {
452
795
  constructor(opts) {
453
796
  this.queue = [];
454
797
  this.inFlight = /* @__PURE__ */ new Set();
455
- var _a, _b, _c, _d, _e, _f, _g, _h;
798
+ this.hasShutdown = false;
799
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
456
800
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
457
801
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
458
802
  this.enabled = opts.enabled !== false;
@@ -465,6 +809,79 @@ var TraceShipper = class {
465
809
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
466
810
  this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
467
811
  this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
812
+ this.localDebuggerUrl = (_i = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _i : void 0;
813
+ if (this.debug && this.localDebuggerUrl) {
814
+ console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
815
+ }
816
+ this.transformSpanHook = opts.transformSpan;
817
+ this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
818
+ this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
819
+ }
820
+ /**
821
+ * Cap every string attribute value on the span. O(#attributes) length
822
+ * checks; only oversized values pay a slice. Runs AFTER the redaction
823
+ * pipeline so the default secret-scrub still sees parseable JSON in
824
+ * `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
825
+ * first could cut a JSON blob mid-way, fail the parse, and ship secrets
826
+ * in the surviving prefix).
827
+ *
828
+ * A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
829
+ * for span content, matching the Python SDK and the OTel SDK convention.
830
+ */
831
+ capSpanAttributes(span) {
832
+ var _a;
833
+ const maxChars = applyOtelSpanAttributeLimit(
834
+ resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
835
+ );
836
+ const attrs = span.attributes;
837
+ if (!attrs || attrs.length === 0) return span;
838
+ let nextAttrs;
839
+ for (let i = 0; i < attrs.length; i++) {
840
+ const attr = attrs[i];
841
+ const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
842
+ if (typeof value !== "string" || value.length <= maxChars) continue;
843
+ if (!nextAttrs) nextAttrs = attrs.slice();
844
+ nextAttrs[i] = {
845
+ key: attr.key,
846
+ value: { ...attr.value, stringValue: capText(value, maxChars) }
847
+ };
848
+ }
849
+ if (!nextAttrs) return span;
850
+ return { ...span, attributes: nextAttrs };
851
+ }
852
+ /**
853
+ * Apply the user `transformSpan` hook (if any) followed by the default
854
+ * redactor (unless disabled). Returns either the (possibly new) span to
855
+ * ship, or `null` to drop the span entirely.
856
+ *
857
+ * Ordering: user hook runs first so callers can rewrite the span freely
858
+ * (rename attrs, add new ones, scrub things the default doesn't know
859
+ * about). The default redactor then runs on whatever the user produced,
860
+ * acting as the always-on floor for documented BYOK secrets. If the user
861
+ * sets `disableDefaultRedaction: true`, the floor is skipped.
862
+ *
863
+ * Fail-closed: if the user hook throws, the span is dropped — a buggy
864
+ * hook can never accidentally ship raw, un-redacted spans.
865
+ */
866
+ redactSpan(span) {
867
+ let current = span;
868
+ if (this.transformSpanHook) {
869
+ try {
870
+ const result = this.transformSpanHook(current);
871
+ if (result === null) return null;
872
+ if (result !== void 0) current = result;
873
+ } catch (err) {
874
+ if (this.debug) {
875
+ const msg = err instanceof Error ? err.message : String(err);
876
+ console.warn(`${this.prefix} transformSpan hook threw: ${msg}`);
877
+ }
878
+ return null;
879
+ }
880
+ }
881
+ if (!this.disableDefaultRedaction) {
882
+ current = defaultTransformSpan(current);
883
+ }
884
+ return this.capSpanAttributes(current);
468
885
  }
469
886
  isDebugEnabled() {
470
887
  return this.debug;
@@ -481,7 +898,30 @@ var TraceShipper = class {
481
898
  attrString("ai.operationId", args.operationId)
482
899
  ];
483
900
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
484
- return { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
901
+ const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
902
+ this.mirrorToLocalDebugger(
903
+ buildOtlpSpan({
904
+ ids: span.ids,
905
+ name: span.name,
906
+ startTimeUnixNano: span.startTimeUnixNano,
907
+ endTimeUnixNano: span.startTimeUnixNano,
908
+ // placeholder — will be updated on endSpan
909
+ attributes: span.attributes,
910
+ status: { code: SpanStatusCode.UNSET }
911
+ })
912
+ );
913
+ return span;
914
+ }
915
+ mirrorToLocalDebugger(span) {
916
+ if (!this.localDebuggerUrl) return;
917
+ const redacted = this.redactSpan(span);
918
+ if (redacted === null) return;
919
+ const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
920
+ mirrorTraceExportToLocalDebugger(body, {
921
+ baseUrl: this.localDebuggerUrl,
922
+ debug: false,
923
+ sdkName: this.sdkName
924
+ });
485
925
  }
486
926
  endSpan(span, extra) {
487
927
  var _a, _b;
@@ -504,6 +944,7 @@ var TraceShipper = class {
504
944
  status
505
945
  });
506
946
  this.enqueue(otlp);
947
+ this.mirrorToLocalDebugger(otlp);
507
948
  }
508
949
  createSpan(args) {
509
950
  var _a;
@@ -521,6 +962,7 @@ var TraceShipper = class {
521
962
  status: args.status
522
963
  });
523
964
  this.enqueue(otlp);
965
+ this.mirrorToLocalDebugger(otlp);
524
966
  }
525
967
  enqueue(span) {
526
968
  if (!this.enabled) return;
@@ -532,10 +974,12 @@ var TraceShipper = class {
532
974
  )}`
533
975
  );
534
976
  }
977
+ const redacted = this.redactSpan(span);
978
+ if (redacted === null) return;
535
979
  if (this.queue.length >= this.maxQueueSize) {
536
980
  this.queue.shift();
537
981
  }
538
- this.queue.push(span);
982
+ this.queue.push(redacted);
539
983
  if (this.queue.length >= this.maxBatchSize) {
540
984
  void this.flush().catch(() => {
541
985
  });
@@ -557,6 +1001,17 @@ var TraceShipper = class {
557
1001
  }
558
1002
  while (this.queue.length > 0) {
559
1003
  const batch = this.queue.splice(0, this.maxBatchSize);
1004
+ if (!this.writeKey) continue;
1005
+ const opts = this.requestOpts();
1006
+ if (!opts) {
1007
+ rateLimitedLog(
1008
+ `${this.prefix}.shutdown_deadline`,
1009
+ () => console.warn(
1010
+ `${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
1011
+ )
1012
+ );
1013
+ continue;
1014
+ }
560
1015
  const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
561
1016
  const url = `${this.baseUrl}traces`;
562
1017
  if (this.debug) {
@@ -565,11 +1020,7 @@ var TraceShipper = class {
565
1020
  endpoint: url
566
1021
  });
567
1022
  }
568
- const p = postJson(url, body, this.authHeaders(), {
569
- maxAttempts: 3,
570
- debug: this.debug,
571
- sdkName: this.sdkName
572
- });
1023
+ const p = postJson(url, body, this.authHeaders(), opts);
573
1024
  this.inFlight.add(p);
574
1025
  try {
575
1026
  try {
@@ -577,24 +1028,195 @@ var TraceShipper = class {
577
1028
  if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
578
1029
  } catch (err) {
579
1030
  const msg = err instanceof Error ? err.message : String(err);
580
- console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`);
1031
+ rateLimitedLog(
1032
+ `${this.prefix}.send_spans_failed`,
1033
+ () => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
1034
+ );
581
1035
  }
582
1036
  } finally {
583
1037
  this.inFlight.delete(p);
584
1038
  }
585
1039
  }
586
1040
  }
1041
+ /** See EventShipper.requestOpts — same shutdown-budget semantics. */
1042
+ requestOpts() {
1043
+ if (this.shutdownDeadlineAt !== void 0) {
1044
+ const remainingMs = this.shutdownDeadlineAt - Date.now();
1045
+ if (remainingMs <= 0) return null;
1046
+ return {
1047
+ maxAttempts: 1,
1048
+ debug: this.debug,
1049
+ sdkName: this.sdkName,
1050
+ timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
1051
+ };
1052
+ }
1053
+ if (this.hasShutdown) {
1054
+ return {
1055
+ maxAttempts: 1,
1056
+ debug: this.debug,
1057
+ sdkName: this.sdkName,
1058
+ timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
1059
+ };
1060
+ }
1061
+ return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
1062
+ }
587
1063
  async shutdown() {
588
- if (this.timer) {
589
- clearTimeout(this.timer);
590
- this.timer = void 0;
1064
+ this.hasShutdown = true;
1065
+ this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
1066
+ try {
1067
+ if (this.timer) {
1068
+ clearTimeout(this.timer);
1069
+ this.timer = void 0;
1070
+ }
1071
+ const drain = async () => {
1072
+ await this.flush();
1073
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1074
+ })));
1075
+ };
1076
+ const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
1077
+ if (!settled) {
1078
+ rateLimitedLog(
1079
+ `${this.prefix}.shutdown_deadline`,
1080
+ () => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
1081
+ );
1082
+ }
1083
+ } finally {
1084
+ this.shutdownDeadlineAt = void 0;
591
1085
  }
592
- await this.flush();
593
- await Promise.all([...this.inFlight].map((p) => p.catch(() => {
594
- })));
595
1086
  }
596
1087
  };
597
1088
 
1089
+ // ../core/dist/index.node.js
1090
+ import { AsyncLocalStorage } from "async_hooks";
1091
+ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
1092
+
1093
+ // src/truncation.ts
1094
+ var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
1095
+ var DEFAULT_MAX_TEXT_FIELD_CHARS2 = 1e6;
1096
+ var MAX_DEPTH = 12;
1097
+ var configuredMaxTextFieldChars;
1098
+ function setMaxTextFieldChars(limit) {
1099
+ if (limit === void 0) return;
1100
+ if (typeof limit === "number" && Number.isFinite(limit) && limit > 0) {
1101
+ configuredMaxTextFieldChars = Math.floor(limit);
1102
+ } else {
1103
+ console.warn(`[raindrop-ai] maxTextFieldChars=${String(limit)} ignored; must be > 0`);
1104
+ }
1105
+ }
1106
+ function otelEnvLimit() {
1107
+ var _a;
1108
+ if (typeof process === "undefined") return void 0;
1109
+ const raw = (_a = process.env) == null ? void 0 : _a.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
1110
+ if (!raw) return void 0;
1111
+ const parsed = Number.parseInt(raw, 10);
1112
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
1113
+ }
1114
+ function effectiveMaxTextFieldChars() {
1115
+ const base = configuredMaxTextFieldChars != null ? configuredMaxTextFieldChars : DEFAULT_MAX_TEXT_FIELD_CHARS2;
1116
+ const env = otelEnvLimit();
1117
+ return env !== void 0 && env < base ? env : base;
1118
+ }
1119
+ function truncateToLimit2(text, limit) {
1120
+ if (limit > TRUNCATION_MARKER2.length) {
1121
+ return text.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
1122
+ }
1123
+ return text.slice(0, limit);
1124
+ }
1125
+ function capText2(value, limit) {
1126
+ if (typeof value !== "string") return value;
1127
+ const max = limit != null ? limit : effectiveMaxTextFieldChars();
1128
+ if (value.length <= max) return value;
1129
+ return truncateToLimit2(value, max);
1130
+ }
1131
+ function boundedClone2(value, budget, depth) {
1132
+ var _a, _b;
1133
+ if (budget.remaining <= 0) return TRUNCATION_MARKER2;
1134
+ if (typeof value === "string") {
1135
+ if (value.length > budget.remaining) {
1136
+ const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
1137
+ budget.remaining = 0;
1138
+ return taken;
1139
+ }
1140
+ budget.remaining -= Math.max(value.length, 1);
1141
+ return value;
1142
+ }
1143
+ if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1144
+ budget.remaining -= 8;
1145
+ return value;
1146
+ }
1147
+ if (typeof value !== "object") {
1148
+ budget.remaining -= 1;
1149
+ return value;
1150
+ }
1151
+ if (value instanceof Uint8Array) {
1152
+ const maxBytes = Math.max(0, Math.ceil(budget.remaining * 3 / 4));
1153
+ if (value.byteLength > maxBytes) {
1154
+ const taken = base64Encode(value.subarray(0, maxBytes)) + TRUNCATION_MARKER2;
1155
+ budget.remaining = 0;
1156
+ return taken;
1157
+ }
1158
+ const encoded = base64Encode(value);
1159
+ budget.remaining -= Math.max(encoded.length, 1);
1160
+ return encoded;
1161
+ }
1162
+ if (depth >= MAX_DEPTH) {
1163
+ budget.remaining -= 16;
1164
+ const name = (_b = (_a = value.constructor) == null ? void 0 : _a.name) != null ? _b : "object";
1165
+ return `<max depth: ${name}>`;
1166
+ }
1167
+ const toJSON = value.toJSON;
1168
+ if (typeof toJSON === "function") {
1169
+ budget.remaining -= 2;
1170
+ return boundedClone2(toJSON.call(value), budget, depth + 1);
1171
+ }
1172
+ if (Array.isArray(value)) {
1173
+ budget.remaining -= 2;
1174
+ const out2 = [];
1175
+ for (const item of value) {
1176
+ if (budget.remaining <= 0) {
1177
+ out2.push(TRUNCATION_MARKER2);
1178
+ break;
1179
+ }
1180
+ out2.push(boundedClone2(item, budget, depth + 1));
1181
+ }
1182
+ return out2;
1183
+ }
1184
+ budget.remaining -= 2;
1185
+ const out = {};
1186
+ for (const key in value) {
1187
+ if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
1188
+ if (budget.remaining <= 0) {
1189
+ out["..."] = TRUNCATION_MARKER2;
1190
+ break;
1191
+ }
1192
+ let cappedKey = key;
1193
+ if (key.length > budget.remaining) {
1194
+ cappedKey = key.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
1195
+ budget.remaining = 0;
1196
+ out[cappedKey] = TRUNCATION_MARKER2;
1197
+ break;
1198
+ }
1199
+ budget.remaining -= Math.max(key.length, 1);
1200
+ out[cappedKey] = boundedClone2(value[key], budget, depth + 1);
1201
+ }
1202
+ return out;
1203
+ }
1204
+ function boundedStringify(value, limit) {
1205
+ const max = limit != null ? limit : effectiveMaxTextFieldChars();
1206
+ try {
1207
+ if (typeof value === "string") {
1208
+ const text2 = JSON.stringify(capText2(value, max));
1209
+ return text2.length <= max ? text2 : truncateToLimit2(text2, max);
1210
+ }
1211
+ const pruned = boundedClone2(value, { remaining: max + TRUNCATION_MARKER2.length + 256 }, 0);
1212
+ const text = JSON.stringify(pruned);
1213
+ if (text === void 0) return void 0;
1214
+ return text.length <= max ? text : truncateToLimit2(text, max);
1215
+ } catch (e) {
1216
+ return void 0;
1217
+ }
1218
+ }
1219
+
598
1220
  // src/callback-handler.ts
599
1221
  var LANGGRAPH_INTERNAL_NAMES = /* @__PURE__ */ new Set([
600
1222
  "LangGraph",
@@ -684,7 +1306,7 @@ var RaindropCallbackHandler = class extends BaseCallbackHandler {
684
1306
  await this.eventShipper.patch(eventId, {
685
1307
  userId: this.userId,
686
1308
  convoId: this.convoId,
687
- input: prompts.join("\n"),
1309
+ input: capText2(prompts.join("\n")),
688
1310
  properties: buildProperties(tags, metadata),
689
1311
  isPending: true
690
1312
  });
@@ -714,7 +1336,7 @@ var RaindropCallbackHandler = class extends BaseCallbackHandler {
714
1336
  return role === "human";
715
1337
  });
716
1338
  const lastMsg = userMessages.length > 0 ? userMessages[userMessages.length - 1] : allMessages[allMessages.length - 1];
717
- const inputText = lastMsg ? typeof lastMsg.content === "string" ? lastMsg.content : safeStringify(lastMsg.content) : void 0;
1339
+ const inputText = lastMsg ? typeof lastMsg.content === "string" ? capText2(lastMsg.content) : safeStringify(lastMsg.content) : void 0;
718
1340
  await this.eventShipper.patch(eventId, {
719
1341
  userId: this.userId,
720
1342
  convoId: this.convoId,
@@ -744,7 +1366,7 @@ var RaindropCallbackHandler = class extends BaseCallbackHandler {
744
1366
  if (promptTokens != null) properties["ai.usage.prompt_tokens"] = promptTokens;
745
1367
  if (completionTokens != null) properties["ai.usage.completion_tokens"] = completionTokens;
746
1368
  await this.finalizeEventIfRoot(runId, {
747
- output: outputText,
1369
+ output: capText2(outputText),
748
1370
  model,
749
1371
  properties: Object.keys(properties).length > 0 ? properties : void 0
750
1372
  });
@@ -837,7 +1459,7 @@ var RaindropCallbackHandler = class extends BaseCallbackHandler {
837
1459
  try {
838
1460
  const span = this.spans.get(runId);
839
1461
  if (span) {
840
- const outputStr = typeof output === "string" ? output : safeStringify(output);
1462
+ const outputStr = typeof output === "string" ? output : safeStringify(output, 4096);
841
1463
  this.traceShipper.endSpan(span, {
842
1464
  attributes: [attrString("tool.output", truncate(outputStr, 4096))]
843
1465
  });
@@ -922,7 +1544,7 @@ var RaindropCallbackHandler = class extends BaseCallbackHandler {
922
1544
  attributes: [
923
1545
  attrString("agent.tool", action.tool),
924
1546
  attrString("agent.tool_input", truncate(
925
- typeof action.toolInput === "string" ? action.toolInput : safeStringify(action.toolInput),
1547
+ typeof action.toolInput === "string" ? action.toolInput : safeStringify(action.toolInput, 4096),
926
1548
  4096
927
1549
  ))
928
1550
  ]
@@ -964,13 +1586,10 @@ function buildProperties(tags, metadata) {
964
1586
  if (metadata) Object.assign(props, metadata);
965
1587
  return props;
966
1588
  }
967
- function safeStringify(value) {
1589
+ function safeStringify(value, limit) {
968
1590
  var _a;
969
- try {
970
- return (_a = JSON.stringify(value)) != null ? _a : "";
971
- } catch (e) {
972
- return String(value);
973
- }
1591
+ if (value === void 0) return "";
1592
+ return (_a = boundedStringify(value, limit)) != null ? _a : String(value);
974
1593
  }
975
1594
  function truncate(str, maxLen) {
976
1595
  if (str.length <= maxLen) return str;
@@ -989,18 +1608,28 @@ function extractLLMMetadata(output) {
989
1608
  const usage = (_g = usageMeta != null ? usageMeta : tokenUsage) != null ? _g : estimatedTokens;
990
1609
  const promptTokens = (_i = (_h = usage == null ? void 0 : usage.input_tokens) != null ? _h : usage == null ? void 0 : usage.prompt_tokens) != null ? _i : usage == null ? void 0 : usage.promptTokens;
991
1610
  const completionTokens = (_k = (_j = usage == null ? void 0 : usage.output_tokens) != null ? _j : usage == null ? void 0 : usage.completion_tokens) != null ? _k : usage == null ? void 0 : usage.completionTokens;
992
- const outputText = (_m = (_l = gen == null ? void 0 : gen.text) != null ? _l : typeof (message == null ? void 0 : message.content) === "string" ? message.content : void 0) != null ? _m : (message == null ? void 0 : message.content) ? JSON.stringify(message.content) : void 0;
1611
+ const outputText = (_m = (_l = gen == null ? void 0 : gen.text) != null ? _l : typeof (message == null ? void 0 : message.content) === "string" ? message.content : void 0) != null ? _m : (message == null ? void 0 : message.content) ? boundedStringify(message.content) : void 0;
993
1612
  return { model, promptTokens, completionTokens, outputText };
994
1613
  }
995
1614
 
996
1615
  // package.json
997
1616
  var package_default = {
998
1617
  name: "@raindrop-ai/langchain",
999
- version: "0.0.2",
1618
+ version: "0.0.4",
1000
1619
  description: "Raindrop integration for LangChain",
1001
1620
  main: "dist/index.js",
1002
1621
  module: "dist/index.mjs",
1003
1622
  types: "dist/index.d.ts",
1623
+ license: "MIT",
1624
+ repository: {
1625
+ type: "git",
1626
+ url: "git+https://github.com/raindrop-ai/raindrop-js.git",
1627
+ directory: "packages/langchain"
1628
+ },
1629
+ homepage: "https://github.com/raindrop-ai/raindrop-js/tree/main/packages/langchain#readme",
1630
+ bugs: {
1631
+ url: "https://github.com/raindrop-ai/raindrop-js/issues"
1632
+ },
1004
1633
  exports: {
1005
1634
  ".": {
1006
1635
  types: "./dist/index.d.ts",
@@ -1009,7 +1638,9 @@ var package_default = {
1009
1638
  }
1010
1639
  },
1011
1640
  sideEffects: false,
1012
- files: ["dist/**"],
1641
+ files: [
1642
+ "dist/**"
1643
+ ],
1013
1644
  scripts: {
1014
1645
  build: "tsup",
1015
1646
  dev: "tsup --watch",
@@ -1029,11 +1660,20 @@ var package_default = {
1029
1660
  vitest: "^2.1.9"
1030
1661
  },
1031
1662
  tsup: {
1032
- entry: ["src/index.ts"],
1033
- format: ["cjs", "esm"],
1034
- dts: { resolve: true },
1663
+ entry: [
1664
+ "src/index.ts"
1665
+ ],
1666
+ format: [
1667
+ "cjs",
1668
+ "esm"
1669
+ ],
1670
+ dts: {
1671
+ resolve: true
1672
+ },
1035
1673
  clean: true,
1036
- noExternal: ["@raindrop-ai/core"]
1674
+ noExternal: [
1675
+ "@raindrop-ai/core"
1676
+ ]
1037
1677
  },
1038
1678
  publishConfig: {
1039
1679
  access: "public"
@@ -1049,6 +1689,7 @@ function createRaindropLangChain(opts) {
1049
1689
  var _a, _b;
1050
1690
  const writeKey = opts.writeKey;
1051
1691
  const enabled = !!writeKey;
1692
+ setMaxTextFieldChars(opts.maxTextFieldChars);
1052
1693
  if (!writeKey) {
1053
1694
  console.warn("[raindrop-ai/langchain] writeKey not provided; telemetry shipping is disabled");
1054
1695
  }