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