@raindrop-ai/ai-sdk 0.0.33 → 0.0.35

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-SK6EJEO7.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,27 @@ function sendLocalDebuggerLiveEvent(event, options = {}) {
331
397
  ).catch(() => {
332
398
  });
333
399
  }
400
+ var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
401
+ var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
402
+ function isValidProjectIdSlug(value) {
403
+ return PROJECT_ID_SLUG_PATTERN.test(value);
404
+ }
405
+ function normalizeProjectId(raw, opts) {
406
+ if (typeof raw !== "string") return void 0;
407
+ const trimmed = raw.trim();
408
+ if (!trimmed) return void 0;
409
+ if (!isValidProjectIdSlug(trimmed) && opts.debug) {
410
+ console.warn(
411
+ `${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
412
+ );
413
+ }
414
+ return trimmed;
415
+ }
416
+ function projectIdHeaders(projectId) {
417
+ return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
418
+ }
419
+ var SHUTDOWN_DEADLINE_MS = 1e4;
420
+ var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
334
421
  function mergePatches(target, source) {
335
422
  var _a, _b, _c, _d;
336
423
  const out = { ...target, ...source };
@@ -348,6 +435,7 @@ var EventShipper = class {
348
435
  this.sticky = /* @__PURE__ */ new Map();
349
436
  this.timers = /* @__PURE__ */ new Map();
350
437
  this.inFlight = /* @__PURE__ */ new Set();
438
+ this.hasShutdown = false;
351
439
  var _a, _b, _c, _d, _e, _f, _g, _h;
352
440
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
353
441
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
@@ -357,10 +445,15 @@ var EventShipper = class {
357
445
  this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
358
446
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
359
447
  this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
448
+ this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
360
449
  this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
361
450
  if (this.debug && this.localDebuggerUrl) {
362
451
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
363
452
  }
453
+ this.projectId = normalizeProjectId(opts.projectId, {
454
+ debug: this.debug,
455
+ prefix: this.prefix
456
+ });
364
457
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
365
458
  this.context = {
366
459
  library: {
@@ -379,10 +472,55 @@ var EventShipper = class {
379
472
  authHeaders() {
380
473
  return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
381
474
  }
475
+ requestHeaders() {
476
+ return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
477
+ }
478
+ /**
479
+ * Build the retry/timeout options for one POST, honoring the shutdown
480
+ * deadline. Returns `null` when the shutdown drain window is exhausted —
481
+ * the caller must drop the payload (with a rate-limited warning) instead
482
+ * of issuing a request that could outlive process exit.
483
+ *
484
+ * Checked fresh on EVERY send, so a shutdown that begins while the flush
485
+ * path is mid-drain takes effect immediately: no further retries, and the
486
+ * per-attempt timeout is clamped to the remaining window. After
487
+ * `shutdown()` returns (deadline cleared, `hasShutdown` still set),
488
+ * sends — late callers, or flush work the deadline abandoned mid-drain —
489
+ * run as a single short attempt rather than regaining the full retry
490
+ * schedule.
491
+ */
492
+ requestOpts() {
493
+ if (this.shutdownDeadlineAt !== void 0) {
494
+ const remainingMs = this.shutdownDeadlineAt - Date.now();
495
+ if (remainingMs <= 0) return null;
496
+ return {
497
+ maxAttempts: 1,
498
+ debug: this.debug,
499
+ sdkName: this.sdkName,
500
+ timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
501
+ };
502
+ }
503
+ if (this.hasShutdown) {
504
+ return {
505
+ maxAttempts: 1,
506
+ debug: this.debug,
507
+ sdkName: this.sdkName,
508
+ timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
509
+ };
510
+ }
511
+ return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
512
+ }
382
513
  async patch(eventId, patch) {
383
514
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
384
515
  if (!this.enabled) return;
385
516
  if (!eventId || !eventId.trim()) return;
517
+ const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
518
+ if (typeof patch.input === "string" && patch.input.length > maxChars) {
519
+ patch = { ...patch, input: capText(patch.input, maxChars) };
520
+ }
521
+ if (typeof patch.output === "string" && patch.output.length > maxChars) {
522
+ patch = { ...patch, output: capText(patch.output, maxChars) };
523
+ }
386
524
  if (this.debug) {
387
525
  console.log(`${this.prefix} queue patch`, {
388
526
  eventId,
@@ -429,9 +567,16 @@ var EventShipper = class {
429
567
  })));
430
568
  }
431
569
  async shutdown() {
432
- for (const t of this.timers.values()) clearTimeout(t);
433
- this.timers.clear();
434
- await this.flush();
570
+ this.hasShutdown = true;
571
+ this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
572
+ try {
573
+ for (const t of this.timers.values()) clearTimeout(t);
574
+ this.timers.clear();
575
+ const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
576
+ if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
577
+ } finally {
578
+ this.shutdownDeadlineAt = void 0;
579
+ }
435
580
  }
436
581
  async trackSignal(signal) {
437
582
  var _a, _b;
@@ -453,15 +598,19 @@ var EventShipper = class {
453
598
  ];
454
599
  if (!this.writeKey) return;
455
600
  const url = `${this.baseUrl}signals/track`;
601
+ const opts = this.requestOpts();
602
+ if (!opts) {
603
+ this.warnShutdownDrop("signal");
604
+ return;
605
+ }
456
606
  try {
457
- await postJson(url, body, this.authHeaders(), {
458
- maxAttempts: 3,
459
- debug: this.debug,
460
- sdkName: this.sdkName
461
- });
607
+ await postJson(url, body, this.requestHeaders(), opts);
462
608
  } catch (err) {
463
609
  const msg = err instanceof Error ? err.message : String(err);
464
- console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`);
610
+ rateLimitedLog(
611
+ `${this.prefix}.send_signal_failed`,
612
+ () => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
613
+ );
465
614
  }
466
615
  }
