@raindrop-ai/ai-sdk 0.0.32 → 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
  import { AsyncLocalStorage } from 'async_hooks';
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 = {
@@ -1017,6 +1248,133 @@ async function* asyncGeneratorWithCurrent(span, gen) {
1017
1248
  }
1018
1249
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
1019
1250
 
1251
+ // src/internal/truncation.ts
1252
+ var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
1253
+ var DEFAULT_MAX_TEXT_FIELD_CHARS2 = 1e6;
1254
+ var MAX_DEPTH = 12;
1255
+ var configuredMaxTextFieldChars;
1256
+ function setMaxTextFieldChars(limit) {
1257
+ if (limit === void 0) return;
1258
+ if (typeof limit === "number" && Number.isFinite(limit) && limit > 0) {
1259
+ configuredMaxTextFieldChars = Math.floor(limit);
1260
+ } else {
1261
+ console.warn(`[raindrop-ai] maxTextFieldChars=${String(limit)} ignored; must be > 0`);
1262
+ }
1263
+ }
1264
+ function otelEnvLimit() {
1265
+ var _a;
1266
+ if (typeof process === "undefined") return void 0;
1267
+ const raw = (_a = process.env) == null ? void 0 : _a.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
1268
+ if (!raw) return void 0;
1269
+ const parsed = Number.parseInt(raw, 10);
1270
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
1271
+ }
1272
+ function effectiveMaxTextFieldChars() {
1273
+ const base = configuredMaxTextFieldChars != null ? configuredMaxTextFieldChars : DEFAULT_MAX_TEXT_FIELD_CHARS2;
1274
+ const env = otelEnvLimit();
1275
+ return env !== void 0 && env < base ? env : base;
1276
+ }
1277
+ function truncateToLimit2(text, limit) {
1278
+ if (limit > TRUNCATION_MARKER2.length) {
1279
+ return text.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
1280
+ }
1281
+ return text.slice(0, limit);
1282
+ }
1283
+ function capText2(value, limit) {
1284
+ if (typeof value !== "string") return value;
1285
+ const max = limit != null ? limit : effectiveMaxTextFieldChars();
1286
+ if (value.length <= max) return value;
1287
+ return truncateToLimit2(value, max);
1288
+ }
1289
+ function boundedClone2(value, budget, depth) {
1290
+ var _a, _b;
1291
+ if (budget.remaining <= 0) return TRUNCATION_MARKER2;
1292
+ if (typeof value === "string") {
1293
+ if (value.length > budget.remaining) {
1294
+ const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
1295
+ budget.remaining = 0;
1296
+ return taken;
1297
+ }
1298
+ budget.remaining -= Math.max(value.length, 1);
1299
+ return value;
1300
+ }
1301
+ if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1302
+ budget.remaining -= 8;
1303
+ return value;
1304
+ }
1305
+ if (typeof value !== "object") {
1306
+ budget.remaining -= 1;
1307
+ return value;
1308
+ }
1309
+ if (value instanceof Uint8Array) {
1310
+ const maxBytes = Math.max(0, Math.ceil(budget.remaining * 3 / 4));
1311
+ if (value.byteLength > maxBytes) {
1312
+ const taken = base64Encode(value.subarray(0, maxBytes)) + TRUNCATION_MARKER2;
1313
+ budget.remaining = 0;
1314
+ return taken;
1315
+ }
1316
+ const encoded = base64Encode(value);
1317
+ budget.remaining -= Math.max(encoded.length, 1);
1318
+ return encoded;
1319
+ }
1320
+ if (depth >= MAX_DEPTH) {
1321
+ budget.remaining -= 16;
1322
+ const name = (_b = (_a = value.constructor) == null ? void 0 : _a.name) != null ? _b : "object";
1323
+ return `<max depth: ${name}>`;
1324
+ }
1325
+ const toJSON = value.toJSON;
1326
+ if (typeof toJSON === "function") {
1327
+ budget.remaining -= 2;
1328
+ return boundedClone2(toJSON.call(value), budget, depth + 1);
1329
+ }
1330
+ if (Array.isArray(value)) {
1331
+ budget.remaining -= 2;
1332
+ const out2 = [];
1333
+ for (const item of value) {
1334
+ if (budget.remaining <= 0) {
1335
+ out2.push(TRUNCATION_MARKER2);
1336
+ break;
1337
+ }
1338
+ out2.push(boundedClone2(item, budget, depth + 1));
1339
+ }
1340
+ return out2;
1341
+ }
1342
+ budget.remaining -= 2;
1343
+ const out = {};
1344
+ for (const key in value) {
1345
+ if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
1346
+ if (budget.remaining <= 0) {
1347
+ out["..."] = TRUNCATION_MARKER2;
1348
+ break;
1349
+ }
1350
+ let cappedKey = key;
1351
+ if (key.length > budget.remaining) {
1352
+ cappedKey = key.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
1353
+ budget.remaining = 0;
1354
+ out[cappedKey] = TRUNCATION_MARKER2;
1355
+ break;
1356
+ }
1357
+ budget.remaining -= Math.max(key.length, 1);
1358
+ out[cappedKey] = boundedClone2(value[key], budget, depth + 1);
1359
+ }
1360
+ return out;
1361
+ }
1362
+ function boundedStringify(value, limit) {
1363
+ const max = limit != null ? limit : effectiveMaxTextFieldChars();
1364
+ try {
1365
+ if (typeof value === "string") {
1366
+ const text2 = JSON.stringify(capText2(value, max));
1367
+ return text2.length <= max ? text2 : truncateToLimit2(text2, max);
1368
+ }
1369
+ const pruned = boundedClone2(value, { remaining: max + TRUNCATION_MARKER2.length + 256 }, 0);
1370
+ const text = JSON.stringify(pruned);
1371
+ if (text === void 0) return void 0;
1372
+ return text.length <= max ? text : truncateToLimit2(text, max);
1373
+ } catch (e) {
1374
+ return void 0;
1375
+ }
1376
+ }
1377
+
1020
1378
  // src/internal/wrap/helpers.ts
