@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.
@@ -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.32"};
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") {
@@ -1807,6 +2154,25 @@ var RaindropTelemetryIntegration = class {
1807
2154
  * (the `event.callId` can be the same for parallel sibling tools).
1808
2155
  */
1809
2156
  this.priorParentContexts = /* @__PURE__ */ new Map();
2157
+ // ── lifecycle ─────────────────────────────────────────────────────────────
2158
+ //
2159
+ // The shippers buffer spans/events and flush on a timer, so a short-lived
2160
+ // script (e.g. a single `generateText` then `process.exit`) can exit before
2161
+ // anything ships. Expose `flush`/`shutdown` on the integration itself so the
2162
+ // value returned by `raindrop()` is self-sufficient — callers can keep the
2163
+ // reference they pass to `registerTelemetry` and `await rd.flush()` before
2164
+ // exiting, without also constructing a separate client.
2165
+ /** Flush any buffered events and trace spans to their destinations. */
2166
+ this.flush = async () => {
2167
+ await Promise.all([this.eventShipper.flush(), this.traceShipper.flush()]);
2168
+ };
2169
+ /** Flush and stop the background flush timers. */
2170
+ this.shutdown = async () => {
2171
+ await Promise.all([
2172
+ this.eventShipper.shutdown(),
2173
+ this.traceShipper.shutdown()
2174
+ ]);
2175
+ };
1810
2176
  // ── onStart ─────────────────────────────────────────────────────────────
1811
2177
  this.onStart = (event) => {
1812
2178
  var _a, _b, _c, _d, _e;
@@ -1958,7 +2324,10 @@ var RaindropTelemetryIntegration = class {
1958
2324
  inputAttrs.push(
1959
2325
  attrStringArray(
1960
2326
  "ai.prompt.tools",
1961
- event.stepTools.map((t) => JSON.stringify(t))
2327
+ event.stepTools.map((t) => {
2328
+ var _a;
2329
+ return (_a = safeJsonWithUint8(t)) != null ? _a : "";
2330
+ })
1962
2331
  )
1963
2332
  );
1964
2333
  }
@@ -1966,7 +2335,7 @@ var RaindropTelemetryIntegration = class {
1966
2335
  inputAttrs.push(
1967
2336
  attrString(
1968
2337
  "ai.prompt.toolChoice",
1969
- JSON.stringify(event.stepToolChoice)
2338
+ safeJsonWithUint8(event.stepToolChoice)
1970
2339
  )
1971
2340
  );
1972
2341
  }
@@ -2022,7 +2391,9 @@ var RaindropTelemetryIntegration = class {
2022
2391
  if (chunk.type === "text-delta") {
2023
2392
  const delta = (_c = chunk.textDelta) != null ? _c : chunk.delta;
2024
2393
  if (typeof delta === "string") {
2025
- state.accumulatedText += delta;
2394
+ if (state.accumulatedText.length <= effectiveMaxTextFieldChars()) {
2395
+ state.accumulatedText += delta;
2396
+ }
2026
2397
  this.emitLive(state, "text_delta", delta);
2027
2398
  }
2028
2399
  } else if (chunk.type === "reasoning" || chunk.type === "reasoning-delta") {
@@ -2041,7 +2412,7 @@ var RaindropTelemetryIntegration = class {
2041
2412
  if (state.recordOutputs) {
2042
2413
  outputAttrs.push(
2043
2414
  attrString("ai.response.finishReason", event.finishReason),
2044
- attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
2415
+ attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
2045
2416
  attrString("ai.response.id", (_b = event.response) == null ? void 0 : _b.id),
2046
2417
  attrString("ai.response.model", (_c = event.response) == null ? void 0 : _c.modelId),
2047
2418
  attrString(
@@ -2057,7 +2428,7 @@ var RaindropTelemetryIntegration = class {
2057
2428
  outputAttrs.push(
2058
2429
  attrString(
2059
2430
  "ai.response.toolCalls",
2060
- JSON.stringify(
2431
+ safeJsonWithUint8(
2061
2432
  event.toolCalls.map((tc) => ({
2062
2433
  toolCallId: tc.toolCallId,
2063
2434
  toolName: tc.toolName,
@@ -2070,7 +2441,7 @@ var RaindropTelemetryIntegration = class {
2070
2441
  if (((_g = event.reasoning) == null ? void 0 : _g.length) > 0) {
2071
2442
  const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
2072
2443
  if (reasoningText) {
2073
- outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
2444
+ outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
2074
2445
  }
2075
2446
  }
2076
2447
  }
@@ -2200,8 +2571,56 @@ var RaindropTelemetryIntegration = class {
2200
2571
  if (state.subagentToolCallSpan) {
2201
2572
  this.traceShipper.endSpan(state.subagentToolCallSpan, { error: actualError });
2202
2573
  }
2574
+ const isEmbed = state.operationId === "ai.embed" || state.operationId === "ai.embedMany";
2575
+ if (!isEmbed) {
2576
+ this.finalizeGenerateEvent(state, state.accumulatedText || void 0, void 0);
2577
+ }
2203
2578
  this.cleanup(event.callId);
2204
2579
  };
2580
+ // ── onAbort ─────────────────────────────────────────────────────────────
2581
+ //
2582
+ // Dispatched by the v7 canary line (post-beta.116) when a `streamText` call
2583
+ // is cancelled via its `AbortSignal`. On abort the SDK fires neither `onEnd`
2584
+ // nor `onError`, so without this every open span would leak and the event
2585
+ // would hang forever in its `isPending` state. We close every open span with
2586
+ // an `ai.response.aborted` marker (no error status — an abort is not a model
2587
+ // failure) and flush the partial event.
2588
+ this.onAbort = (event) => {
2589
+ const callId = event == null ? void 0 : event.callId;
2590
+ if (!callId) return;
2591
+ const state = this.getState(callId);
2592
+ if (!state) return;
2593
+ const abortedAttr = attrString("ai.response.aborted", "true");
2594
+ if (state.stepSpan) {
2595
+ this.traceShipper.endSpan(state.stepSpan, { attributes: [abortedAttr] });
2596
+ state.stepSpan = void 0;
2597
+ state.stepParent = void 0;
2598
+ }
2599
+ for (const embedSpan of state.embedSpans.values()) {
2600
+ this.traceShipper.endSpan(embedSpan, { attributes: [abortedAttr] });
2601
+ }
2602
+ state.embedSpans.clear();
2603
+ for (const toolCallId of state.parentContextToolCallIds) {
2604
+ this.priorParentContexts.delete(toolCallId);
2605
+ }
2606
+ state.parentContextToolCallIds.clear();
2607
+ for (const toolSpan of state.toolSpans.values()) {
2608
+ this.traceShipper.endSpan(toolSpan, { attributes: [abortedAttr] });
2609
+ }
2610
+ state.toolSpans.clear();
2611
+ if (state.rootSpan) {
2612
+ this.traceShipper.endSpan(state.rootSpan, {
2613
+ attributes: [abortedAttr, attrInt("ai.toolCall.count", state.toolCallCount)]
2614
+ });
2615
+ }
2616
+ if (state.subagentToolCallSpan) {
2617
+ this.traceShipper.endSpan(state.subagentToolCallSpan, {
2618
+ attributes: [abortedAttr]
2619
+ });
2620
+ }
2621
+ this.finalizeGenerateEvent(state, state.accumulatedText || void 0, void 0);
2622
+ this.cleanup(callId);
2623
+ };
2205
2624
  // ── executeTool ─────────────────────────────────────────────────────────
2206
2625
  this.executeTool = async ({
2207
2626
  callId,
@@ -2359,11 +2778,12 @@ var RaindropTelemetryIntegration = class {
2359
2778
  const toolSpan = state.toolSpans.get(event.toolCall.toolCallId);
2360
2779
  if (!toolSpan) return;
2361
2780
  state.toolCallCount += 1;
2362
- if (event.success) {
2363
- const outputAttrs = state.recordOutputs ? [attrString("ai.toolCall.result", safeJsonWithUint8(event.output))] : [];
2781
+ 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 };
2782
+ if (outcome.success) {
2783
+ const outputAttrs = state.recordOutputs ? [attrString("ai.toolCall.result", safeJsonWithUint8(outcome.output))] : [];
2364
2784
  this.traceShipper.endSpan(toolSpan, { attributes: outputAttrs });
2365
2785
  } else {
2366
- this.traceShipper.endSpan(toolSpan, { error: event.error });
2786
+ this.traceShipper.endSpan(toolSpan, { error: outcome.error });
2367
2787
  }
2368
2788
  this.emitLive(state, "tool_result", event.toolCall.toolName);
2369
2789
  state.toolSpans.delete(event.toolCall.toolCallId);
@@ -2437,13 +2857,13 @@ var RaindropTelemetryIntegration = class {
2437
2857
  }
2438
2858
  }
2439
2859
  finishGenerate(event, state) {
2440
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
2860
+ var _a, _b, _c, _d, _e, _f;
2441
2861
  if (state.rootSpan) {
2442
2862
  const outputAttrs = [];
2443
2863
  if (state.recordOutputs) {
2444
2864
  outputAttrs.push(
2445
2865
  attrString("ai.response.finishReason", event.finishReason),
2446
- attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
2866
+ attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
2447
2867
  attrString(
2448
2868
  "ai.response.providerMetadata",
2449
2869
  event.providerMetadata ? safeJsonWithUint8(event.providerMetadata) : void 0
@@ -2453,7 +2873,7 @@ var RaindropTelemetryIntegration = class {
2453
2873
  outputAttrs.push(
2454
2874
  attrString(
2455
2875
  "ai.response.toolCalls",
2456
- JSON.stringify(
2876
+ safeJsonWithUint8(
2457
2877
  event.toolCalls.map((tc) => ({
2458
2878
  toolCallId: tc.toolCallId,
2459
2879
  toolName: tc.toolName,
@@ -2466,7 +2886,7 @@ var RaindropTelemetryIntegration = class {
2466
2886
  if (((_c = event.reasoning) == null ? void 0 : _c.length) > 0) {
2467
2887
  const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
2468
2888
  if (reasoningText) {
2469
- outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
2889
+ outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
2470
2890
  }
2471
2891
  }
2472
2892
  }
@@ -2485,38 +2905,48 @@ var RaindropTelemetryIntegration = class {
2485
2905
  );
2486
2906
  this.traceShipper.endSpan(state.rootSpan, { attributes: outputAttrs });
2487
2907
  }
2908
+ this.finalizeGenerateEvent(
2909
+ state,
2910
+ (_e = event.text) != null ? _e : state.accumulatedText || void 0,
2911
+ (_f = event.response) == null ? void 0 : _f.modelId
2912
+ );
2913
+ }
2914
+ /**
2915
+ * Patch the Raindrop event for a completed, aborted, or errored text
2916
+ * generation so it leaves the `isPending` state. Shared by `onFinish`/`onEnd`,
2917
+ * `onAbort`, and `onError`.
2918
+ */
2919
+ finalizeGenerateEvent(state, output, model) {
2920
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2488
2921
  const suppressSubagentEvent = state.subagentName !== void 0 && state.subagentToolCallSpan !== void 0;
2489
- if (this.sendEvents && !suppressSubagentEvent) {
2490
- const callMeta = this.extractRaindropMetadata(state.metadata);
2491
- const userId = (_f = callMeta.userId) != null ? _f : (_e = this.defaultContext) == null ? void 0 : _e.userId;
2492
- if (userId) {
2493
- const eventName = (_i = (_h = callMeta.eventName) != null ? _h : (_g = this.defaultContext) == null ? void 0 : _g.eventName) != null ? _i : state.operationId;
2494
- const output = (_j = event.text) != null ? _j : state.accumulatedText || void 0;
2495
- const input = state.inputText;
2496
- const model = (_k = event.response) == null ? void 0 : _k.modelId;
2497
- const properties = {
2498
- ...(_l = this.defaultContext) == null ? void 0 : _l.properties,
2499
- ...callMeta.properties
2500
- };
2501
- const convoId = (_n = callMeta.convoId) != null ? _n : (_m = this.defaultContext) == null ? void 0 : _m.convoId;
2502
- void this.eventShipper.patch(state.eventId, {
2503
- eventName,
2504
- userId,
2505
- convoId,
2506
- input,
2507
- output,
2508
- model,
2509
- properties: Object.keys(properties).length > 0 ? properties : void 0,
2510
- isPending: false
2511
- }).catch((err) => {
2512
- if (this.debug) {
2513
- console.warn(
2514
- `[raindrop-ai/ai-sdk] event patch failed: ${err instanceof Error ? err.message : err}`
2515
- );
2516
- }
2517
- });
2922
+ if (!this.sendEvents || suppressSubagentEvent) return;
2923
+ const callMeta = this.extractRaindropMetadata(state.metadata);
2924
+ const userId = (_b = callMeta.userId) != null ? _b : (_a = this.defaultContext) == null ? void 0 : _a.userId;
2925
+ if (!userId) return;
2926
+ const eventName = (_e = (_d = callMeta.eventName) != null ? _d : (_c = this.defaultContext) == null ? void 0 : _c.eventName) != null ? _e : state.operationId;
2927
+ const cappedOutput = capText2(output);
2928
+ const input = capText2(state.inputText);
2929
+ const properties = {
2930
+ ...(_f = this.defaultContext) == null ? void 0 : _f.properties,
2931
+ ...callMeta.properties
2932
+ };
2933
+ const convoId = (_h = callMeta.convoId) != null ? _h : (_g = this.defaultContext) == null ? void 0 : _g.convoId;
2934
+ void this.eventShipper.patch(state.eventId, {
2935
+ eventName,
2936
+ userId,
2937
+ convoId,
2938
+ input,
2939
+ output: cappedOutput,
2940
+ model,
2941
+ properties: Object.keys(properties).length > 0 ? properties : void 0,
2942
+ isPending: false
2943
+ }).catch((err) => {
2944
+ if (this.debug) {
2945
+ console.warn(
2946
+ `[raindrop-ai/ai-sdk] event patch failed: ${err instanceof Error ? err.message : err}`
2947
+ );
2518
2948
  }
2519
- }
2949
+ });
2520
2950
  }
2521
2951
  finishEmbed(event, state) {
2522
2952
  var _a;
@@ -2979,13 +3409,14 @@ function shouldKeepEventPending(params) {
2979
3409
  return params.finishReason === "tool-calls" || params.finishReason === "tool_calls";
2980
3410
  }
2981
3411
  function normalizePromptAttr(arg) {
3412
+ var _a, _b;
2982
3413
  if (!isRecord(arg)) return arg;
2983
3414
  const system = arg["system"];
2984
3415
  const prompt = arg["prompt"];
2985
3416
  const messages = arg["messages"];
2986
3417
  if (Array.isArray(messages)) {
2987
3418
  if (system) {
2988
- const sysContent = typeof system === "string" ? system : JSON.stringify(system);
3419
+ const sysContent = typeof system === "string" ? system : (_a = safeJsonWithUint8(system)) != null ? _a : String(system);
2989
3420
  return [{ role: "system", content: sysContent }, ...messages];
2990
3421
  }
2991
3422
  return messages;
@@ -2993,7 +3424,10 @@ function normalizePromptAttr(arg) {
2993
3424
  if (typeof prompt === "string") {
2994
3425
  const msgs = [];
2995
3426
  if (system) {
2996
- 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
+ });
2997
3431
  }
2998
3432
  msgs.push({ role: "user", content: prompt });
2999
3433
  return msgs;
@@ -3162,7 +3596,8 @@ function createFinalize(params) {
3162
3596
  };
3163
3597
  const built = await maybeBuildEvent(options.buildEvent, allMessages);
3164
3598
  const patch = mergeBuildEventPatch(defaultPatch, built);
3165
- const output = patch.output;
3599
+ const output = capText2(patch.output);
3600
+ const input = capText2(patch.input);
3166
3601
  const finalModel = (_c = patch.model) != null ? _c : model;
3167
3602
  if (setup.rootSpan) {
3168
3603
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3206,7 +3641,7 @@ function createFinalize(params) {
3206
3641
  eventName: patch.eventName,
3207
3642
  userId: setup.ctx.userId,
3208
3643
  convoId: setup.ctx.convoId,
3209
- input: patch.input,
3644
+ input,
3210
3645
  output,
3211
3646
  model: finalModel,
3212
3647
  properties: patch.properties,
@@ -3721,7 +4156,8 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
3721
4156
  };
3722
4157
  const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
3723
4158
  const patch = mergeBuildEventPatch(defaultPatch, built);
3724
- const output = patch.output;
4159
+ const output = capText2(patch.output);
4160
+ const input = capText2(patch.input);
3725
4161
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
3726
4162
  if (rootSpan) {
3727
4163
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3779,7 +4215,7 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
3779
4215
  eventName: patch.eventName,
3780
4216
  userId: ctx.userId,
3781
4217
  convoId: ctx.convoId,
3782
- input: patch.input,
4218
+ input,
3783
4219
  output,
3784
4220
  model: finalModel,
3785
4221
  properties: patch.properties,
@@ -3928,7 +4364,8 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
3928
4364
  };
3929
4365
  const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
3930
4366
  const patch = mergeBuildEventPatch(defaultPatch, built);
3931
- const output = patch.output;
4367
+ const output = capText2(patch.output);
4368
+ const input = capText2(patch.input);
3932
4369
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
3933
4370
  if (rootSpan) {
3934
4371
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3986,7 +4423,7 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
3986
4423
  eventName: patch.eventName,
3987
4424
  userId: ctx.userId,
3988
4425
  convoId: ctx.convoId,
3989
- input: patch.input,
4426
+ input,
3990
4427
  output,
3991
4428
  model: finalModel,
3992
4429
  properties: patch.properties,
@@ -4326,7 +4763,7 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4326
4763
  attributes: [
4327
4764
  ...((_a = ctx.telemetry) == null ? void 0 : _a.recordOutputs) === false ? [] : [
4328
4765
  attrString("ai.response.finishReason", finishReason),
4329
- attrString("ai.response.text", activeText.length ? activeText : void 0),
4766
+ attrString("ai.response.text", activeText.length ? capText2(activeText) : void 0),
4330
4767
  attrString(
4331
4768
  "ai.response.toolCalls",
4332
4769
  safeJsonWithUint8(toolCallsLocal.length ? toolCallsLocal : void 0)
@@ -4379,7 +4816,9 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4379
4816
  } else if (typeof value["textDelta"] === "string") {
4380
4817
  textDelta = value["textDelta"];
4381
4818
  }
4382
- if (typeof textDelta === "string") activeText += textDelta;
4819
+ if (typeof textDelta === "string" && activeText.length <= effectiveMaxTextFieldChars()) {
4820
+ activeText += textDelta;
4821
+ }
4383
4822
  }
4384
4823
  if (type === "finish") finishReason = extractFinishReason(value);
4385
4824
  if (type === "tool-call") toolCallsLocal.push(value);
@@ -4652,8 +5091,8 @@ function eventMetadataFromChatRequest(options) {
4652
5091
  });
4653
5092
  }
4654
5093
  function stringify(v) {
4655
- if (typeof v === "string") return v;
4656
- return JSON.stringify(v);
5094
+ if (typeof v === "string") return capText2(v);
5095
+ return boundedStringify(v);
4657
5096
  }
4658
5097
  function userAttrsToOtlp(attrs) {
4659
5098
  if (!attrs) return [];
@@ -4671,6 +5110,7 @@ function createRaindropAISDK(opts) {
4671
5110
  const eventsRequested = ((_a = opts.events) == null ? void 0 : _a.enabled) !== false;
4672
5111
  const tracesRequested = ((_b = opts.traces) == null ? void 0 : _b.enabled) !== false;
4673
5112
  const envDebug = envDebugEnabled();
5113
+ setMaxTextFieldChars(opts.maxTextFieldChars);
4674
5114
  const localWorkshopInput = opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl;
4675
5115
  const resolvedLocalDebuggerUrl = resolveLocalDebuggerBaseUrl(localWorkshopInput);
4676
5116
  const localDebuggerUrl = localWorkshopInput === null ? null : resolvedLocalDebuggerUrl != null ? resolvedLocalDebuggerUrl : void 0;
@@ -4849,17 +5289,28 @@ function createRaindropAISDK(opts) {
4849
5289
  }
4850
5290
  };
4851
5291
  }
5292
+ function raindrop(options = {}) {
5293
+ var _a;
5294
+ const { context, subagentWrapping, writeKey, ...rest } = options;
5295
+ const resolvedWriteKey = writeKey != null ? writeKey : typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a.RAINDROP_WRITE_KEY : void 0;
5296
+ const client = createRaindropAISDK({ ...rest, writeKey: resolvedWriteKey });
5297
+ return client.createTelemetryIntegration({ context, subagentWrapping });
5298
+ }
4852
5299
 
4853
5300
  // src/index.node.ts
4854
5301
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = async_hooks.AsyncLocalStorage;
4855
5302
 
5303
+ exports.DEFAULT_MAX_TEXT_FIELD_CHARS = DEFAULT_MAX_TEXT_FIELD_CHARS2;
4856
5304
  exports.DEFAULT_REDACT_ATTRIBUTE_KEYS = DEFAULT_REDACT_ATTRIBUTE_KEYS;
4857
5305
  exports.DEFAULT_SECRET_KEY_NAMES = DEFAULT_SECRET_KEY_NAMES;
4858
5306
  exports.REDACTED_PLACEHOLDER = REDACTED_PLACEHOLDER;
4859
5307
  exports.RaindropTelemetryIntegration = RaindropTelemetryIntegration;
5308
+ exports.TRUNCATION_MARKER = TRUNCATION_MARKER2;
4860
5309
  exports._resetParentToolContextStorage = _resetParentToolContextStorage;
4861
5310
  exports._resetRaindropCallMetadataStorage = _resetRaindropCallMetadataStorage;
4862
5311
  exports._resetWarnedMissingUserId = _resetWarnedMissingUserId;
5312
+ exports.boundedStringify = boundedStringify;
5313
+ exports.capText = capText2;
4863
5314
  exports.clearParentToolContext = clearParentToolContext;
4864
5315
  exports.createRaindropAISDK = createRaindropAISDK;
4865
5316
  exports.currentSpan = currentSpan;
@@ -4870,6 +5321,7 @@ exports.eventMetadataFromChatRequest = eventMetadataFromChatRequest;
4870
5321
  exports.getContextManager = getContextManager;
4871
5322
  exports.getCurrentParentToolContext = getCurrentParentToolContext;
4872
5323
  exports.getCurrentRaindropCallMetadata = getCurrentRaindropCallMetadata;
5324
+ exports.raindrop = raindrop;
4873
5325
  exports.readRaindropCallMetadataFromArgs = readRaindropCallMetadataFromArgs;
4874
5326
  exports.redactJsonAttributeValue = redactJsonAttributeValue;
4875
5327
  exports.redactSecretsInObject = redactSecretsInObject;