467
616
  async identify(users) {
@@ -485,17 +634,27 @@ var EventShipper = class {
485
634
  if (!this.writeKey) return;
486
635
  if (body.length === 0) return;
487
636
  const url = `${this.baseUrl}users/identify`;
637
+ const opts = this.requestOpts();
638
+ if (!opts) {
639
+ this.warnShutdownDrop("identify");
640
+ return;
641
+ }
488
642
  try {
489
- await postJson(url, body, this.authHeaders(), {
490
- maxAttempts: 3,
491
- debug: this.debug,
492
- sdkName: this.sdkName
493
- });
643
+ await postJson(url, body, this.requestHeaders(), opts);
494
644
  } catch (err) {
495
645
  const msg = err instanceof Error ? err.message : String(err);
496
- console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`);
646
+ rateLimitedLog(
647
+ `${this.prefix}.send_identify_failed`,
648
+ () => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
649
+ );
497
650
  }
498
651
  }
652
+ warnShutdownDrop(what) {
653
+ rateLimitedLog(
654
+ `${this.prefix}.shutdown_deadline`,
655
+ () => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
656
+ );
657
+ }
499
658
  async flushOne(eventId) {
500
659
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
501
660
  if (!this.enabled) return;
@@ -571,11 +730,13 @@ var EventShipper = class {
571
730
  if (!isPending) this.sticky.delete(eventId);
572
731
  return;
573
732
  }
574
- const p = postJson(url, payload, this.authHeaders(), {
575
- maxAttempts: 3,
576
- debug: this.debug,
577
- sdkName: this.sdkName
578
- });
733
+ const opts = this.requestOpts();
734
+ if (!opts) {
735
+ this.warnShutdownDrop(`track_partial ${eventId}`);
736
+ if (!isPending) this.sticky.delete(eventId);
737
+ return;
738
+ }
739
+ const p = postJson(url, payload, this.requestHeaders(), opts);
579
740
  this.inFlight.add(p);
580
741
  try {
581
742
  try {
@@ -585,7 +746,10 @@ var EventShipper = class {
585
746
  }
586
747
  } catch (err) {
587
748
  const msg = err instanceof Error ? err.message : String(err);
588
- console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`);
749
+ rateLimitedLog(
750
+ `${this.prefix}.send_track_partial_failed`,
751
+ () => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
752
+ );
589
753
  }
590
754
  } finally {
591
755
  this.inFlight.delete(p);
@@ -684,10 +848,24 @@ function redactJsonAttributeValue(key, value) {
684
848
  if (scrubbedJson === json) return void 0;
685
849
  return { stringValue: scrubbedJson };
686
850
  }
851
+ function applyOtelSpanAttributeLimit(limit) {
852
+ var _a, _b;
853
+ try {
854
+ 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;
855
+ if (!raw) return limit;
856
+ const parsed = Number.parseInt(raw, 10);
857
+ if (Number.isFinite(parsed) && parsed > 0) {
858
+ return Math.min(limit, parsed);
859
+ }
860
+ } catch (e) {
861
+ }
862
+ return limit;
863
+ }
687
864
  var TraceShipper = class {
688
865
  constructor(opts) {
689
866
  this.queue = [];
690
867
  this.inFlight = /* @__PURE__ */ new Set();
868
+ this.hasShutdown = false;
691
869
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
692
870
  this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
693
871
  this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
@@ -705,8 +883,45 @@ var TraceShipper = class {
705
883
  if (this.debug && this.localDebuggerUrl) {
706
884
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
707
885
  }
886
+ this.projectId = normalizeProjectId(opts.projectId, {
887
+ debug: this.debug,
888
+ prefix: this.prefix
889
+ });
708
890
  this.transformSpanHook = opts.transformSpan;
709
891
  this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
892
+ this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
893
+ }
894
+ /**
895
+ * Cap every string attribute value on the span. O(#attributes) length
896
+ * checks; only oversized values pay a slice. Runs AFTER the redaction
897
+ * pipeline so the default secret-scrub still sees parseable JSON in
898
+ * `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
899
+ * first could cut a JSON blob mid-way, fail the parse, and ship secrets
900
+ * in the surviving prefix).
901
+ *
902
+ * A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
903
+ * for span content, matching the Python SDK and the OTel SDK convention.
904
+ */
905
+ capSpanAttributes(span) {
906
+ var _a;
907
+ const maxChars = applyOtelSpanAttributeLimit(
908
+ resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
909
+ );
910
+ const attrs = span.attributes;
911
+ if (!attrs || attrs.length === 0) return span;
912
+ let nextAttrs;
913
+ for (let i = 0; i < attrs.length; i++) {
914
+ const attr = attrs[i];
915
+ const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
916
+ if (typeof value !== "string" || value.length <= maxChars) continue;
917
+ if (!nextAttrs) nextAttrs = attrs.slice();
918
+ nextAttrs[i] = {
919
+ key: attr.key,
920
+ value: { ...attr.value, stringValue: capText(value, maxChars) }
921
+ };
922
+ }
923
+ if (!nextAttrs) return span;
924
+ return { ...span, attributes: nextAttrs };
710
925
  }
711
926
  /**
712
927
  * Apply the user `transformSpan` hook (if any) followed by the default
@@ -740,7 +955,7 @@ var TraceShipper = class {
740
955
  if (!this.disableDefaultRedaction) {
741
956
  current = defaultTransformSpan(current);
742
957
  }
743
- return current;
958
+ return this.capSpanAttributes(current);
744
959
  }
745
960
  isDebugEnabled() {
746
961
  return this.debug;
@@ -748,6 +963,9 @@ var TraceShipper = class {
748
963
  authHeaders() {
749
964
  return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
750
965
  }
966
+ requestHeaders() {
967
+ return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
968
+ }
751
969
  startSpan(args) {
752
970
  var _a, _b;
753
971
  const ids = createSpanIds(args.parent);
@@ -861,6 +1079,16 @@ var TraceShipper = class {
861
1079
  while (this.queue.length > 0) {
862
1080
  const batch = this.queue.splice(0, this.maxBatchSize);
863
1081
  if (!this.writeKey) continue;
1082
+ const opts = this.requestOpts();
1083
+ if (!opts) {
1084
+ rateLimitedLog(
1085
+ `${this.prefix}.shutdown_deadline`,
1086
+ () => console.warn(
1087
+ `${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
1088
+ )
1089
+ );
1090
+ continue;
1091
+ }
864
1092
  const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
865
1093
  const url = `${this.baseUrl}traces`;
866
1094
  if (this.debug) {
@@ -869,11 +1097,7 @@ var TraceShipper = class {
869
1097
  endpoint: url
870
1098
  });
871
1099
  }
872
- const p = postJson(url, body, this.authHeaders(), {
873
- maxAttempts: 3,
874
- debug: this.debug,
875
- sdkName: this.sdkName
876
- });
1100
+ const p = postJson(url, body, this.requestHeaders(), opts);
877
1101
  this.inFlight.add(p);
878
1102
  try {
879
1103
  try {
@@ -881,21 +1105,61 @@ var TraceShipper = class {
881
1105
  if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
882
1106
  } catch (err) {
883
1107
  const msg = err instanceof Error ? err.message : String(err);
884
- console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`);
1108
+ rateLimitedLog(
1109
+ `${this.prefix}.send_spans_failed`,
1110
+ () => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
1111
+ );
885
1112
  }
886
1113
  } finally {
887
1114
  this.inFlight.delete(p);
888
1115
  }
889
1116
  }
890
1117
  }
1118
+ /** See EventShipper.requestOpts — same shutdown-budget semantics. */
1119
+ requestOpts() {
1120
+ if (this.shutdownDeadlineAt !== void 0) {
1121
+ const remainingMs = this.shutdownDeadlineAt - Date.now();
1122
+ if (remainingMs <= 0) return null;
1123
+ return {
1124
+ maxAttempts: 1,
1125
+ debug: this.debug,
1126
+ sdkName: this.sdkName,
1127
+ timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
1128
+ };
1129
+ }
1130
+ if (this.hasShutdown) {
1131
+ return {
1132
+ maxAttempts: 1,
1133
+ debug: this.debug,
1134
+ sdkName: this.sdkName,
1135
+ timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
1136
+ };
1137
+ }
1138
+ return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
1139
+ }
891
1140
  async shutdown() {
892
- if (this.timer) {
893
- clearTimeout(this.timer);
894
- this.timer = void 0;
1141
+ this.hasShutdown = true;
1142
+ this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
1143
+ try {
1144
+ if (this.timer) {
1145
+ clearTimeout(this.timer);
1146
+ this.timer = void 0;
1147
+ }
1148
+ const drain = async () => {
1149
+ await this.flush();
1150
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1151
+ })));
1152
+ };
1153
+ const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
1154
+ if (!settled) {
1155
+ rateLimitedLog(
1156
+ `${this.prefix}.shutdown_deadline`,
1157
+ () => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
1158
+ );
1159
+ }
1160
+ } finally {
1161
+ this.shutdownDeadlineAt = void 0;
895
1162
  }
896
- await this.flush();
897
- await Promise.all([...this.inFlight].map((p) => p.catch(() => {
898
- })));
899
1163
  }
