@raindrop-ai/ai-sdk 0.0.33 → 0.0.34

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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- // ../core/dist/chunk-VUNUOE2X.js
3
+ // ../core/dist/chunk-U5HUTMR5.js
4
4
  function getCrypto() {
5
5
  const c = globalThis.crypto;
6
6
  return c;
@@ -55,6 +55,8 @@ function base64Encode(bytes) {
55
55
  }
56
56
  return out;
57
57
  }
58
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
59
+ var MAX_RETRY_DELAY_MS = 3e4;
58
60
  function wait(ms) {
59
61
  return new Promise((resolve) => setTimeout(resolve, ms));
60
62
  }
@@ -62,6 +64,46 @@ function formatEndpoint(endpoint) {
62
64
  if (!endpoint) return void 0;
63
65
  return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
64
66
  }
67
+ function redactUrlForLog(url) {
68
+ try {
69
+ const parsed = new URL(url);
70
+ parsed.username = "";
71
+ parsed.password = "";
72
+ parsed.search = "";
73
+ parsed.hash = "";
74
+ return parsed.toString();
75
+ } catch (e) {
76
+ return "<unparseable-url>";
77
+ }
78
+ }
79
+ var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
80
+ var rateLimitedLogLast = /* @__PURE__ */ new Map();
81
+ function rateLimitedLog(key, log) {
82
+ const now = Date.now();
83
+ const last = rateLimitedLogLast.get(key);
84
+ if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
85
+ return false;
86
+ }
87
+ rateLimitedLogLast.set(key, now);
88
+ log();
89
+ return true;
90
+ }
91
+ async function raceWithTimeout(promise, timeoutMs) {
92
+ let timer;
93
+ const settledInTime = await Promise.race([
94
+ promise.then(
95
+ () => true,
96
+ () => true
97
+ ),
98
+ new Promise((resolve) => {
99
+ var _a;
100
+ timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
101
+ (_a = timer.unref) == null ? void 0 : _a.call(timer);
102
+ })
103
+ ]);
104
+ if (timer) clearTimeout(timer);
105
+ return settledInTime;
106
+ }
65
107
  function parseRetryAfter(headers) {
66
108
  var _a;
67
109
  const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
@@ -78,12 +120,12 @@ function parseRetryAfter(headers) {
78
120
  function getRetryDelayMs(attemptNumber, previousError) {
79
121
  if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
80
122
  const v = previousError.retryAfterMs;
81
- if (typeof v === "number") return Math.max(0, v);
123
+ if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
82
124
  }
83
125
  if (attemptNumber <= 1) return 0;
84
126
  const base = 500;
85
127
  const factor = Math.pow(2, attemptNumber - 2);
86
- return base * factor;
128
+ return Math.min(base * factor, MAX_RETRY_DELAY_MS);
87
129
  }
88
130
  async function withRetry(operation, opName2, opts) {
89
131
  const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
@@ -118,7 +160,9 @@ async function withRetry(operation, opName2, opts) {
118
160
  throw lastError instanceof Error ? lastError : new Error(String(lastError));
119
161
  }
120
162
  async function postJson(url, body, headers, opts) {
121
- const opName2 = `POST ${url}`;
163
+ var _a;
164
+ const opName2 = `POST ${redactUrlForLog(url)}`;
165
+ const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
122
166
  await withRetry(
123
167
  async () => {
124
168
  const resp = await fetch(url, {
@@ -127,7 +171,8 @@ async function postJson(url, body, headers, opts) {
127
171
  "Content-Type": "application/json",
128
172
  ...headers
129
173
  },
130
- body: JSON.stringify(body)
174
+ body: JSON.stringify(body),
175
+ signal: AbortSignal.timeout(timeoutMs)
131
176
  });
132
177
  if (!resp.ok) {
133
178
  const text = await resp.text().catch(() => "");
@@ -144,6 +189,27 @@ async function postJson(url, body, headers, opts) {
144
189
  opts
145
190
  );
146
191
  }
192
+ var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
193
+ var TRUNCATION_MARKER = "...[truncated by raindrop]";
194
+ var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
195
+ function resolveMaxTextFieldChars(value) {
196
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
197
+ return Math.floor(value);
198
+ }
199
+ return currentDefaultMaxTextFieldChars;
200
+ }
201
+ function truncateToLimit(text, limit) {
202
+ if (limit > TRUNCATION_MARKER.length) {
203
+ return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
204
+ }
205
+ return text.slice(0, Math.max(0, limit));
206
+ }
207
+ function capText(value, limit) {
208
+ if (typeof value !== "string") return value;
209
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
210
+ if (value.length <= max) return value;
211
+ return truncateToLimit(value, max);
212
+ }
147
213
  var SpanStatusCode = {
148
214
  UNSET: 0,
149
215
  ERROR: 2
@@ -331,6 +397,8 @@ function sendLocalDebuggerLiveEvent(event, options = {}) {
331
397
  ).catch(() => {
332
398
  });
333
399
  }
400
+ var SHUTDOWN_DEADLINE_MS = 1e4;
401
+ var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
334
402
  function mergePatches(target, source) {
335
403
  var _a, _b, _c, _d;
336
404
  const out = { ...target, ...source };
@@ -348,6 +416,7 @@ var EventShipper = class {
348
416
  this.sticky = /* @__PURE__ */ new Map();
349
417
  this.timers = /* @__PURE__ */ new Map();
350
418
  this.inFlight = /* @__PURE__ */ new Set();
419
+ this.hasShutdown = false;
351
420
  var _a, _b, _c, _d, _e, _f, _g, _h;
352
421
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
353
422
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
@@ -357,6 +426,7 @@ var EventShipper = class {
357
426
  this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
358
427
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
359
428
  this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
429
+ this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
360
430
  this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
361
431
  if (this.debug && this.localDebuggerUrl) {
362
432
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
@@ -379,10 +449,52 @@ var EventShipper = class {
379
449
  authHeaders() {
380
450
  return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
381
451
  }
452
+ /**
453
+ * Build the retry/timeout options for one POST, honoring the shutdown
454
+ * deadline. Returns `null` when the shutdown drain window is exhausted —
455
+ * the caller must drop the payload (with a rate-limited warning) instead
456
+ * of issuing a request that could outlive process exit.
457
+ *
458
+ * Checked fresh on EVERY send, so a shutdown that begins while the flush
459
+ * path is mid-drain takes effect immediately: no further retries, and the
460
+ * per-attempt timeout is clamped to the remaining window. After
461
+ * `shutdown()` returns (deadline cleared, `hasShutdown` still set),
462
+ * sends — late callers, or flush work the deadline abandoned mid-drain —
463
+ * run as a single short attempt rather than regaining the full retry
464
+ * schedule.
465
+ */
466
+ requestOpts() {
467
+ if (this.shutdownDeadlineAt !== void 0) {
468
+ const remainingMs = this.shutdownDeadlineAt - Date.now();
469
+ if (remainingMs <= 0) return null;
470
+ return {
471
+ maxAttempts: 1,
472
+ debug: this.debug,
473
+ sdkName: this.sdkName,
474
+ timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
475
+ };
476
+ }
477
+ if (this.hasShutdown) {
478
+ return {
479
+ maxAttempts: 1,
480
+ debug: this.debug,
481
+ sdkName: this.sdkName,
482
+ timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
483
+ };
484
+ }
485
+ return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
486
+ }
382
487
  async patch(eventId, patch) {
383
488
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
384
489
  if (!this.enabled) return;
385
490
  if (!eventId || !eventId.trim()) return;
491
+ const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
492
+ if (typeof patch.input === "string" && patch.input.length > maxChars) {
493
+ patch = { ...patch, input: capText(patch.input, maxChars) };
494
+ }
495
+ if (typeof patch.output === "string" && patch.output.length > maxChars) {
496
+ patch = { ...patch, output: capText(patch.output, maxChars) };
497
+ }
386
498
  if (this.debug) {
387
499
  console.log(`${this.prefix} queue patch`, {
388
500
  eventId,
@@ -429,9 +541,16 @@ var EventShipper = class {
429
541
  })));
430
542
  }
431
543
  async shutdown() {
432
- for (const t of this.timers.values()) clearTimeout(t);
433
- this.timers.clear();
434
- await this.flush();
544
+ this.hasShutdown = true;
545
+ this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
546
+ try {
547
+ for (const t of this.timers.values()) clearTimeout(t);
548
+ this.timers.clear();
549
+ const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
550
+ if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
551
+ } finally {
552
+ this.shutdownDeadlineAt = void 0;
553
+ }
435
554
  }
436
555
  async trackSignal(signal) {
437
556
  var _a, _b;
@@ -453,15 +572,19 @@ var EventShipper = class {
453
572
  ];
454
573
  if (!this.writeKey) return;
455
574
  const url = `${this.baseUrl}signals/track`;
575
+ const opts = this.requestOpts();
576
+ if (!opts) {
577
+ this.warnShutdownDrop("signal");
578
+ return;
579
+ }
456
580
  try {
457
- await postJson(url, body, this.authHeaders(), {
458
- maxAttempts: 3,
459
- debug: this.debug,
460
- sdkName: this.sdkName
461
- });
581
+ await postJson(url, body, this.authHeaders(), opts);
462
582
  } catch (err) {
463
583
  const msg = err instanceof Error ? err.message : String(err);
464
- console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`);
584
+ rateLimitedLog(
585
+ `${this.prefix}.send_signal_failed`,
586
+ () => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
587
+ );
465
588
  }
466
589
  }
467
590
  async identify(users) {
@@ -485,17 +608,27 @@ var EventShipper = class {
485
608
  if (!this.writeKey) return;
486
609
  if (body.length === 0) return;
487
610
  const url = `${this.baseUrl}users/identify`;
611
+ const opts = this.requestOpts();
612
+ if (!opts) {
613
+ this.warnShutdownDrop("identify");
614
+ return;
615
+ }
488
616
  try {
489
- await postJson(url, body, this.authHeaders(), {
490
- maxAttempts: 3,
491
- debug: this.debug,
492
- sdkName: this.sdkName
493
- });
617
+ await postJson(url, body, this.authHeaders(), opts);
494
618
  } catch (err) {
495
619
  const msg = err instanceof Error ? err.message : String(err);
496
- console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`);
620
+ rateLimitedLog(
621
+ `${this.prefix}.send_identify_failed`,
622
+ () => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
623
+ );
497
624
  }
498
625
  }
626
+ warnShutdownDrop(what) {
627
+ rateLimitedLog(
628
+ `${this.prefix}.shutdown_deadline`,
629
+ () => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
630
+ );
631
+ }
499
632
  async flushOne(eventId) {
500
633
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
501
634
  if (!this.enabled) return;
@@ -571,11 +704,13 @@ var EventShipper = class {
571
704
  if (!isPending) this.sticky.delete(eventId);
572
705
  return;
573
706
  }
574
- const p = postJson(url, payload, this.authHeaders(), {
575
- maxAttempts: 3,
576
- debug: this.debug,
577
- sdkName: this.sdkName
578
- });
707
+ const opts = this.requestOpts();
708
+ if (!opts) {
709
+ this.warnShutdownDrop(`track_partial ${eventId}`);
710
+ if (!isPending) this.sticky.delete(eventId);
711
+ return;
712
+ }
713
+ const p = postJson(url, payload, this.authHeaders(), opts);
579
714
  this.inFlight.add(p);
580
715
  try {
581
716
  try {
@@ -585,7 +720,10 @@ var EventShipper = class {
585
720
  }
586
721
  } catch (err) {
587
722
  const msg = err instanceof Error ? err.message : String(err);
588
- console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`);
723
+ rateLimitedLog(
724
+ `${this.prefix}.send_track_partial_failed`,
725
+ () => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
726
+ );
589
727
  }
590
728
  } finally {
591
729
  this.inFlight.delete(p);
@@ -684,10 +822,24 @@ function redactJsonAttributeValue(key, value) {
684
822
  if (scrubbedJson === json) return void 0;
685
823
  return { stringValue: scrubbedJson };
686
824
  }
825
+ function applyOtelSpanAttributeLimit(limit) {
826
+ var _a, _b;
827
+ try {
828
+ 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;
829
+ if (!raw) return limit;
830
+ const parsed = Number.parseInt(raw, 10);
831
+ if (Number.isFinite(parsed) && parsed > 0) {
832
+ return Math.min(limit, parsed);
833
+ }
834
+ } catch (e) {
835
+ }
836
+ return limit;
837
+ }
687
838
  var TraceShipper = class {
688
839
  constructor(opts) {
689
840
  this.queue = [];
690
841
  this.inFlight = /* @__PURE__ */ new Set();
842
+ this.hasShutdown = false;
691
843
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
692
844
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
693
845
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
@@ -707,6 +859,39 @@ var TraceShipper = class {
707
859
  }
708
860
  this.transformSpanHook = opts.transformSpan;
709
861
  this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
862
+ this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
863
+ }
864
+ /**
865
+ * Cap every string attribute value on the span. O(#attributes) length
866
+ * checks; only oversized values pay a slice. Runs AFTER the redaction
867
+ * pipeline so the default secret-scrub still sees parseable JSON in
868
+ * `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
869
+ * first could cut a JSON blob mid-way, fail the parse, and ship secrets
870
+ * in the surviving prefix).
871
+ *
872
+ * A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
873
+ * for span content, matching the Python SDK and the OTel SDK convention.
874
+ */
875
+ capSpanAttributes(span) {
876
+ var _a;
877
+ const maxChars = applyOtelSpanAttributeLimit(
878
+ resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
879
+ );
880
+ const attrs = span.attributes;
881
+ if (!attrs || attrs.length === 0) return span;
882
+ let nextAttrs;
883
+ for (let i = 0; i < attrs.length; i++) {
884
+ const attr = attrs[i];
885
+ const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
886
+ if (typeof value !== "string" || value.length <= maxChars) continue;
887
+ if (!nextAttrs) nextAttrs = attrs.slice();
888
+ nextAttrs[i] = {
889
+ key: attr.key,
890
+ value: { ...attr.value, stringValue: capText(value, maxChars) }
891
+ };
892
+ }
893
+ if (!nextAttrs) return span;
894
+ return { ...span, attributes: nextAttrs };
710
895
  }
711
896
  /**
712
897
  * Apply the user `transformSpan` hook (if any) followed by the default
@@ -740,7 +925,7 @@ var TraceShipper = class {
740
925
  if (!this.disableDefaultRedaction) {
741
926
  current = defaultTransformSpan(current);
742
927
  }
743
- return current;
928
+ return this.capSpanAttributes(current);
744
929
  }
745
930
  isDebugEnabled() {
746
931
  return this.debug;
@@ -861,6 +1046,16 @@ var TraceShipper = class {
861
1046
  while (this.queue.length > 0) {
862
1047
  const batch = this.queue.splice(0, this.maxBatchSize);
863
1048
  if (!this.writeKey) continue;
1049
+ const opts = this.requestOpts();
1050
+ if (!opts) {
1051
+ rateLimitedLog(
1052
+ `${this.prefix}.shutdown_deadline`,
1053
+ () => console.warn(
1054
+ `${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
1055
+ )
1056
+ );
1057
+ continue;
1058
+ }
864
1059
  const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
865
1060
  const url = `${this.baseUrl}traces`;
866
1061
  if (this.debug) {
@@ -869,11 +1064,7 @@ var TraceShipper = class {
869
1064
  endpoint: url
870
1065
  });
871
1066
  }
872
- const p = postJson(url, body, this.authHeaders(), {
873
- maxAttempts: 3,
874
- debug: this.debug,
875
- sdkName: this.sdkName
876
- });
1067
+ const p = postJson(url, body, this.authHeaders(), opts);
877
1068
  this.inFlight.add(p);
878
1069
  try {
879
1070
  try {
@@ -881,21 +1072,61 @@ var TraceShipper = class {
881
1072
  if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
882
1073
  } catch (err) {
883
1074
  const msg = err instanceof Error ? err.message : String(err);
884
- console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`);
1075
+ rateLimitedLog(
1076
+ `${this.prefix}.send_spans_failed`,
1077
+ () => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
1078
+ );
885
1079
  }
886
1080
  } finally {
887
1081
  this.inFlight.delete(p);
888
1082
  }
889
1083
  }
890
1084
  }
1085
+ /** See EventShipper.requestOpts — same shutdown-budget semantics. */
1086
+ requestOpts() {
1087
+ if (this.shutdownDeadlineAt !== void 0) {
1088
+ const remainingMs = this.shutdownDeadlineAt - Date.now();
1089
+ if (remainingMs <= 0) return null;
1090
+ return {
1091
+ maxAttempts: 1,
1092
+ debug: this.debug,
1093
+ sdkName: this.sdkName,
1094
+ timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
1095
+ };
1096
+ }
1097
+ if (this.hasShutdown) {
1098
+ return {
1099
+ maxAttempts: 1,
1100
+ debug: this.debug,
1101
+ sdkName: this.sdkName,
1102
+ timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
1103
+ };
1104
+ }
1105
+ return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
1106
+ }
891
1107
  async shutdown() {
892
- if (this.timer) {
893
- clearTimeout(this.timer);
894
- this.timer = void 0;
1108
+ this.hasShutdown = true;
1109
+ this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
1110
+ try {
1111
+ if (this.timer) {
1112
+ clearTimeout(this.timer);
1113
+ this.timer = void 0;
1114
+ }
1115
+ const drain = async () => {
1116
+ await this.flush();
1117
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1118
+ })));
1119
+ };
1120
+ const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
1121
+ if (!settled) {
1122
+ rateLimitedLog(
1123
+ `${this.prefix}.shutdown_deadline`,
1124
+ () => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
1125
+ );
1126
+ }
1127
+ } finally {
1128
+ this.shutdownDeadlineAt = void 0;
895
1129
  }
896
- await this.flush();
897
- await Promise.all([...this.inFlight].map((p) => p.catch(() => {
898
- })));
899
1130
  }
900
1131
  };
901
1132
  var NOOP_SPAN = {
@@ -1019,7 +1250,7 @@ async function* asyncGeneratorWithCurrent(span, gen) {
1019
1250
  // package.json
1020
1251
  var package_default = {
1021
1252
  name: "@raindrop-ai/ai-sdk",
1022
- version: "0.0.33"};
1253
+ version: "0.0.34"};
1023
1254
 
1024
1255
  // src/internal/version.ts
1025
1256
  var libraryName = package_default.name;
@@ -1038,6 +1269,133 @@ var EventShipper2 = class extends EventShipper {
1038
1269
  }
1039
1270
  };
1040
1271
 
1272
+ // src/internal/truncation.ts
1273
+ var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
1274
+ var DEFAULT_MAX_TEXT_FIELD_CHARS2 = 1e6;
1275
+ var MAX_DEPTH = 12;
1276
+ var configuredMaxTextFieldChars;
1277
+ function setMaxTextFieldChars(limit) {
1278
+ if (limit === void 0) return;
1279
+ if (typeof limit === "number" && Number.isFinite(limit) && limit > 0) {
1280
+ configuredMaxTextFieldChars = Math.floor(limit);
1281
+ } else {
1282
+ console.warn(`[raindrop-ai] maxTextFieldChars=${String(limit)} ignored; must be > 0`);
1283
+ }
1284
+ }
1285
+ function otelEnvLimit() {
1286
+ var _a;
1287
+ if (typeof process === "undefined") return void 0;
1288
+ const raw = (_a = process.env) == null ? void 0 : _a.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
1289
+ if (!raw) return void 0;
1290
+ const parsed = Number.parseInt(raw, 10);
1291
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
1292
+ }
1293
+ function effectiveMaxTextFieldChars() {
1294
+ const base = configuredMaxTextFieldChars != null ? configuredMaxTextFieldChars : DEFAULT_MAX_TEXT_FIELD_CHARS2;
1295
+ const env = otelEnvLimit();
1296
+ return env !== void 0 && env < base ? env : base;
1297
+ }
1298
+ function truncateToLimit2(text, limit) {
1299
+ if (limit > TRUNCATION_MARKER2.length) {
1300
+ return text.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
1301
+ }
1302
+ return text.slice(0, limit);
1303
+ }
1304
+ function capText2(value, limit) {
1305
+ if (typeof value !== "string") return value;
1306
+ const max = limit != null ? limit : effectiveMaxTextFieldChars();
1307
+ if (value.length <= max) return value;
1308
+ return truncateToLimit2(value, max);
1309
+ }
1310
+ function boundedClone2(value, budget, depth) {
1311
+ var _a, _b;
1312
+ if (budget.remaining <= 0) return TRUNCATION_MARKER2;
1313
+ if (typeof value === "string") {
1314
+ if (value.length > budget.remaining) {
1315
+ const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
1316
+ budget.remaining = 0;
1317
+ return taken;
1318
+ }
1319
+ budget.remaining -= Math.max(value.length, 1);
1320
+ return value;
1321
+ }
1322
+ if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1323
+ budget.remaining -= 8;
1324
+ return value;
1325
+ }
1326
+ if (typeof value !== "object") {
1327
+ budget.remaining -= 1;
1328
+ return value;
1329
+ }
1330
+ if (value instanceof Uint8Array) {
1331
+ const maxBytes = Math.max(0, Math.ceil(budget.remaining * 3 / 4));
1332
+ if (value.byteLength > maxBytes) {
1333
+ const taken = base64Encode(value.subarray(0, maxBytes)) + TRUNCATION_MARKER2;
1334
+ budget.remaining = 0;
1335
+ return taken;
1336
+ }
1337
+ const encoded = base64Encode(value);
1338
+ budget.remaining -= Math.max(encoded.length, 1);
1339
+ return encoded;
1340
+ }
1341
+ if (depth >= MAX_DEPTH) {
1342
+ budget.remaining -= 16;
1343
+ const name = (_b = (_a = value.constructor) == null ? void 0 : _a.name) != null ? _b : "object";
1344
+ return `<max depth: ${name}>`;
1345
+ }
1346
+ const toJSON = value.toJSON;
1347
+ if (typeof toJSON === "function") {
1348
+ budget.remaining -= 2;
1349
+ return boundedClone2(toJSON.call(value), budget, depth + 1);
1350
+ }
1351
+ if (Array.isArray(value)) {
1352
+ budget.remaining -= 2;
1353
+ const out2 = [];
1354
+ for (const item of value) {
1355
+ if (budget.remaining <= 0) {
1356
+ out2.push(TRUNCATION_MARKER2);
1357
+ break;
1358
+ }
1359
+ out2.push(boundedClone2(item, budget, depth + 1));
1360
+ }
1361
+ return out2;
1362
+ }
1363
+ budget.remaining -= 2;
1364
+ const out = {};
1365
+ for (const key in value) {
1366
+ if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
1367
+ if (budget.remaining <= 0) {
1368
+ out["..."] = TRUNCATION_MARKER2;
1369
+ break;
1370
+ }
1371
+ let cappedKey = key;
1372
+ if (key.length > budget.remaining) {
1373
+ cappedKey = key.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
1374
+ budget.remaining = 0;
1375
+ out[cappedKey] = TRUNCATION_MARKER2;
1376
+ break;
1377
+ }
1378
+ budget.remaining -= Math.max(key.length, 1);
1379
+ out[cappedKey] = boundedClone2(value[key], budget, depth + 1);
1380
+ }
1381
+ return out;
1382
+ }
1383
+ function boundedStringify(value, limit) {
1384
+ const max = limit != null ? limit : effectiveMaxTextFieldChars();
1385
+ try {
1386
+ if (typeof value === "string") {
1387
+ const text2 = JSON.stringify(capText2(value, max));
1388
+ return text2.length <= max ? text2 : truncateToLimit2(text2, max);
1389
+ }
1390
+ const pruned = boundedClone2(value, { remaining: max + TRUNCATION_MARKER2.length + 256 }, 0);
1391
+ const text = JSON.stringify(pruned);
1392
+ if (text === void 0) return void 0;
1393
+ return text.length <= max ? text : truncateToLimit2(text, max);
1394
+ } catch (e) {
1395
+ return void 0;
1396
+ }
1397
+ }
1398
+
1041
1399
  // src/internal/wrap/helpers.ts
1042
1400
  function isRecord(value) {
1043
1401
  return typeof value === "object" && value !== null;
@@ -1062,21 +1420,10 @@ function isModuleNamespace(obj) {
1062
1420
  }
1063
1421
  }
1064
1422
  function safeJson(value) {
1065
- try {
1066
- return JSON.stringify(value);
1067
- } catch (e) {
1068
- return void 0;
1069
- }
1423
+ return boundedStringify(value);
1070
1424
  }
1071
1425
  function safeJsonWithUint8(value) {
1072
- try {
1073
- return JSON.stringify(value, (_key, v) => {
1074
- if (v instanceof Uint8Array) return base64Encode(v);
1075
- return v;
1076
- });
1077
- } catch (e) {
1078
- return void 0;
1079
- }
1426
+ return boundedStringify(value);
1080
1427
  }
1081
1428
  function extractModelInfo(model) {
1082
1429
  if (typeof model === "string") {
@@ -1972,7 +2319,10 @@ var RaindropTelemetryIntegration = class {
1972
2319
  inputAttrs.push(
1973
2320
  attrStringArray(
1974
2321
  "ai.prompt.tools",
1975
- event.stepTools.map((t) => JSON.stringify(t))
2322
+ event.stepTools.map((t) => {
2323
+ var _a;
2324
+ return (_a = safeJsonWithUint8(t)) != null ? _a : "";
2325
+ })
1976
2326
  )
1977
2327
  );
1978
2328
  }
@@ -1980,7 +2330,7 @@ var RaindropTelemetryIntegration = class {
1980
2330
  inputAttrs.push(
1981
2331
  attrString(
1982
2332
  "ai.prompt.toolChoice",
1983
- JSON.stringify(event.stepToolChoice)
2333
+ safeJsonWithUint8(event.stepToolChoice)
1984
2334
  )
1985
2335
  );
1986
2336
  }
@@ -2036,7 +2386,9 @@ var RaindropTelemetryIntegration = class {
2036
2386
  if (chunk.type === "text-delta") {
2037
2387
  const delta = (_c = chunk.textDelta) != null ? _c : chunk.delta;
2038
2388
  if (typeof delta === "string") {
2039
- state.accumulatedText += delta;
2389
+ if (state.accumulatedText.length <= effectiveMaxTextFieldChars()) {
2390
+ state.accumulatedText += delta;
2391
+ }
2040
2392
  this.emitLive(state, "text_delta", delta);
2041
2393
  }
2042
2394
  } else if (chunk.type === "reasoning" || chunk.type === "reasoning-delta") {
@@ -2055,7 +2407,7 @@ var RaindropTelemetryIntegration = class {
2055
2407
  if (state.recordOutputs) {
2056
2408
  outputAttrs.push(
2057
2409
  attrString("ai.response.finishReason", event.finishReason),
2058
- attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
2410
+ attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
2059
2411
  attrString("ai.response.id", (_b = event.response) == null ? void 0 : _b.id),
2060
2412
  attrString("ai.response.model", (_c = event.response) == null ? void 0 : _c.modelId),
2061
2413
  attrString(
@@ -2071,7 +2423,7 @@ var RaindropTelemetryIntegration = class {
2071
2423
  outputAttrs.push(
2072
2424
  attrString(
2073
2425
  "ai.response.toolCalls",
2074
- JSON.stringify(
2426
+ safeJsonWithUint8(
2075
2427
  event.toolCalls.map((tc) => ({
2076
2428
  toolCallId: tc.toolCallId,
2077
2429
  toolName: tc.toolName,
@@ -2084,7 +2436,7 @@ var RaindropTelemetryIntegration = class {
2084
2436
  if (((_g = event.reasoning) == null ? void 0 : _g.length) > 0) {
2085
2437
  const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
2086
2438
  if (reasoningText) {
2087
- outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
2439
+ outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
2088
2440
  }
2089
2441
  }
2090
2442
  }
@@ -2506,7 +2858,7 @@ var RaindropTelemetryIntegration = class {
2506
2858
  if (state.recordOutputs) {
2507
2859
  outputAttrs.push(
2508
2860
  attrString("ai.response.finishReason", event.finishReason),
2509
- attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
2861
+ attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
2510
2862
  attrString(
2511
2863
  "ai.response.providerMetadata",
2512
2864
  event.providerMetadata ? safeJsonWithUint8(event.providerMetadata) : void 0
@@ -2516,7 +2868,7 @@ var RaindropTelemetryIntegration = class {
2516
2868
  outputAttrs.push(
2517
2869
  attrString(
2518
2870
  "ai.response.toolCalls",
2519
- JSON.stringify(
2871
+ safeJsonWithUint8(
2520
2872
  event.toolCalls.map((tc) => ({
2521
2873
  toolCallId: tc.toolCallId,
2522
2874
  toolName: tc.toolName,
@@ -2529,7 +2881,7 @@ var RaindropTelemetryIntegration = class {
2529
2881
  if (((_c = event.reasoning) == null ? void 0 : _c.length) > 0) {
2530
2882
  const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
2531
2883
  if (reasoningText) {
2532
- outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
2884
+ outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
2533
2885
  }
2534
2886
  }
2535
2887
  }
@@ -2567,7 +2919,8 @@ var RaindropTelemetryIntegration = class {
2567
2919
  const userId = (_b = callMeta.userId) != null ? _b : (_a = this.defaultContext) == null ? void 0 : _a.userId;
2568
2920
  if (!userId) return;
2569
2921
  const eventName = (_e = (_d = callMeta.eventName) != null ? _d : (_c = this.defaultContext) == null ? void 0 : _c.eventName) != null ? _e : state.operationId;
2570
- const input = state.inputText;
2922
+ const cappedOutput = capText2(output);
2923
+ const input = capText2(state.inputText);
2571
2924
  const properties = {
2572
2925
  ...(_f = this.defaultContext) == null ? void 0 : _f.properties,
2573
2926
  ...callMeta.properties
@@ -2578,7 +2931,7 @@ var RaindropTelemetryIntegration = class {
2578
2931
  userId,
2579
2932
  convoId,
2580
2933
  input,
2581
- output,
2934
+ output: cappedOutput,
2582
2935
  model,
2583
2936
  properties: Object.keys(properties).length > 0 ? properties : void 0,
2584
2937
  isPending: false
@@ -3051,13 +3404,14 @@ function shouldKeepEventPending(params) {
3051
3404
  return params.finishReason === "tool-calls" || params.finishReason === "tool_calls";
3052
3405
  }
3053
3406
  function normalizePromptAttr(arg) {
3407
+ var _a, _b;
3054
3408
  if (!isRecord(arg)) return arg;
3055
3409
  const system = arg["system"];
3056
3410
  const prompt = arg["prompt"];
3057
3411
  const messages = arg["messages"];
3058
3412
  if (Array.isArray(messages)) {
3059
3413
  if (system) {
3060
- const sysContent = typeof system === "string" ? system : JSON.stringify(system);
3414
+ const sysContent = typeof system === "string" ? system : (_a = safeJsonWithUint8(system)) != null ? _a : String(system);
3061
3415
  return [{ role: "system", content: sysContent }, ...messages];
3062
3416
  }
3063
3417
  return messages;
@@ -3065,7 +3419,10 @@ function normalizePromptAttr(arg) {
3065
3419
  if (typeof prompt === "string") {
3066
3420
  const msgs = [];
3067
3421
  if (system) {
3068
- msgs.push({ role: "system", content: typeof system === "string" ? system : JSON.stringify(system) });
3422
+ msgs.push({
3423
+ role: "system",
3424
+ content: typeof system === "string" ? system : (_b = safeJsonWithUint8(system)) != null ? _b : String(system)
3425
+ });
3069
3426
  }
3070
3427
  msgs.push({ role: "user", content: prompt });
3071
3428
  return msgs;
@@ -3234,7 +3591,8 @@ function createFinalize(params) {
3234
3591
  };
3235
3592
  const built = await maybeBuildEvent(options.buildEvent, allMessages);
3236
3593
  const patch = mergeBuildEventPatch(defaultPatch, built);
3237
- const output = patch.output;
3594
+ const output = capText2(patch.output);
3595
+ const input = capText2(patch.input);
3238
3596
  const finalModel = (_c = patch.model) != null ? _c : model;
3239
3597
  if (setup.rootSpan) {
3240
3598
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3278,7 +3636,7 @@ function createFinalize(params) {
3278
3636
  eventName: patch.eventName,
3279
3637
  userId: setup.ctx.userId,
3280
3638
  convoId: setup.ctx.convoId,
3281
- input: patch.input,
3639
+ input,
3282
3640
  output,
3283
3641
  model: finalModel,
3284
3642
  properties: patch.properties,
@@ -3793,7 +4151,8 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
3793
4151
  };
3794
4152
  const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
3795
4153
  const patch = mergeBuildEventPatch(defaultPatch, built);
3796
- const output = patch.output;
4154
+ const output = capText2(patch.output);
4155
+ const input = capText2(patch.input);
3797
4156
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
3798
4157
  if (rootSpan) {
3799
4158
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3851,7 +4210,7 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
3851
4210
  eventName: patch.eventName,
3852
4211
  userId: ctx.userId,
3853
4212
  convoId: ctx.convoId,
3854
- input: patch.input,
4213
+ input,
3855
4214
  output,
3856
4215
  model: finalModel,
3857
4216
  properties: patch.properties,
@@ -4000,7 +4359,8 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
4000
4359
  };
4001
4360
  const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
4002
4361
  const patch = mergeBuildEventPatch(defaultPatch, built);
4003
- const output = patch.output;
4362
+ const output = capText2(patch.output);
4363
+ const input = capText2(patch.input);
4004
4364
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
4005
4365
  if (rootSpan) {
4006
4366
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -4058,7 +4418,7 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
4058
4418
  eventName: patch.eventName,
4059
4419
  userId: ctx.userId,
4060
4420
  convoId: ctx.convoId,
4061
- input: patch.input,
4421
+ input,
4062
4422
  output,
4063
4423
  model: finalModel,
4064
4424
  properties: patch.properties,
@@ -4398,7 +4758,7 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4398
4758
  attributes: [
4399
4759
  ...((_a = ctx.telemetry) == null ? void 0 : _a.recordOutputs) === false ? [] : [
4400
4760
  attrString("ai.response.finishReason", finishReason),
4401
- attrString("ai.response.text", activeText.length ? activeText : void 0),
4761
+ attrString("ai.response.text", activeText.length ? capText2(activeText) : void 0),
4402
4762
  attrString(
4403
4763
  "ai.response.toolCalls",
4404
4764
  safeJsonWithUint8(toolCallsLocal.length ? toolCallsLocal : void 0)
@@ -4451,7 +4811,9 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4451
4811
  } else if (typeof value["textDelta"] === "string") {
4452
4812
  textDelta = value["textDelta"];
4453
4813
  }
4454
- if (typeof textDelta === "string") activeText += textDelta;
4814
+ if (typeof textDelta === "string" && activeText.length <= effectiveMaxTextFieldChars()) {
4815
+ activeText += textDelta;
4816
+ }
4455
4817
  }
4456
4818
  if (type === "finish") finishReason = extractFinishReason(value);
4457
4819
  if (type === "tool-call") toolCallsLocal.push(value);
@@ -4724,8 +5086,8 @@ function eventMetadataFromChatRequest(options) {
4724
5086
  });
4725
5087
  }
4726
5088
  function stringify(v) {
4727
- if (typeof v === "string") return v;
4728
- return JSON.stringify(v);
5089
+ if (typeof v === "string") return capText2(v);
5090
+ return boundedStringify(v);
4729
5091
  }
4730
5092
  function userAttrsToOtlp(attrs) {
4731
5093
  if (!attrs) return [];
@@ -4743,6 +5105,7 @@ function createRaindropAISDK(opts) {
4743
5105
  const eventsRequested = ((_a = opts.events) == null ? void 0 : _a.enabled) !== false;
4744
5106
  const tracesRequested = ((_b = opts.traces) == null ? void 0 : _b.enabled) !== false;
4745
5107
  const envDebug = envDebugEnabled();
5108
+ setMaxTextFieldChars(opts.maxTextFieldChars);
4746
5109
  const localWorkshopInput = opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl;
4747
5110
  const resolvedLocalDebuggerUrl = resolveLocalDebuggerBaseUrl(localWorkshopInput);
4748
5111
  const localDebuggerUrl = localWorkshopInput === null ? null : resolvedLocalDebuggerUrl != null ? resolvedLocalDebuggerUrl : void 0;
@@ -4929,13 +5292,17 @@ function raindrop(options = {}) {
4929
5292
  return client.createTelemetryIntegration({ context, subagentWrapping });
4930
5293
  }
4931
5294
 
5295
+ exports.DEFAULT_MAX_TEXT_FIELD_CHARS = DEFAULT_MAX_TEXT_FIELD_CHARS2;
4932
5296
  exports.DEFAULT_REDACT_ATTRIBUTE_KEYS = DEFAULT_REDACT_ATTRIBUTE_KEYS;
4933
5297
  exports.DEFAULT_SECRET_KEY_NAMES = DEFAULT_SECRET_KEY_NAMES;
4934
5298
  exports.REDACTED_PLACEHOLDER = REDACTED_PLACEHOLDER;
4935
5299
  exports.RaindropTelemetryIntegration = RaindropTelemetryIntegration;
5300
+ exports.TRUNCATION_MARKER = TRUNCATION_MARKER2;
4936
5301
  exports._resetParentToolContextStorage = _resetParentToolContextStorage;
4937
5302
  exports._resetRaindropCallMetadataStorage = _resetRaindropCallMetadataStorage;
4938
5303
  exports._resetWarnedMissingUserId = _resetWarnedMissingUserId;
5304
+ exports.boundedStringify = boundedStringify;
5305
+ exports.capText = capText2;
4939
5306
  exports.clearParentToolContext = clearParentToolContext;
4940
5307
  exports.createRaindropAISDK = createRaindropAISDK;
4941
5308
  exports.currentSpan = currentSpan;