@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.
@@ -4,7 +4,7 @@ var async_hooks = require('async_hooks');
4
4
 
5
5
  // src/index.node.ts
6
6
 
7
- // ../core/dist/chunk-VUNUOE2X.js
7
+ // ../core/dist/chunk-U5HUTMR5.js
8
8
  function getCrypto() {
9
9
  const c = globalThis.crypto;
10
10
  return c;
@@ -59,6 +59,8 @@ function base64Encode(bytes) {
59
59
  }
60
60
  return out;
61
61
  }
62
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
63
+ var MAX_RETRY_DELAY_MS = 3e4;
62
64
  function wait(ms) {
63
65
  return new Promise((resolve) => setTimeout(resolve, ms));
64
66
  }
@@ -66,6 +68,46 @@ function formatEndpoint(endpoint) {
66
68
  if (!endpoint) return void 0;
67
69
  return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
68
70
  }
71
+ function redactUrlForLog(url) {
72
+ try {
73
+ const parsed = new URL(url);
74
+ parsed.username = "";
75
+ parsed.password = "";
76
+ parsed.search = "";
77
+ parsed.hash = "";
78
+ return parsed.toString();
79
+ } catch (e) {
80
+ return "<unparseable-url>";
81
+ }
82
+ }
83
+ var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
84
+ var rateLimitedLogLast = /* @__PURE__ */ new Map();
85
+ function rateLimitedLog(key, log) {
86
+ const now = Date.now();
87
+ const last = rateLimitedLogLast.get(key);
88
+ if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
89
+ return false;
90
+ }
91
+ rateLimitedLogLast.set(key, now);
92
+ log();
93
+ return true;
94
+ }
95
+ async function raceWithTimeout(promise, timeoutMs) {
96
+ let timer;
97
+ const settledInTime = await Promise.race([
98
+ promise.then(
99
+ () => true,
100
+ () => true
101
+ ),
102
+ new Promise((resolve) => {
103
+ var _a;
104
+ timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
105
+ (_a = timer.unref) == null ? void 0 : _a.call(timer);
106
+ })
107
+ ]);
108
+ if (timer) clearTimeout(timer);
109
+ return settledInTime;
110
+ }
69
111
  function parseRetryAfter(headers) {
70
112
  var _a;
71
113
  const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
@@ -82,12 +124,12 @@ function parseRetryAfter(headers) {
82
124
  function getRetryDelayMs(attemptNumber, previousError) {
83
125
  if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
84
126
  const v = previousError.retryAfterMs;
85
- if (typeof v === "number") return Math.max(0, v);
127
+ if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
86
128
  }
87
129
  if (attemptNumber <= 1) return 0;
88
130
  const base = 500;
89
131
  const factor = Math.pow(2, attemptNumber - 2);
90
- return base * factor;
132
+ return Math.min(base * factor, MAX_RETRY_DELAY_MS);
91
133
  }
92
134
  async function withRetry(operation, opName2, opts) {
93
135
  const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
@@ -122,7 +164,9 @@ async function withRetry(operation, opName2, opts) {
122
164
  throw lastError instanceof Error ? lastError : new Error(String(lastError));
123
165
  }
124
166
  async function postJson(url, body, headers, opts) {
125
- const opName2 = `POST ${url}`;
167
+ var _a;
168
+ const opName2 = `POST ${redactUrlForLog(url)}`;
169
+ const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
126
170
  await withRetry(
127
171
  async () => {
128
172
  const resp = await fetch(url, {
@@ -131,7 +175,8 @@ async function postJson(url, body, headers, opts) {
131
175
  "Content-Type": "application/json",
132
176
  ...headers
133
177
  },
134
- body: JSON.stringify(body)
178
+ body: JSON.stringify(body),
179
+ signal: AbortSignal.timeout(timeoutMs)
135
180
  });
136
181
  if (!resp.ok) {
137
182
  const text = await resp.text().catch(() => "");
@@ -148,6 +193,27 @@ async function postJson(url, body, headers, opts) {
148
193
  opts
149
194
  );
150
195
  }
196
+ var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
197
+ var TRUNCATION_MARKER = "...[truncated by raindrop]";
198
+ var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
199
+ function resolveMaxTextFieldChars(value) {
200
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
201
+ return Math.floor(value);
202
+ }
203
+ return currentDefaultMaxTextFieldChars;
204
+ }
205
+ function truncateToLimit(text, limit) {
206
+ if (limit > TRUNCATION_MARKER.length) {
207
+ return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
208
+ }
209
+ return text.slice(0, Math.max(0, limit));
210
+ }
211
+ function capText(value, limit) {
212
+ if (typeof value !== "string") return value;
213
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
214
+ if (value.length <= max) return value;
215
+ return truncateToLimit(value, max);
216
+ }
151
217
  var SpanStatusCode = {
152
218
  UNSET: 0,
153
219
  ERROR: 2
@@ -335,6 +401,8 @@ function sendLocalDebuggerLiveEvent(event, options = {}) {
335
401
  ).catch(() => {
336
402
  });
337
403
  }
404
+ var SHUTDOWN_DEADLINE_MS = 1e4;
405
+ var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
338
406
  function mergePatches(target, source) {
339
407
  var _a, _b, _c, _d;
340
408
  const out = { ...target, ...source };
@@ -352,6 +420,7 @@ var EventShipper = class {
352
420
  this.sticky = /* @__PURE__ */ new Map();
353
421
  this.timers = /* @__PURE__ */ new Map();
354
422
  this.inFlight = /* @__PURE__ */ new Set();
423
+ this.hasShutdown = false;
355
424
  var _a, _b, _c, _d, _e, _f, _g, _h;
356
425
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
357
426
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
@@ -361,6 +430,7 @@ var EventShipper = class {
361
430
  this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
362
431
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
363
432
  this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
433
+ this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
364
434
  this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
365
435
  if (this.debug && this.localDebuggerUrl) {
366
436
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
@@ -383,10 +453,52 @@ var EventShipper = class {
383
453
  authHeaders() {
384
454
  return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
385
455
  }
456
+ /**
457
+ * Build the retry/timeout options for one POST, honoring the shutdown
458
+ * deadline. Returns `null` when the shutdown drain window is exhausted —
459
+ * the caller must drop the payload (with a rate-limited warning) instead
460
+ * of issuing a request that could outlive process exit.
461
+ *
462
+ * Checked fresh on EVERY send, so a shutdown that begins while the flush
463
+ * path is mid-drain takes effect immediately: no further retries, and the
464
+ * per-attempt timeout is clamped to the remaining window. After
465
+ * `shutdown()` returns (deadline cleared, `hasShutdown` still set),
466
+ * sends — late callers, or flush work the deadline abandoned mid-drain —
467
+ * run as a single short attempt rather than regaining the full retry
468
+ * schedule.
469
+ */
470
+ requestOpts() {
471
+ if (this.shutdownDeadlineAt !== void 0) {
472
+ const remainingMs = this.shutdownDeadlineAt - Date.now();
473
+ if (remainingMs <= 0) return null;
474
+ return {
475
+ maxAttempts: 1,
476
+ debug: this.debug,
477
+ sdkName: this.sdkName,
478
+ timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
479
+ };
480
+ }
481
+ if (this.hasShutdown) {
482
+ return {
483
+ maxAttempts: 1,
484
+ debug: this.debug,
485
+ sdkName: this.sdkName,
486
+ timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
487
+ };
488
+ }
489
+ return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
490
+ }
386
491
  async patch(eventId, patch) {
387
492
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
388
493
  if (!this.enabled) return;
389
494
  if (!eventId || !eventId.trim()) return;
495
+ const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
496
+ if (typeof patch.input === "string" && patch.input.length > maxChars) {
497
+ patch = { ...patch, input: capText(patch.input, maxChars) };
498
+ }
499
+ if (typeof patch.output === "string" && patch.output.length > maxChars) {
500
+ patch = { ...patch, output: capText(patch.output, maxChars) };
501
+ }
390
502
  if (this.debug) {
391
503
  console.log(`${this.prefix} queue patch`, {
392
504
  eventId,
@@ -433,9 +545,16 @@ var EventShipper = class {
433
545
  })));
434
546
  }
435
547
  async shutdown() {
436
- for (const t of this.timers.values()) clearTimeout(t);
437
- this.timers.clear();
438
- await this.flush();
548
+ this.hasShutdown = true;
549
+ this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
550
+ try {
551
+ for (const t of this.timers.values()) clearTimeout(t);
552
+ this.timers.clear();
553
+ const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
554
+ if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
555
+ } finally {
556
+ this.shutdownDeadlineAt = void 0;
557
+ }
439
558
  }
440
559
  async trackSignal(signal) {
441
560
  var _a, _b;
@@ -457,15 +576,19 @@ var EventShipper = class {
457
576
  ];
458
577
  if (!this.writeKey) return;
459
578
  const url = `${this.baseUrl}signals/track`;
579
+ const opts = this.requestOpts();
580
+ if (!opts) {
581
+ this.warnShutdownDrop("signal");
582
+ return;
583
+ }
460
584
  try {
461
- await postJson(url, body, this.authHeaders(), {
462
- maxAttempts: 3,
463
- debug: this.debug,
464
- sdkName: this.sdkName
465
- });
585
+ await postJson(url, body, this.authHeaders(), opts);
466
586
  } catch (err) {
467
587
  const msg = err instanceof Error ? err.message : String(err);
468
- console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`);
588
+ rateLimitedLog(
589
+ `${this.prefix}.send_signal_failed`,
590
+ () => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
591
+ );
469
592
  }
470
593
  }
471
594
  async identify(users) {
@@ -489,17 +612,27 @@ var EventShipper = class {
489
612
  if (!this.writeKey) return;
490
613
  if (body.length === 0) return;
491
614
  const url = `${this.baseUrl}users/identify`;
615
+ const opts = this.requestOpts();
616
+ if (!opts) {
617
+ this.warnShutdownDrop("identify");
618
+ return;
619
+ }
492
620
  try {
493
- await postJson(url, body, this.authHeaders(), {
494
- maxAttempts: 3,
495
- debug: this.debug,
496
- sdkName: this.sdkName
497
- });
621
+ await postJson(url, body, this.authHeaders(), opts);
498
622
  } catch (err) {
499
623
  const msg = err instanceof Error ? err.message : String(err);
500
- console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`);
624
+ rateLimitedLog(
625
+ `${this.prefix}.send_identify_failed`,
626
+ () => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
627
+ );
501
628
  }
502
629
  }
630
+ warnShutdownDrop(what) {
631
+ rateLimitedLog(
632
+ `${this.prefix}.shutdown_deadline`,
633
+ () => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
634
+ );
635
+ }
503
636
  async flushOne(eventId) {
504
637
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
505
638
  if (!this.enabled) return;
@@ -575,11 +708,13 @@ var EventShipper = class {
575
708
  if (!isPending) this.sticky.delete(eventId);
576
709
  return;
577
710
  }
578
- const p = postJson(url, payload, this.authHeaders(), {
579
- maxAttempts: 3,
580
- debug: this.debug,
581
- sdkName: this.sdkName
582
- });
711
+ const opts = this.requestOpts();
712
+ if (!opts) {
713
+ this.warnShutdownDrop(`track_partial ${eventId}`);
714
+ if (!isPending) this.sticky.delete(eventId);
715
+ return;
716
+ }
717
+ const p = postJson(url, payload, this.authHeaders(), opts);
583
718
  this.inFlight.add(p);
584
719
  try {
585
720
  try {
@@ -589,7 +724,10 @@ var EventShipper = class {
589
724
  }
590
725
  } catch (err) {
591
726
  const msg = err instanceof Error ? err.message : String(err);
592
- console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`);
727
+ rateLimitedLog(
728
+ `${this.prefix}.send_track_partial_failed`,
729
+ () => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
730
+ );
593
731
  }
594
732
  } finally {
595
733
  this.inFlight.delete(p);
@@ -688,10 +826,24 @@ function redactJsonAttributeValue(key, value) {
688
826
  if (scrubbedJson === json) return void 0;
689
827
  return { stringValue: scrubbedJson };
690
828
  }
829
+ function applyOtelSpanAttributeLimit(limit) {
830
+ var _a, _b;
831
+ try {
832
+ 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;
833
+ if (!raw) return limit;
834
+ const parsed = Number.parseInt(raw, 10);
835
+ if (Number.isFinite(parsed) && parsed > 0) {
836
+ return Math.min(limit, parsed);
837
+ }
838
+ } catch (e) {
839
+ }
840
+ return limit;
841
+ }
691
842
  var TraceShipper = class {
692
843
  constructor(opts) {
693
844
  this.queue = [];
694
845
  this.inFlight = /* @__PURE__ */ new Set();
846
+ this.hasShutdown = false;
695
847
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
696
848
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
697
849
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
@@ -711,6 +863,39 @@ var TraceShipper = class {
711
863
  }
712
864
  this.transformSpanHook = opts.transformSpan;
713
865
  this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
866
+ this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
867
+ }
868
+ /**
869
+ * Cap every string attribute value on the span. O(#attributes) length
870
+ * checks; only oversized values pay a slice. Runs AFTER the redaction
871
+ * pipeline so the default secret-scrub still sees parseable JSON in
872
+ * `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
873
+ * first could cut a JSON blob mid-way, fail the parse, and ship secrets
874
+ * in the surviving prefix).
875
+ *
876
+ * A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
877
+ * for span content, matching the Python SDK and the OTel SDK convention.
878
+ */
879
+ capSpanAttributes(span) {
880
+ var _a;
881
+ const maxChars = applyOtelSpanAttributeLimit(
882
+ resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
883
+ );
884
+ const attrs = span.attributes;
885
+ if (!attrs || attrs.length === 0) return span;
886
+ let nextAttrs;
887
+ for (let i = 0; i < attrs.length; i++) {
888
+ const attr = attrs[i];
889
+ const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
890
+ if (typeof value !== "string" || value.length <= maxChars) continue;
891
+ if (!nextAttrs) nextAttrs = attrs.slice();
892
+ nextAttrs[i] = {
893
+ key: attr.key,
894
+ value: { ...attr.value, stringValue: capText(value, maxChars) }
895
+ };
896
+ }
897
+ if (!nextAttrs) return span;
898
+ return { ...span, attributes: nextAttrs };
714
899
  }
715
900
  /**
716
901
  * Apply the user `transformSpan` hook (if any) followed by the default
@@ -744,7 +929,7 @@ var TraceShipper = class {
744
929
  if (!this.disableDefaultRedaction) {
745
930
  current = defaultTransformSpan(current);
746
931
  }
747
- return current;
932
+ return this.capSpanAttributes(current);
748
933
  }
749
934
  isDebugEnabled() {
750
935
  return this.debug;
@@ -865,6 +1050,16 @@ var TraceShipper = class {
865
1050
  while (this.queue.length > 0) {
866
1051
  const batch = this.queue.splice(0, this.maxBatchSize);
867
1052
  if (!this.writeKey) continue;
1053
+ const opts = this.requestOpts();
1054
+ if (!opts) {
1055
+ rateLimitedLog(
1056
+ `${this.prefix}.shutdown_deadline`,
1057
+ () => console.warn(
1058
+ `${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
1059
+ )
1060
+ );
1061
+ continue;
1062
+ }
868
1063
  const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
869
1064
  const url = `${this.baseUrl}traces`;
870
1065
  if (this.debug) {
@@ -873,11 +1068,7 @@ var TraceShipper = class {
873
1068
  endpoint: url
874
1069
  });
875
1070
  }
876
- const p = postJson(url, body, this.authHeaders(), {
877
- maxAttempts: 3,
878
- debug: this.debug,
879
- sdkName: this.sdkName
880
- });
1071
+ const p = postJson(url, body, this.authHeaders(), opts);
881
1072
  this.inFlight.add(p);
882
1073
  try {
883
1074
  try {
@@ -885,21 +1076,61 @@ var TraceShipper = class {
885
1076
  if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
886
1077
  } catch (err) {
887
1078
  const msg = err instanceof Error ? err.message : String(err);
888
- console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`);
1079
+ rateLimitedLog(
1080
+ `${this.prefix}.send_spans_failed`,
1081
+ () => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
1082
+ );
889
1083
  }
890
1084
  } finally {
891
1085
  this.inFlight.delete(p);
892
1086
  }
893
1087
  }
894
1088
  }
1089
+ /** See EventShipper.requestOpts — same shutdown-budget semantics. */
1090
+ requestOpts() {
1091
+ if (this.shutdownDeadlineAt !== void 0) {
1092
+ const remainingMs = this.shutdownDeadlineAt - Date.now();
1093
+ if (remainingMs <= 0) return null;
1094
+ return {
1095
+ maxAttempts: 1,
1096
+ debug: this.debug,
1097
+ sdkName: this.sdkName,
1098
+ timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
1099
+ };
1100
+ }
1101
+ if (this.hasShutdown) {
1102
+ return {
1103
+ maxAttempts: 1,
1104
+ debug: this.debug,
1105
+ sdkName: this.sdkName,
1106
+ timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
1107
+ };
1108
+ }
1109
+ return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
1110
+ }
895
1111
  async shutdown() {
896
- if (this.timer) {
897
- clearTimeout(this.timer);
898
- this.timer = void 0;
1112
+ this.hasShutdown = true;
1113
+ this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
1114
+ try {
1115
+ if (this.timer) {
1116
+ clearTimeout(this.timer);
1117
+ this.timer = void 0;
1118
+ }
1119
+ const drain = async () => {
1120
+ await this.flush();
1121
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1122
+ })));
1123
+ };
1124
+ const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
1125
+ if (!settled) {
1126
+ rateLimitedLog(
1127
+ `${this.prefix}.shutdown_deadline`,
1128
+ () => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
1129
+ );
1130
+ }
1131
+ } finally {
1132
+ this.shutdownDeadlineAt = void 0;
899
1133
  }
900
- await this.flush();
901
- await Promise.all([...this.inFlight].map((p) => p.catch(() => {
902
- })));
903
1134
  }
904
1135
  };
905
1136
  var NOOP_SPAN = {
@@ -1024,7 +1255,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = async_hooks.AsyncLocalStorage;
1024
1255
  // package.json
1025
1256
  var package_default = {
1026
1257
  name: "@raindrop-ai/ai-sdk",
1027
- version: "0.0.33"};
1258
+ version: "0.0.34"};
1028
1259
 
1029
1260
  // src/internal/version.ts
1030
1261
  var libraryName = package_default.name;
@@ -1043,6 +1274,133 @@ var EventShipper2 = class extends EventShipper {
1043
1274
  }
1044
1275
  };
1045
1276
 
1277
+ // src/internal/truncation.ts
1278
+ var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
1279
+ var DEFAULT_MAX_TEXT_FIELD_CHARS2 = 1e6;
1280
+ var MAX_DEPTH = 12;
1281
+ var configuredMaxTextFieldChars;
1282
+ function setMaxTextFieldChars(limit) {
1283
+ if (limit === void 0) return;
1284
+ if (typeof limit === "number" && Number.isFinite(limit) && limit > 0) {
1285
+ configuredMaxTextFieldChars = Math.floor(limit);
1286
+ } else {
1287
+ console.warn(`[raindrop-ai] maxTextFieldChars=${String(limit)} ignored; must be > 0`);
1288
+ }
1289
+ }
1290
+ function otelEnvLimit() {
1291
+ var _a;
1292
+ if (typeof process === "undefined") return void 0;
1293
+ const raw = (_a = process.env) == null ? void 0 : _a.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
1294
+ if (!raw) return void 0;
1295
+ const parsed = Number.parseInt(raw, 10);
1296
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
1297
+ }
1298
+ function effectiveMaxTextFieldChars() {
1299
+ const base = configuredMaxTextFieldChars != null ? configuredMaxTextFieldChars : DEFAULT_MAX_TEXT_FIELD_CHARS2;
1300
+ const env = otelEnvLimit();
1301
+ return env !== void 0 && env < base ? env : base;
1302
+ }
1303
+ function truncateToLimit2(text, limit) {
1304
+ if (limit > TRUNCATION_MARKER2.length) {
1305
+ return text.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
1306
+ }
1307
+ return text.slice(0, limit);
1308
+ }
1309
+ function capText2(value, limit) {
1310
+ if (typeof value !== "string") return value;
1311
+ const max = limit != null ? limit : effectiveMaxTextFieldChars();
1312
+ if (value.length <= max) return value;
1313
+ return truncateToLimit2(value, max);
1314
+ }
1315
+ function boundedClone2(value, budget, depth) {
1316
+ var _a, _b;
1317
+ if (budget.remaining <= 0) return TRUNCATION_MARKER2;
1318
+ if (typeof value === "string") {
1319
+ if (value.length > budget.remaining) {
1320
+ const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
1321
+ budget.remaining = 0;
1322
+ return taken;
1323
+ }
1324
+ budget.remaining -= Math.max(value.length, 1);
1325
+ return value;
1326
+ }
1327
+ if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1328
+ budget.remaining -= 8;
1329
+ return value;
1330
+ }
1331
+ if (typeof value !== "object") {
1332
+ budget.remaining -= 1;
1333
+ return value;
1334
+ }
1335
+ if (value instanceof Uint8Array) {
1336
+ const maxBytes = Math.max(0, Math.ceil(budget.remaining * 3 / 4));
1337
+ if (value.byteLength > maxBytes) {
1338
+ const taken = base64Encode(value.subarray(0, maxBytes)) + TRUNCATION_MARKER2;
1339
+ budget.remaining = 0;
1340
+ return taken;
1341
+ }
1342
+ const encoded = base64Encode(value);
1343
+ budget.remaining -= Math.max(encoded.length, 1);
1344
+ return encoded;
1345
+ }
1346
+ if (depth >= MAX_DEPTH) {
1347
+ budget.remaining -= 16;
1348
+ const name = (_b = (_a = value.constructor) == null ? void 0 : _a.name) != null ? _b : "object";
1349
+ return `<max depth: ${name}>`;
1350
+ }
1351
+ const toJSON = value.toJSON;
1352
+ if (typeof toJSON === "function") {
1353
+ budget.remaining -= 2;
1354
+ return boundedClone2(toJSON.call(value), budget, depth + 1);
1355
+ }
1356
+ if (Array.isArray(value)) {
1357
+ budget.remaining -= 2;
1358
+ const out2 = [];
1359
+ for (const item of value) {
1360
+ if (budget.remaining <= 0) {
1361
+ out2.push(TRUNCATION_MARKER2);
1362
+ break;
1363
+ }
1364
+ out2.push(boundedClone2(item, budget, depth + 1));
1365
+ }
1366
+ return out2;
1367
+ }
1368
+ budget.remaining -= 2;
1369
+ const out = {};
1370
+ for (const key in value) {
1371
+ if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
1372
+ if (budget.remaining <= 0) {
1373
+ out["..."] = TRUNCATION_MARKER2;
1374
+ break;
1375
+ }
1376
+ let cappedKey = key;
1377
+ if (key.length > budget.remaining) {
1378
+ cappedKey = key.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
1379
+ budget.remaining = 0;
1380
+ out[cappedKey] = TRUNCATION_MARKER2;
1381
+ break;
1382
+ }
1383
+ budget.remaining -= Math.max(key.length, 1);
1384
+ out[cappedKey] = boundedClone2(value[key], budget, depth + 1);
1385
+ }
1386
+ return out;
1387
+ }
1388
+ function boundedStringify(value, limit) {
1389
+ const max = limit != null ? limit : effectiveMaxTextFieldChars();
1390
+ try {
1391
+ if (typeof value === "string") {
1392
+ const text2 = JSON.stringify(capText2(value, max));
1393
+ return text2.length <= max ? text2 : truncateToLimit2(text2, max);
1394
+ }
1395
+ const pruned = boundedClone2(value, { remaining: max + TRUNCATION_MARKER2.length + 256 }, 0);
1396
+ const text = JSON.stringify(pruned);
1397
+ if (text === void 0) return void 0;
1398
+ return text.length <= max ? text : truncateToLimit2(text, max);
1399
+ } catch (e) {
1400
+ return void 0;
1401
+ }
1402
+ }
1403
+
1046
1404
  // src/internal/wrap/helpers.ts
1047
1405
  function isRecord(value) {
1048
1406
  return typeof value === "object" && value !== null;
@@ -1067,21 +1425,10 @@ function isModuleNamespace(obj) {
1067
1425
  }
1068
1426
  }
1069
1427
  function safeJson(value) {
1070
- try {
1071
- return JSON.stringify(value);
1072
- } catch (e) {
1073
- return void 0;
1074
- }
1428
+ return boundedStringify(value);
1075
1429
  }
1076
1430
  function safeJsonWithUint8(value) {
1077
- try {
1078
- return JSON.stringify(value, (_key, v) => {
1079
- if (v instanceof Uint8Array) return base64Encode(v);
1080
- return v;
1081
- });
1082
- } catch (e) {
1083
- return void 0;
1084
- }
1431
+ return boundedStringify(value);
1085
1432
  }
1086
1433
  function extractModelInfo(model) {
1087
1434
  if (typeof model === "string") {
@@ -1977,7 +2324,10 @@ var RaindropTelemetryIntegration = class {
1977
2324
  inputAttrs.push(
1978
2325
  attrStringArray(
1979
2326
  "ai.prompt.tools",
1980
- event.stepTools.map((t) => JSON.stringify(t))
2327
+ event.stepTools.map((t) => {
2328
+ var _a;
2329
+ return (_a = safeJsonWithUint8(t)) != null ? _a : "";
2330
+ })
1981
2331
  )
1982
2332
  );
1983
2333
  }
@@ -1985,7 +2335,7 @@ var RaindropTelemetryIntegration = class {
1985
2335
  inputAttrs.push(
1986
2336
  attrString(
1987
2337
  "ai.prompt.toolChoice",
1988
- JSON.stringify(event.stepToolChoice)
2338
+ safeJsonWithUint8(event.stepToolChoice)
1989
2339
  )
1990
2340
  );
1991
2341
  }
@@ -2041,7 +2391,9 @@ var RaindropTelemetryIntegration = class {
2041
2391
  if (chunk.type === "text-delta") {
2042
2392
  const delta = (_c = chunk.textDelta) != null ? _c : chunk.delta;
2043
2393
  if (typeof delta === "string") {
2044
- state.accumulatedText += delta;
2394
+ if (state.accumulatedText.length <= effectiveMaxTextFieldChars()) {
2395
+ state.accumulatedText += delta;
2396
+ }
2045
2397
  this.emitLive(state, "text_delta", delta);
2046
2398
  }
2047
2399
  } else if (chunk.type === "reasoning" || chunk.type === "reasoning-delta") {
@@ -2060,7 +2412,7 @@ var RaindropTelemetryIntegration = class {
2060
2412
  if (state.recordOutputs) {
2061
2413
  outputAttrs.push(
2062
2414
  attrString("ai.response.finishReason", event.finishReason),
2063
- attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
2415
+ attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
2064
2416
  attrString("ai.response.id", (_b = event.response) == null ? void 0 : _b.id),
2065
2417
  attrString("ai.response.model", (_c = event.response) == null ? void 0 : _c.modelId),
2066
2418
  attrString(
@@ -2076,7 +2428,7 @@ var RaindropTelemetryIntegration = class {
2076
2428
  outputAttrs.push(
2077
2429
  attrString(
2078
2430
  "ai.response.toolCalls",
2079
- JSON.stringify(
2431
+ safeJsonWithUint8(
2080
2432
  event.toolCalls.map((tc) => ({
2081
2433
  toolCallId: tc.toolCallId,
2082
2434
  toolName: tc.toolName,
@@ -2089,7 +2441,7 @@ var RaindropTelemetryIntegration = class {
2089
2441
  if (((_g = event.reasoning) == null ? void 0 : _g.length) > 0) {
2090
2442
  const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
2091
2443
  if (reasoningText) {
2092
- outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
2444
+ outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
2093
2445
  }
2094
2446
  }
2095
2447
  }
@@ -2511,7 +2863,7 @@ var RaindropTelemetryIntegration = class {
2511
2863
  if (state.recordOutputs) {
2512
2864
  outputAttrs.push(
2513
2865
  attrString("ai.response.finishReason", event.finishReason),
2514
- attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
2866
+ attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
2515
2867
  attrString(
2516
2868
  "ai.response.providerMetadata",
2517
2869
  event.providerMetadata ? safeJsonWithUint8(event.providerMetadata) : void 0
@@ -2521,7 +2873,7 @@ var RaindropTelemetryIntegration = class {
2521
2873
  outputAttrs.push(
2522
2874
  attrString(
2523
2875
  "ai.response.toolCalls",
2524
- JSON.stringify(
2876
+ safeJsonWithUint8(
2525
2877
  event.toolCalls.map((tc) => ({
2526
2878
  toolCallId: tc.toolCallId,
2527
2879
  toolName: tc.toolName,
@@ -2534,7 +2886,7 @@ var RaindropTelemetryIntegration = class {
2534
2886
  if (((_c = event.reasoning) == null ? void 0 : _c.length) > 0) {
2535
2887
  const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
2536
2888
  if (reasoningText) {
2537
- outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
2889
+ outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
2538
2890
  }
2539
2891
  }
2540
2892
  }
@@ -2572,7 +2924,8 @@ var RaindropTelemetryIntegration = class {
2572
2924
  const userId = (_b = callMeta.userId) != null ? _b : (_a = this.defaultContext) == null ? void 0 : _a.userId;
2573
2925
  if (!userId) return;
2574
2926
  const eventName = (_e = (_d = callMeta.eventName) != null ? _d : (_c = this.defaultContext) == null ? void 0 : _c.eventName) != null ? _e : state.operationId;
2575
- const input = state.inputText;
2927
+ const cappedOutput = capText2(output);
2928
+ const input = capText2(state.inputText);
2576
2929
  const properties = {
2577
2930
  ...(_f = this.defaultContext) == null ? void 0 : _f.properties,
2578
2931
  ...callMeta.properties
@@ -2583,7 +2936,7 @@ var RaindropTelemetryIntegration = class {
2583
2936
  userId,
2584
2937
  convoId,
2585
2938
  input,
2586
- output,
2939
+ output: cappedOutput,
2587
2940
  model,
2588
2941
  properties: Object.keys(properties).length > 0 ? properties : void 0,
2589
2942
  isPending: false
@@ -3056,13 +3409,14 @@ function shouldKeepEventPending(params) {
3056
3409
  return params.finishReason === "tool-calls" || params.finishReason === "tool_calls";
3057
3410
  }
3058
3411
  function normalizePromptAttr(arg) {
3412
+ var _a, _b;
3059
3413
  if (!isRecord(arg)) return arg;
3060
3414
  const system = arg["system"];
3061
3415
  const prompt = arg["prompt"];
3062
3416
  const messages = arg["messages"];
3063
3417
  if (Array.isArray(messages)) {
3064
3418
  if (system) {
3065
- const sysContent = typeof system === "string" ? system : JSON.stringify(system);
3419
+ const sysContent = typeof system === "string" ? system : (_a = safeJsonWithUint8(system)) != null ? _a : String(system);
3066
3420
  return [{ role: "system", content: sysContent }, ...messages];
3067
3421
  }
3068
3422
  return messages;
@@ -3070,7 +3424,10 @@ function normalizePromptAttr(arg) {
3070
3424
  if (typeof prompt === "string") {
3071
3425
  const msgs = [];
3072
3426
  if (system) {
3073
- msgs.push({ role: "system", content: typeof system === "string" ? system : JSON.stringify(system) });
3427
+ msgs.push({
3428
+ role: "system",
3429
+ content: typeof system === "string" ? system : (_b = safeJsonWithUint8(system)) != null ? _b : String(system)
3430
+ });
3074
3431
  }
3075
3432
  msgs.push({ role: "user", content: prompt });
3076
3433
  return msgs;
@@ -3239,7 +3596,8 @@ function createFinalize(params) {
3239
3596
  };
3240
3597
  const built = await maybeBuildEvent(options.buildEvent, allMessages);
3241
3598
  const patch = mergeBuildEventPatch(defaultPatch, built);
3242
- const output = patch.output;
3599
+ const output = capText2(patch.output);
3600
+ const input = capText2(patch.input);
3243
3601
  const finalModel = (_c = patch.model) != null ? _c : model;
3244
3602
  if (setup.rootSpan) {
3245
3603
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3283,7 +3641,7 @@ function createFinalize(params) {
3283
3641
  eventName: patch.eventName,
3284
3642
  userId: setup.ctx.userId,
3285
3643
  convoId: setup.ctx.convoId,
3286
- input: patch.input,
3644
+ input,
3287
3645
  output,
3288
3646
  model: finalModel,
3289
3647
  properties: patch.properties,
@@ -3798,7 +4156,8 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
3798
4156
  };
3799
4157
  const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
3800
4158
  const patch = mergeBuildEventPatch(defaultPatch, built);
3801
- const output = patch.output;
4159
+ const output = capText2(patch.output);
4160
+ const input = capText2(patch.input);
3802
4161
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
3803
4162
  if (rootSpan) {
3804
4163
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3856,7 +4215,7 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
3856
4215
  eventName: patch.eventName,
3857
4216
  userId: ctx.userId,
3858
4217
  convoId: ctx.convoId,
3859
- input: patch.input,
4218
+ input,
3860
4219
  output,
3861
4220
  model: finalModel,
3862
4221
  properties: patch.properties,
@@ -4005,7 +4364,8 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
4005
4364
  };
4006
4365
  const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
4007
4366
  const patch = mergeBuildEventPatch(defaultPatch, built);
4008
- const output = patch.output;
4367
+ const output = capText2(patch.output);
4368
+ const input = capText2(patch.input);
4009
4369
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
4010
4370
  if (rootSpan) {
4011
4371
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -4063,7 +4423,7 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
4063
4423
  eventName: patch.eventName,
4064
4424
  userId: ctx.userId,
4065
4425
  convoId: ctx.convoId,
4066
- input: patch.input,
4426
+ input,
4067
4427
  output,
4068
4428
  model: finalModel,
4069
4429
  properties: patch.properties,
@@ -4403,7 +4763,7 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4403
4763
  attributes: [
4404
4764
  ...((_a = ctx.telemetry) == null ? void 0 : _a.recordOutputs) === false ? [] : [
4405
4765
  attrString("ai.response.finishReason", finishReason),
4406
- attrString("ai.response.text", activeText.length ? activeText : void 0),
4766
+ attrString("ai.response.text", activeText.length ? capText2(activeText) : void 0),
4407
4767
  attrString(
4408
4768
  "ai.response.toolCalls",
4409
4769
  safeJsonWithUint8(toolCallsLocal.length ? toolCallsLocal : void 0)
@@ -4456,7 +4816,9 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4456
4816
  } else if (typeof value["textDelta"] === "string") {
4457
4817
  textDelta = value["textDelta"];
4458
4818
  }
4459
- if (typeof textDelta === "string") activeText += textDelta;
4819
+ if (typeof textDelta === "string" && activeText.length <= effectiveMaxTextFieldChars()) {
4820
+ activeText += textDelta;
4821
+ }
4460
4822
  }
4461
4823
  if (type === "finish") finishReason = extractFinishReason(value);
4462
4824
  if (type === "tool-call") toolCallsLocal.push(value);
@@ -4729,8 +5091,8 @@ function eventMetadataFromChatRequest(options) {
4729
5091
  });
4730
5092
  }
4731
5093
  function stringify(v) {
4732
- if (typeof v === "string") return v;
4733
- return JSON.stringify(v);
5094
+ if (typeof v === "string") return capText2(v);
5095
+ return boundedStringify(v);
4734
5096
  }
4735
5097
  function userAttrsToOtlp(attrs) {
4736
5098
  if (!attrs) return [];
@@ -4748,6 +5110,7 @@ function createRaindropAISDK(opts) {
4748
5110
  const eventsRequested = ((_a = opts.events) == null ? void 0 : _a.enabled) !== false;
4749
5111
  const tracesRequested = ((_b = opts.traces) == null ? void 0 : _b.enabled) !== false;
4750
5112
  const envDebug = envDebugEnabled();
5113
+ setMaxTextFieldChars(opts.maxTextFieldChars);
4751
5114
  const localWorkshopInput = opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl;
4752
5115
  const resolvedLocalDebuggerUrl = resolveLocalDebuggerBaseUrl(localWorkshopInput);
4753
5116
  const localDebuggerUrl = localWorkshopInput === null ? null : resolvedLocalDebuggerUrl != null ? resolvedLocalDebuggerUrl : void 0;
@@ -4937,13 +5300,17 @@ function raindrop(options = {}) {
4937
5300
  // src/index.node.ts
4938
5301
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = async_hooks.AsyncLocalStorage;
4939
5302
 
5303
+ exports.DEFAULT_MAX_TEXT_FIELD_CHARS = DEFAULT_MAX_TEXT_FIELD_CHARS2;
4940
5304
  exports.DEFAULT_REDACT_ATTRIBUTE_KEYS = DEFAULT_REDACT_ATTRIBUTE_KEYS;
4941
5305
  exports.DEFAULT_SECRET_KEY_NAMES = DEFAULT_SECRET_KEY_NAMES;
4942
5306
  exports.REDACTED_PLACEHOLDER = REDACTED_PLACEHOLDER;
4943
5307
  exports.RaindropTelemetryIntegration = RaindropTelemetryIntegration;
5308
+ exports.TRUNCATION_MARKER = TRUNCATION_MARKER2;
4944
5309
  exports._resetParentToolContextStorage = _resetParentToolContextStorage;
4945
5310
  exports._resetRaindropCallMetadataStorage = _resetRaindropCallMetadataStorage;
4946
5311
  exports._resetWarnedMissingUserId = _resetWarnedMissingUserId;
5312
+ exports.boundedStringify = boundedStringify;
5313
+ exports.capText = capText2;
4947
5314
  exports.clearParentToolContext = clearParentToolContext;
4948
5315
  exports.createRaindropAISDK = createRaindropAISDK;
4949
5316
  exports.currentSpan = currentSpan;