900
1164
  };
901
1165
  var NOOP_SPAN = {
@@ -1017,6 +1281,133 @@ async function* asyncGeneratorWithCurrent(span, gen) {
1017
1281
  }
1018
1282
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
1019
1283
 
1284
+ // src/internal/truncation.ts
1285
+ var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
1286
+ var DEFAULT_MAX_TEXT_FIELD_CHARS2 = 1e6;
1287
+ var MAX_DEPTH = 12;
1288
+ var configuredMaxTextFieldChars;
1289
+ function setMaxTextFieldChars(limit) {
1290
+ if (limit === void 0) return;
1291
+ if (typeof limit === "number" && Number.isFinite(limit) && limit > 0) {
1292
+ configuredMaxTextFieldChars = Math.floor(limit);
1293
+ } else {
1294
+ console.warn(`[raindrop-ai] maxTextFieldChars=${String(limit)} ignored; must be > 0`);
1295
+ }
1296
+ }
1297
+ function otelEnvLimit() {
1298
+ var _a;
1299
+ if (typeof process === "undefined") return void 0;
1300
+ const raw = (_a = process.env) == null ? void 0 : _a.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
1301
+ if (!raw) return void 0;
1302
+ const parsed = Number.parseInt(raw, 10);
1303
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
1304
+ }
1305
+ function effectiveMaxTextFieldChars() {
1306
+ const base = configuredMaxTextFieldChars != null ? configuredMaxTextFieldChars : DEFAULT_MAX_TEXT_FIELD_CHARS2;
1307
+ const env = otelEnvLimit();
1308
+ return env !== void 0 && env < base ? env : base;
1309
+ }
1310
+ function truncateToLimit2(text, limit) {
1311
+ if (limit > TRUNCATION_MARKER2.length) {
1312
+ return text.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
1313
+ }
1314
+ return text.slice(0, limit);
1315
+ }
1316
+ function capText2(value, limit) {
1317
+ if (typeof value !== "string") return value;
1318
+ const max = limit != null ? limit : effectiveMaxTextFieldChars();
1319
+ if (value.length <= max) return value;
1320
+ return truncateToLimit2(value, max);
1321
+ }
1322
+ function boundedClone2(value, budget, depth) {
1323
+ var _a, _b;
1324
+ if (budget.remaining <= 0) return TRUNCATION_MARKER2;
1325
+ if (typeof value === "string") {
1326
+ if (value.length > budget.remaining) {
1327
+ const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
1328
+ budget.remaining = 0;
1329
+ return taken;
1330
+ }
1331
+ budget.remaining -= Math.max(value.length, 1);
1332
+ return value;
1333
+ }
1334
+ if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1335
+ budget.remaining -= 8;
1336
+ return value;
1337
+ }
1338
+ if (typeof value !== "object") {
1339
+ budget.remaining -= 1;
1340
+ return value;
1341
+ }
1342
+ if (value instanceof Uint8Array) {
1343
+ const maxBytes = Math.max(0, Math.ceil(budget.remaining * 3 / 4));
1344
+ if (value.byteLength > maxBytes) {
1345
+ const taken = base64Encode(value.subarray(0, maxBytes)) + TRUNCATION_MARKER2;
1346
+ budget.remaining = 0;
1347
+ return taken;
1348
+ }
1349
+ const encoded = base64Encode(value);
1350
+ budget.remaining -= Math.max(encoded.length, 1);
1351
+ return encoded;
1352
+ }
1353
+ if (depth >= MAX_DEPTH) {
1354
+ budget.remaining -= 16;
1355
+ const name = (_b = (_a = value.constructor) == null ? void 0 : _a.name) != null ? _b : "object";
1356
+ return `<max depth: ${name}>`;
1357
+ }
1358
+ const toJSON = value.toJSON;
1359
+ if (typeof toJSON === "function") {
1360
+ budget.remaining -= 2;
1361
+ return boundedClone2(toJSON.call(value), budget, depth + 1);
1362
+ }
1363
+ if (Array.isArray(value)) {
1364
+ budget.remaining -= 2;
1365
+ const out2 = [];
1366
+ for (const item of value) {
1367
+ if (budget.remaining <= 0) {
1368
+ out2.push(TRUNCATION_MARKER2);
1369
+ break;
1370
+ }
1371
+ out2.push(boundedClone2(item, budget, depth + 1));
1372
+ }
1373
+ return out2;
1374
+ }
1375
+ budget.remaining -= 2;
1376
+ const out = {};
1377
+ for (const key in value) {
1378
+ if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
1379
+ if (budget.remaining <= 0) {
1380
+ out["..."] = TRUNCATION_MARKER2;
1381
+ break;
1382
+ }
1383
+ let cappedKey = key;
1384
+ if (key.length > budget.remaining) {
1385
+ cappedKey = key.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
1386
+ budget.remaining = 0;
1387
+ out[cappedKey] = TRUNCATION_MARKER2;
1388
+ break;
1389
+ }
1390
+ budget.remaining -= Math.max(key.length, 1);
1391
+ out[cappedKey] = boundedClone2(value[key], budget, depth + 1);
1392
+ }
1393
+ return out;
1394
+ }
1395
+ function boundedStringify(value, limit) {
1396
+ const max = limit != null ? limit : effectiveMaxTextFieldChars();
1397
+ try {
1398
+ if (typeof value === "string") {
1399
+ const text2 = JSON.stringify(capText2(value, max));
1400
+ return text2.length <= max ? text2 : truncateToLimit2(text2, max);
1401
+ }
1402
+ const pruned = boundedClone2(value, { remaining: max + TRUNCATION_MARKER2.length + 256 }, 0);
1403
+ const text = JSON.stringify(pruned);
1404
+ if (text === void 0) return void 0;
1405
+ return text.length <= max ? text : truncateToLimit2(text, max);
1406
+ } catch (e) {
1407
+ return void 0;
1408
+ }
1409
+ }
1410
+
1020
1411
  // src/internal/wrap/helpers.ts