1021
1379
  function isRecord(value) {
1022
1380
  return typeof value === "object" && value !== null;
@@ -1041,21 +1399,10 @@ function isModuleNamespace(obj) {
1041
1399
  }
1042
1400
  }
1043
1401
  function safeJson(value) {
1044
- try {
1045
- return JSON.stringify(value);
1046
- } catch (e) {
1047
- return void 0;
1048
- }
1402
+ return boundedStringify(value);
1049
1403
  }
1050
1404
  function safeJsonWithUint8(value) {
1051
- try {
1052
- return JSON.stringify(value, (_key, v) => {
1053
- if (v instanceof Uint8Array) return base64Encode(v);
1054
- return v;
1055
- });
1056
- } catch (e) {
1057
- return void 0;
1058
- }
1405
+ return boundedStringify(value);
1059
1406
  }
1060
1407
  function extractModelInfo(model) {
1061
1408
  if (typeof model === "string") {
@@ -1781,6 +2128,25 @@ var RaindropTelemetryIntegration = class {
1781
2128
  * (the `event.callId` can be the same for parallel sibling tools).
1782
2129
  */
1783
2130
  this.priorParentContexts = /* @__PURE__ */ new Map();
2131
+ // ── lifecycle ─────────────────────────────────────────────────────────────
2132
+ //
2133
+ // The shippers buffer spans/events and flush on a timer, so a short-lived
2134
+ // script (e.g. a single `generateText` then `process.exit`) can exit before
2135
+ // anything ships. Expose `flush`/`shutdown` on the integration itself so the
2136
+ // value returned by `raindrop()` is self-sufficient — callers can keep the
2137
+ // reference they pass to `registerTelemetry` and `await rd.flush()` before
2138
+ // exiting, without also constructing a separate client.
2139
+ /** Flush any buffered events and trace spans to their destinations. */
2140
+ this.flush = async () => {
2141
+ await Promise.all([this.eventShipper.flush(), this.traceShipper.flush()]);
2142
+ };
2143
+ /** Flush and stop the background flush timers. */
2144
+ this.shutdown = async () => {
2145
+ await Promise.all([
2146
+ this.eventShipper.shutdown(),
2147
+ this.traceShipper.shutdown()
2148
+ ]);
2149
+ };
1784
2150
  // ── onStart ─────────────────────────────────────────────────────────────
1785
2151
  this.onStart = (event) => {
1786
2152
  var _a, _b, _c, _d, _e;
@@ -1932,7 +2298,10 @@ var RaindropTelemetryIntegration = class {
1932
2298
  inputAttrs.push(
1933
2299
  attrStringArray(
1934
2300
  "ai.prompt.tools",
1935
- event.stepTools.map((t) => JSON.stringify(t))
2301
+ event.stepTools.map((t) => {
2302
+ var _a;
2303
+ return (_a = safeJsonWithUint8(t)) != null ? _a : "";
2304
+ })
1936
2305
  )
1937
2306
  );
1938
2307
  }
@@ -1940,7 +2309,7 @@ var RaindropTelemetryIntegration = class {
1940
2309
  inputAttrs.push(
1941
2310
  attrString(
1942
2311
  "ai.prompt.toolChoice",
1943
- JSON.stringify(event.stepToolChoice)
2312
+ safeJsonWithUint8(event.stepToolChoice)
1944
2313
  )
1945
2314
  );
1946
2315
  }
@@ -1996,7 +2365,9 @@ var RaindropTelemetryIntegration = class {
1996
2365
  if (chunk.type === "text-delta") {
1997
2366
  const delta = (_c = chunk.textDelta) != null ? _c : chunk.delta;
1998
2367
  if (typeof delta === "string") {
1999
- state.accumulatedText += delta;
2368
+ if (state.accumulatedText.length <= effectiveMaxTextFieldChars()) {
2369
+ state.accumulatedText += delta;
2370
+ }
2000
2371
  this.emitLive(state, "text_delta", delta);
2001
2372
  }
2002
2373
  } else if (chunk.type === "reasoning" || chunk.type === "reasoning-delta") {
@@ -2015,7 +2386,7 @@ var RaindropTelemetryIntegration = class {
2015
2386
  if (state.recordOutputs) {
2016
2387
  outputAttrs.push(
2017
2388
  attrString("ai.response.finishReason", event.finishReason),
2018
- attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
2389
+ attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
2019
2390
  attrString("ai.response.id", (_b = event.response) == null ? void 0 : _b.id),
2020
2391
  attrString("ai.response.model", (_c = event.response) == null ? void 0 : _c.modelId),
2021
2392
  attrString(
@@ -2031,7 +2402,7 @@ var RaindropTelemetryIntegration = class {
2031
2402
  outputAttrs.push(
2032
2403
  attrString(
2033
2404
  "ai.response.toolCalls",
2034
- JSON.stringify(
2405
+ safeJsonWithUint8(
2035
2406
  event.toolCalls.map((tc) => ({
2036
2407
  toolCallId: tc.toolCallId,
2037
2408
  toolName: tc.toolName,
@@ -2044,7 +2415,7 @@ var RaindropTelemetryIntegration = class {
2044
2415
  if (((_g = event.reasoning) == null ? void 0 : _g.length) > 0) {
2045
2416
  const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
2046
2417
  if (reasoningText) {
2047
- outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
2418
+ outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
2048
2419
  }
2049
2420
  }
2050
2421
  }
@@ -2174,8 +2545,56 @@ var RaindropTelemetryIntegration = class {
2174
2545
  if (state.subagentToolCallSpan) {
2175
2546
  this.traceShipper.endSpan(state.subagentToolCallSpan, { error: actualError });
2176
2547
  }
2548
+ const isEmbed = state.operationId === "ai.embed" || state.operationId === "ai.embedMany";
2549
+ if (!isEmbed) {
2550
+ this.finalizeGenerateEvent(state, state.accumulatedText || void 0, void 0);
2551
+ }
2177
2552
  this.cleanup(event.callId);
2178
2553
  };
2554
+ // ── onAbort ─────────────────────────────────────────────────────────────
2555
+ //
2556
+ // Dispatched by the v7 canary line (post-beta.116) when a `streamText` call
2557
+ // is cancelled via its `AbortSignal`. On abort the SDK fires neither `onEnd`
2558
+ // nor `onError`, so without this every open span would leak and the event
2559
+ // would hang forever in its `isPending` state. We close every open span with
2560
+ // an `ai.response.aborted` marker (no error status — an abort is not a model
2561
+ // failure) and flush the partial event.
2562
+ this.onAbort = (event) => {
2563
+ const callId = event == null ? void 0 : event.callId;
2564
+ if (!callId) return;
2565
+ const state = this.getState(callId);
2566
+ if (!state) return;
2567
+ const abortedAttr = attrString("ai.response.aborted", "true");
2568
+ if (state.stepSpan) {
2569
+ this.traceShipper.endSpan(state.stepSpan, { attributes: [abortedAttr] });
2570
+ state.stepSpan = void 0;
2571
+ state.stepParent = void 0;
2572
+ }
2573
+ for (const embedSpan of state.embedSpans.values()) {
2574
+ this.traceShipper.endSpan(embedSpan, { attributes: [abortedAttr] });
2575
+ }
2576
+ state.embedSpans.clear();
2577
+ for (const toolCallId of state.parentContextToolCallIds) {
2578
+ this.priorParentContexts.delete(toolCallId);
2579
+ }
2580
+ state.parentContextToolCallIds.clear();
2581
+ for (const toolSpan of state.toolSpans.values()) {
2582
+ this.traceShipper.endSpan(toolSpan, { attributes: [abortedAttr] });
2583
+ }
2584
+ state.toolSpans.clear();
2585
+ if (state.rootSpan) {
2586
+ this.traceShipper.endSpan(state.rootSpan, {
2587
+ attributes: [abortedAttr, attrInt("ai.toolCall.count", state.toolCallCount)]
2588
+ });
2589
+ }
2590
+ if (state.subagentToolCallSpan) {
2591
+ this.traceShipper.endSpan(state.subagentToolCallSpan, {
2592
+ attributes: [abortedAttr]
2593
+ });
2594
+ }
2595
+ this.finalizeGenerateEvent(state, state.accumulatedText || void 0, void 0);
2596
+ this.cleanup(callId);
2597
+ };
2179
2598
  // ── executeTool ─────────────────────────────────────────────────────────
2180
2599
  this.executeTool = async ({
2181
2600
  callId,
@@ -2333,11 +2752,12 @@ var RaindropTelemetryIntegration = class {
2333
2752
  const toolSpan = state.toolSpans.get(event.toolCall.toolCallId);
2334
2753
  if (!toolSpan) return;
2335
2754
  state.toolCallCount += 1;
2336
- if (event.success) {
2337
- const outputAttrs = state.recordOutputs ? [attrString("ai.toolCall.result", safeJsonWithUint8(event.output))] : [];
2755
+ const outcome = "toolOutput" in event ? event.toolOutput.type === "tool-result" ? { success: true, output: event.toolOutput.output } : { success: false, error: event.toolOutput.error } : event.success ? { success: true, output: event.output } : { success: false, error: event.error };
2756
+ if (outcome.success) {
2757
+ const outputAttrs = state.recordOutputs ? [attrString("ai.toolCall.result", safeJsonWithUint8(outcome.output))] : [];
2338
2758
  this.traceShipper.endSpan(toolSpan, { attributes: outputAttrs });
2339
2759
  } else {
2340
- this.traceShipper.endSpan(toolSpan, { error: event.error });
2760
+ this.traceShipper.endSpan(toolSpan, { error: outcome.error });
2341
2761
  }
2342
2762
  this.emitLive(state, "tool_result", event.toolCall.toolName);
2343
2763
  state.toolSpans.delete(event.toolCall.toolCallId);
@@ -2411,13 +2831,13 @@ var RaindropTelemetryIntegration = class {
2411
2831
  }
2412
2832
  }
2413
2833
  finishGenerate(event, state) {
2414
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
2834
+ var _a, _b, _c, _d, _e, _f;
2415
2835
  if (state.rootSpan) {
2416
2836
  const outputAttrs = [];
2417
2837
  if (state.recordOutputs) {
2418
2838
  outputAttrs.push(
2419
2839
  attrString("ai.response.finishReason", event.finishReason),
2420
- attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
2840
+ attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
2421
2841
  attrString(
2422
2842
  "ai.response.providerMetadata",
2423
2843
  event.providerMetadata ? safeJsonWithUint8(event.providerMetadata) : void 0
@@ -2427,7 +2847,7 @@ var RaindropTelemetryIntegration = class {
2427
2847
  outputAttrs.push(
2428
2848
  attrString(
2429
2849
  "ai.response.toolCalls",
2430
- JSON.stringify(
2850
+ safeJsonWithUint8(
2431
2851
  event.toolCalls.map((tc) => ({
2432
2852
  toolCallId: tc.toolCallId,
2433
2853
  toolName: tc.toolName,
@@ -2440,7 +2860,7 @@ var RaindropTelemetryIntegration = class {
2440
2860
  if (((_c = event.reasoning) == null ? void 0 : _c.length) > 0) {
2441
2861
  const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
2442
2862
  if (reasoningText) {
2443
- outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
2863
+ outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
2444
2864
  }
2445
2865
  }
2446
2866
  }
@@ -2459,38 +2879,48 @@ var RaindropTelemetryIntegration = class {
2459
2879
  );
2460
2880
  this.traceShipper.endSpan(state.rootSpan, { attributes: outputAttrs });
2461
2881
  }
2882
+ this.finalizeGenerateEvent(
2883
+ state,
2884
+ (_e = event.text) != null ? _e : state.accumulatedText || void 0,
2885
+ (_f = event.response) == null ? void 0 : _f.modelId
2886
+ );
2887
+ }
2888
+ /**
2889
+ * Patch the Raindrop event for a completed, aborted, or errored text
2890
+ * generation so it leaves the `isPending` state. Shared by `onFinish`/`onEnd`,
2891
+ * `onAbort`, and `onError`.
2892
+ */
2893
+ finalizeGenerateEvent(state, output, model) {
2894
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2462
2895
  const suppressSubagentEvent = state.subagentName !== void 0 && state.subagentToolCallSpan !== void 0;
2463
- if (this.sendEvents && !suppressSubagentEvent) {
2464
- const callMeta = this.extractRaindropMetadata(state.metadata);
2465
- const userId = (_f = callMeta.userId) != null ? _f : (_e = this.defaultContext) == null ? void 0 : _e.userId;
2466
- if (userId) {
2467
- const eventName = (_i = (_h = callMeta.eventName) != null ? _h : (_g = this.defaultContext) == null ? void 0 : _g.eventName) != null ? _i : state.operationId;
2468
- const output = (_j = event.text) != null ? _j : state.accumulatedText || void 0;
2469
- const input = state.inputText;
2470
- const model = (_k = event.response) == null ? void 0 : _k.modelId;
2471
- const properties = {
2472
- ...(_l = this.defaultContext) == null ? void 0 : _l.properties,
2473
- ...callMeta.properties
2474
- };
2475
- const convoId = (_n = callMeta.convoId) != null ? _n : (_m = this.defaultContext) == null ? void 0 : _m.convoId;
2476
- void this.eventShipper.patch(state.eventId, {
2477
- eventName,
2478
- userId,
2479
- convoId,
2480
- input,
2481
- output,
2482
- model,
2483
- properties: Object.keys(properties).length > 0 ? properties : void 0,
2484
- isPending: false
2485
- }).catch((err) => {
2486
- if (this.debug) {
2487
- console.warn(
2488
- `[raindrop-ai/ai-sdk] event patch failed: ${err instanceof Error ? err.message : err}`
2489
- );
2490
- }
2491
- });
2896
+ if (!this.sendEvents || suppressSubagentEvent) return;
2897
+ const callMeta = this.extractRaindropMetadata(state.metadata);
2898
+ const userId = (_b = callMeta.userId) != null ? _b : (_a = this.defaultContext) == null ? void 0 : _a.userId;
2899
+ if (!userId) return;
2900
+ const eventName = (_e = (_d = callMeta.eventName) != null ? _d : (_c = this.defaultContext) == null ? void 0 : _c.eventName) != null ? _e : state.operationId;
2901
+ const cappedOutput = capText2(output);
2902
+ const input = capText2(state.inputText);
2903
+ const properties = {
2904
+ ...(_f = this.defaultContext) == null ? void 0 : _f.properties,
2905
+ ...callMeta.properties
2906
+ };
2907
+ const convoId = (_h = callMeta.convoId) != null ? _h : (_g = this.defaultContext) == null ? void 0 : _g.convoId;
2908
+ void this.eventShipper.patch(state.eventId, {
2909
+ eventName,
2910
+ userId,
2911
+ convoId,
2912
+ input,
2913
+ output: cappedOutput,
2914
+ model,
2915
+ properties: Object.keys(properties).length > 0 ? properties : void 0,
2916
+ isPending: false
2917
+ }).catch((err) => {
2918
+ if (this.debug) {
2919
+ console.warn(
2920
+ `[raindrop-ai/ai-sdk] event patch failed: ${err instanceof Error ? err.message : err}`
2921
+ );
2492
2922
  }
2493
- }
2923
+ });
2494
2924
  }
2495
2925
  finishEmbed(event, state) {
2496
2926
  var _a;
@@ -2940,13 +3370,14 @@ function shouldKeepEventPending(params) {
2940
3370
  return params.finishReason === "tool-calls" || params.finishReason === "tool_calls";
2941
3371
  }
2942
3372
  function normalizePromptAttr(arg) {
3373
+ var _a, _b;
2943
3374
  if (!isRecord(arg)) return arg;
2944
3375
  const system = arg["system"];
2945
3376
  const prompt = arg["prompt"];
2946
3377
  const messages = arg["messages"];
2947
3378
  if (Array.isArray(messages)) {
2948
3379
  if (system) {
2949
- const sysContent = typeof system === "string" ? system : JSON.stringify(system);
3380
+ const sysContent = typeof system === "string" ? system : (_a = safeJsonWithUint8(system)) != null ? _a : String(system);
2950
3381
  return [{ role: "system", content: sysContent }, ...messages];
2951
3382
  }
2952
3383
  return messages;
@@ -2954,7 +3385,10 @@ function normalizePromptAttr(arg) {
2954
3385
  if (typeof prompt === "string") {
2955
3386
  const msgs = [];
2956
3387
  if (system) {
2957
- msgs.push({ role: "system", content: typeof system === "string" ? system : JSON.stringify(system) });
3388
+ msgs.push({
3389
+ role: "system",
3390
+ content: typeof system === "string" ? system : (_b = safeJsonWithUint8(system)) != null ? _b : String(system)
3391
+ });
2958
3392
  }
2959
3393
  msgs.push({ role: "user", content: prompt });
2960
3394
  return msgs;
@@ -3123,7 +3557,8 @@ function createFinalize(params) {
3123
3557
  };
3124
3558
  const built = await maybeBuildEvent(options.buildEvent, allMessages);
3125
3559
  const patch = mergeBuildEventPatch(defaultPatch, built);
3126
- const output = patch.output;
3560
+ const output = capText2(patch.output);
3561
+ const input = capText2(patch.input);
3127
3562
  const finalModel = (_c = patch.model) != null ? _c : model;
3128
3563
  if (setup.rootSpan) {
3129
3564
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3167,7 +3602,7 @@ function createFinalize(params) {
3167
3602
  eventName: patch.eventName,
3168
3603
  userId: setup.ctx.userId,
3169
3604
  convoId: setup.ctx.convoId,
3170
- input: patch.input,
3605
+ input,
3171
3606
  output,
3172
3607
  model: finalModel,
3173
3608
  properties: patch.properties,
@@ -3682,7 +4117,8 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
3682
4117
  };
3683
4118
  const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
3684
4119
  const patch = mergeBuildEventPatch(defaultPatch, built);
3685
- const output = patch.output;
4120
+ const output = capText2(patch.output);
4121
+ const input = capText2(patch.input);
3686
4122
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
3687
4123
  if (rootSpan) {
3688
4124
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3740,7 +4176,7 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
3740
4176
  eventName: patch.eventName,
3741
4177
  userId: ctx.userId,
3742
4178
  convoId: ctx.convoId,
3743
- input: patch.input,
4179
+ input,
3744
4180
  output,
3745
4181
  model: finalModel,
3746
4182
  properties: patch.properties,
@@ -3889,7 +4325,8 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
3889
4325
  };
3890
4326
  const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
3891
4327
  const patch = mergeBuildEventPatch(defaultPatch, built);
3892
- const output = patch.output;
4328
+ const output = capText2(patch.output);
4329
+ const input = capText2(patch.input);
3893
4330
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
3894
4331
  if (rootSpan) {
3895
4332
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3947,7 +4384,7 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
3947
4384
  eventName: patch.eventName,
3948
4385
  userId: ctx.userId,
3949
4386
  convoId: ctx.convoId,
3950
- input: patch.input,
4387
+ input,
3951
4388
  output,
3952
4389
  model: finalModel,
3953
4390
  properties: patch.properties,
@@ -4287,7 +4724,7 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4287
4724
  attributes: [
4288
4725
  ...((_a = ctx.telemetry) == null ? void 0 : _a.recordOutputs) === false ? [] : [
4289
4726
  attrString("ai.response.finishReason", finishReason),
4290
- attrString("ai.response.text", activeText.length ? activeText : void 0),
4727
+ attrString("ai.response.text", activeText.length ? capText2(activeText) : void 0),
4291
4728
  attrString(
4292
4729
  "ai.response.toolCalls",
4293
4730
  safeJsonWithUint8(toolCallsLocal.length ? toolCallsLocal : void 0)
@@ -4340,7 +4777,9 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4340
4777
  } else if (typeof value["textDelta"] === "string") {
4341
4778
  textDelta = value["textDelta"];
4342
4779
  }
4343
- if (typeof textDelta === "string") activeText += textDelta;
4780
+ if (typeof textDelta === "string" && activeText.length <= effectiveMaxTextFieldChars()) {
4781
+ activeText += textDelta;
4782
+ }
4344
4783
  }
4345
4784
  if (type === "finish") finishReason = extractFinishReason(value);
4346
4785
  if (type === "tool-call") toolCallsLocal.push(value);
@@ -4575,7 +5014,7 @@ function extractNestedTokens(usage, key) {
4575
5014
  // package.json
4576
5015
  var package_default = {
4577
5016
  name: "@raindrop-ai/ai-sdk",
4578
- version: "0.0.32"};
5017
+ version: "0.0.34"};
4579
5018
 
4580
5019
  // src/internal/version.ts
4581
5020
  var libraryName = package_default.name;
@@ -4648,8 +5087,8 @@ function eventMetadataFromChatRequest(options) {
4648
5087
  });
4649
5088
  }
4650
5089
  function stringify(v) {
4651
- if (typeof v === "string") return v;
4652
- return JSON.stringify(v);
5090
+ if (typeof v === "string") return capText2(v);
5091
+ return boundedStringify(v);
4653
5092
  }
4654
5093
  function userAttrsToOtlp(attrs) {
4655
5094
  if (!attrs) return [];
@@ -4667,6 +5106,7 @@ function createRaindropAISDK(opts) {
4667
5106
  const eventsRequested = ((_a = opts.events) == null ? void 0 : _a.enabled) !== false;
4668
5107
  const tracesRequested = ((_b = opts.traces) == null ? void 0 : _b.enabled) !== false;
4669
5108
  const envDebug = envDebugEnabled();
5109
+ setMaxTextFieldChars(opts.maxTextFieldChars);
4670
5110
  const localWorkshopInput = opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl;
4671
5111
  const resolvedLocalDebuggerUrl = resolveLocalDebuggerBaseUrl(localWorkshopInput);
4672
5112
  const localDebuggerUrl = localWorkshopInput === null ? null : resolvedLocalDebuggerUrl != null ? resolvedLocalDebuggerUrl : void 0;
@@ -4845,5 +5285,12 @@ function createRaindropAISDK(opts) {
4845
5285
  }
4846
5286
  };
4847
5287
  }
5288
+ function raindrop(options = {}) {
5289
+ var _a;
5290
+ const { context, subagentWrapping, writeKey, ...rest } = options;
5291
+ const resolvedWriteKey = writeKey != null ? writeKey : typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a.RAINDROP_WRITE_KEY : void 0;
5292
+ const client = createRaindropAISDK({ ...rest, writeKey: resolvedWriteKey });
5293
+ return client.createTelemetryIntegration({ context, subagentWrapping });
5294
+ }
4848
5295
 
4849
- 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, readRaindropCallMetadataFromArgs, redactJsonAttributeValue, redactSecretsInObject, runWithParentToolContext, runWithRaindropCallMetadata, withCurrent };
5296
+ 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 };