1021
1412
  function isRecord(value) {
1022
1413
  return typeof value === "object" && value !== null;
@@ -1041,21 +1432,10 @@ function isModuleNamespace(obj) {
1041
1432
  }
1042
1433
  }
1043
1434
  function safeJson(value) {
1044
- try {
1045
- return JSON.stringify(value);
1046
- } catch (e) {
1047
- return void 0;
1048
- }
1435
+ return boundedStringify(value);
1049
1436
  }
1050
1437
  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
- }
1438
+ return boundedStringify(value);
1059
1439
  }
1060
1440
  function extractModelInfo(model) {
1061
1441
  if (typeof model === "string") {
@@ -1951,7 +2331,10 @@ var RaindropTelemetryIntegration = class {
1951
2331
  inputAttrs.push(
1952
2332
  attrStringArray(
1953
2333
  "ai.prompt.tools",
1954
- event.stepTools.map((t) => JSON.stringify(t))
2334
+ event.stepTools.map((t) => {
2335
+ var _a;
2336
+ return (_a = safeJsonWithUint8(t)) != null ? _a : "";
2337
+ })
1955
2338
  )
1956
2339
  );
1957
2340
  }
@@ -1959,7 +2342,7 @@ var RaindropTelemetryIntegration = class {
1959
2342
  inputAttrs.push(
1960
2343
  attrString(
1961
2344
  "ai.prompt.toolChoice",
1962
- JSON.stringify(event.stepToolChoice)
2345
+ safeJsonWithUint8(event.stepToolChoice)
1963
2346
  )
1964
2347
  );
1965
2348
  }
@@ -2015,7 +2398,9 @@ var RaindropTelemetryIntegration = class {
2015
2398
  if (chunk.type === "text-delta") {
2016
2399
  const delta = (_c = chunk.textDelta) != null ? _c : chunk.delta;
2017
2400
  if (typeof delta === "string") {
2018
- state.accumulatedText += delta;
2401
+ if (state.accumulatedText.length <= effectiveMaxTextFieldChars()) {
2402
+ state.accumulatedText += delta;
2403
+ }
2019
2404
  this.emitLive(state, "text_delta", delta);
2020
2405
  }
2021
2406
  } else if (chunk.type === "reasoning" || chunk.type === "reasoning-delta") {
@@ -2034,7 +2419,7 @@ var RaindropTelemetryIntegration = class {
2034
2419
  if (state.recordOutputs) {
2035
2420
  outputAttrs.push(
2036
2421
  attrString("ai.response.finishReason", event.finishReason),
2037
- attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
2422
+ attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
2038
2423
  attrString("ai.response.id", (_b = event.response) == null ? void 0 : _b.id),
2039
2424
  attrString("ai.response.model", (_c = event.response) == null ? void 0 : _c.modelId),
2040
2425
  attrString(
@@ -2050,7 +2435,7 @@ var RaindropTelemetryIntegration = class {
2050
2435
  outputAttrs.push(
2051
2436
  attrString(
2052
2437
  "ai.response.toolCalls",
2053
- JSON.stringify(
2438
+ safeJsonWithUint8(
2054
2439
  event.toolCalls.map((tc) => ({
2055
2440
  toolCallId: tc.toolCallId,
2056
2441
  toolName: tc.toolName,
@@ -2063,7 +2448,7 @@ var RaindropTelemetryIntegration = class {
2063
2448
  if (((_g = event.reasoning) == null ? void 0 : _g.length) > 0) {
2064
2449
  const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
2065
2450
  if (reasoningText) {
2066
- outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
2451
+ outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
2067
2452
  }
2068
2453
  }
2069
2454
  }
@@ -2485,7 +2870,7 @@ var RaindropTelemetryIntegration = class {
2485
2870
  if (state.recordOutputs) {
2486
2871
  outputAttrs.push(
2487
2872
  attrString("ai.response.finishReason", event.finishReason),
2488
- attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
2873
+ attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
2489
2874
  attrString(
2490
2875
  "ai.response.providerMetadata",
2491
2876
  event.providerMetadata ? safeJsonWithUint8(event.providerMetadata) : void 0
@@ -2495,7 +2880,7 @@ var RaindropTelemetryIntegration = class {
2495
2880
  outputAttrs.push(
2496
2881
  attrString(
2497
2882
  "ai.response.toolCalls",
2498
- JSON.stringify(
2883
+ safeJsonWithUint8(
2499
2884
  event.toolCalls.map((tc) => ({
2500
2885
  toolCallId: tc.toolCallId,
2501
2886
  toolName: tc.toolName,
@@ -2508,7 +2893,7 @@ var RaindropTelemetryIntegration = class {
2508
2893
  if (((_c = event.reasoning) == null ? void 0 : _c.length) > 0) {
2509
2894
  const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
2510
2895
  if (reasoningText) {
2511
- outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
2896
+ outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
2512
2897
  }
2513
2898
  }
2514
2899
  }
@@ -2546,7 +2931,8 @@ var RaindropTelemetryIntegration = class {
2546
2931
  const userId = (_b = callMeta.userId) != null ? _b : (_a = this.defaultContext) == null ? void 0 : _a.userId;
2547
2932
  if (!userId) return;
2548
2933
  const eventName = (_e = (_d = callMeta.eventName) != null ? _d : (_c = this.defaultContext) == null ? void 0 : _c.eventName) != null ? _e : state.operationId;
2549
- const input = state.inputText;
2934
+ const cappedOutput = capText2(output);
2935
+ const input = capText2(state.inputText);
2550
2936
  const properties = {
2551
2937
  ...(_f = this.defaultContext) == null ? void 0 : _f.properties,
2552
2938
  ...callMeta.properties
@@ -2557,7 +2943,7 @@ var RaindropTelemetryIntegration = class {
2557
2943
  userId,
2558
2944
  convoId,
2559
2945
  input,
2560
- output,
2946
+ output: cappedOutput,
2561
2947
  model,
2562
2948
  properties: Object.keys(properties).length > 0 ? properties : void 0,
2563
2949
  isPending: false
@@ -3017,13 +3403,14 @@ function shouldKeepEventPending(params) {
3017
3403
  return params.finishReason === "tool-calls" || params.finishReason === "tool_calls";
3018
3404
  }
3019
3405
  function normalizePromptAttr(arg) {
3406
+ var _a, _b;
3020
3407
  if (!isRecord(arg)) return arg;
3021
3408
  const system = arg["system"];
3022
3409
  const prompt = arg["prompt"];
3023
3410
  const messages = arg["messages"];
3024
3411
  if (Array.isArray(messages)) {
3025
3412
  if (system) {
3026
- const sysContent = typeof system === "string" ? system : JSON.stringify(system);
3413
+ const sysContent = typeof system === "string" ? system : (_a = safeJsonWithUint8(system)) != null ? _a : String(system);
3027
3414
  return [{ role: "system", content: sysContent }, ...messages];
3028
3415
  }
3029
3416
  return messages;
@@ -3031,7 +3418,10 @@ function normalizePromptAttr(arg) {
3031
3418
  if (typeof prompt === "string") {
3032
3419
  const msgs = [];
3033
3420
  if (system) {
3034
- msgs.push({ role: "system", content: typeof system === "string" ? system : JSON.stringify(system) });
3421
+ msgs.push({
3422
+ role: "system",
3423
+ content: typeof system === "string" ? system : (_b = safeJsonWithUint8(system)) != null ? _b : String(system)
3424
+ });
3035
3425
  }
3036
3426
  msgs.push({ role: "user", content: prompt });
3037
3427
  return msgs;
@@ -3200,7 +3590,8 @@ function createFinalize(params) {
3200
3590
  };
3201
3591
  const built = await maybeBuildEvent(options.buildEvent, allMessages);
3202
3592
  const patch = mergeBuildEventPatch(defaultPatch, built);
3203
- const output = patch.output;
3593
+ const output = capText2(patch.output);
3594
+ const input = capText2(patch.input);
3204
3595
  const finalModel = (_c = patch.model) != null ? _c : model;
3205
3596
  if (setup.rootSpan) {
3206
3597
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3244,7 +3635,7 @@ function createFinalize(params) {
3244
3635
  eventName: patch.eventName,
3245
3636
  userId: setup.ctx.userId,
3246
3637
  convoId: setup.ctx.convoId,
3247
- input: patch.input,
3638
+ input,
3248
3639
  output,
3249
3640
  model: finalModel,
3250
3641
  properties: patch.properties,
@@ -3759,7 +4150,8 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
3759
4150
  };
3760
4151
  const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
3761
4152
  const patch = mergeBuildEventPatch(defaultPatch, built);
3762
- const output = patch.output;
4153
+ const output = capText2(patch.output);
4154
+ const input = capText2(patch.input);
3763
4155
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
3764
4156
  if (rootSpan) {
3765
4157
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -3817,7 +4209,7 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
3817
4209
  eventName: patch.eventName,
3818
4210
  userId: ctx.userId,
3819
4211
  convoId: ctx.convoId,
3820
- input: patch.input,
4212
+ input,
3821
4213
  output,
3822
4214
  model: finalModel,
3823
4215
  properties: patch.properties,
@@ -3966,7 +4358,8 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
3966
4358
  };
3967
4359
  const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
3968
4360
  const patch = mergeBuildEventPatch(defaultPatch, built);
3969
- const output = patch.output;
4361
+ const output = capText2(patch.output);
4362
+ const input = capText2(patch.input);
3970
4363
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
3971
4364
  if (rootSpan) {
3972
4365
  const spanEndTimeUnixNano = nowUnixNanoString();
@@ -4024,7 +4417,7 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
4024
4417
  eventName: patch.eventName,
4025
4418
  userId: ctx.userId,
4026
4419
  convoId: ctx.convoId,
4027
- input: patch.input,
4420
+ input,
4028
4421
  output,
4029
4422
  model: finalModel,
4030
4423
  properties: patch.properties,
@@ -4364,7 +4757,7 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4364
4757
  attributes: [
4365
4758
  ...((_a = ctx.telemetry) == null ? void 0 : _a.recordOutputs) === false ? [] : [
4366
4759
  attrString("ai.response.finishReason", finishReason),
4367
- attrString("ai.response.text", activeText.length ? activeText : void 0),
4760
+ attrString("ai.response.text", activeText.length ? capText2(activeText) : void 0),
4368
4761
  attrString(
4369
4762
  "ai.response.toolCalls",
4370
4763
  safeJsonWithUint8(toolCallsLocal.length ? toolCallsLocal : void 0)
@@ -4417,7 +4810,9 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
4417
4810
  } else if (typeof value["textDelta"] === "string") {
4418
4811
  textDelta = value["textDelta"];
4419
4812
  }
4420
- if (typeof textDelta === "string") activeText += textDelta;
4813
+ if (typeof textDelta === "string" && activeText.length <= effectiveMaxTextFieldChars()) {
4814
+ activeText += textDelta;
4815
+ }
4421
4816
  }
4422
4817
  if (type === "finish") finishReason = extractFinishReason(value);
4423
4818
  if (type === "tool-call") toolCallsLocal.push(value);
@@ -4652,7 +5047,7 @@ function extractNestedTokens(usage, key) {
4652
5047
  // package.json
4653
5048
  var package_default = {
4654
5049
  name: "@raindrop-ai/ai-sdk",
4655
- version: "0.0.33"};
5050
+ version: "0.0.35"};
4656
5051
 
4657
5052
  // src/internal/version.ts
4658
5053
  var libraryName = package_default.name;
@@ -4725,8 +5120,8 @@ function eventMetadataFromChatRequest(options) {
4725
5120
  });
4726
5121
  }
4727
5122
  function stringify(v) {
4728
- if (typeof v === "string") return v;
4729
- return JSON.stringify(v);
5123
+ if (typeof v === "string") return capText2(v);
5124
+ return boundedStringify(v);
4730
5125
  }
4731
5126
  function userAttrsToOtlp(attrs) {
4732
5127
  if (!attrs) return [];
@@ -4744,6 +5139,7 @@ function createRaindropAISDK(opts) {
4744
5139
  const eventsRequested = ((_a = opts.events) == null ? void 0 : _a.enabled) !== false;
4745
5140
  const tracesRequested = ((_b = opts.traces) == null ? void 0 : _b.enabled) !== false;
4746
5141
  const envDebug = envDebugEnabled();
5142
+ setMaxTextFieldChars(opts.maxTextFieldChars);
4747
5143
  const localWorkshopInput = opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl;
4748
5144
  const resolvedLocalDebuggerUrl = resolveLocalDebuggerBaseUrl(localWorkshopInput);
4749
5145
  const localDebuggerUrl = localWorkshopInput === null ? null : resolvedLocalDebuggerUrl != null ? resolvedLocalDebuggerUrl : void 0;
@@ -4761,6 +5157,7 @@ function createRaindropAISDK(opts) {
4761
5157
  enabled: eventsEnabled,
4762
5158
  debug: ((_c = opts.events) == null ? void 0 : _c.debug) === true || envDebug,
4763
5159
  partialFlushMs: (_d = opts.events) == null ? void 0 : _d.partialFlushMs,
5160
+ projectId: opts.projectId,
4764
5161
  localDebuggerUrl
4765
5162
  });
4766
5163
  const traceShipper = new TraceShipper2({
@@ -4772,6 +5169,7 @@ function createRaindropAISDK(opts) {
4772
5169
  flushIntervalMs: (_g = opts.traces) == null ? void 0 : _g.flushIntervalMs,
4773
5170
  maxBatchSize: (_h = opts.traces) == null ? void 0 : _h.maxBatchSize,
4774
5171
  maxQueueSize: (_i = opts.traces) == null ? void 0 : _i.maxQueueSize,
5172
+ projectId: opts.projectId,
4775
5173
  localDebuggerUrl,
4776
5174
  transformSpan: (_j = opts.traces) == null ? void 0 : _j.transformSpan,
4777
5175
  disableDefaultRedaction: (_k = opts.traces) == null ? void 0 : _k.disableDefaultRedaction
@@ -4930,4 +5328,4 @@ function raindrop(options = {}) {
4930
5328
  return client.createTelemetryIntegration({ context, subagentWrapping });
4931
5329
  }
4932
5330
 
4933
- export { DEFAULT_REDACT_ATTRIBUTE_KEYS, DEFAULT_SECRET_KEY_NAMES, REDACTED_PLACEHOLDER, RaindropTelemetryIntegration, _resetParentToolContextStorage, _resetRaindropCallMetadataStorage, _resetWarnedMissingUserId, clearParentToolContext, createRaindropAISDK, currentSpan, defaultTransformSpan, enterParentToolContext, eventMetadata, eventMetadataFromChatRequest, getContextManager, getCurrentParentToolContext, getCurrentRaindropCallMetadata, raindrop, readRaindropCallMetadataFromArgs, redactJsonAttributeValue, redactSecretsInObject, runWithParentToolContext, runWithRaindropCallMetadata, withCurrent };
5331
+ 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 };