@raindrop-ai/deep-agents 0.0.1 → 0.0.3
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.
- package/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/index.d.mts +179 -2
- package/dist/index.d.ts +179 -2
- package/dist/index.js +621 -96
- package/dist/index.mjs +621 -96
- package/package.json +10 -10
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/callback-handler.ts
|
|
2
2
|
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
3
3
|
|
|
4
|
-
// ../core/dist/chunk-
|
|
4
|
+
// ../core/dist/chunk-SK6EJEO7.js
|
|
5
5
|
function getCrypto() {
|
|
6
6
|
const c = globalThis.crypto;
|
|
7
7
|
return c;
|
|
@@ -56,6 +56,8 @@ function base64Encode(bytes) {
|
|
|
56
56
|
}
|
|
57
57
|
return out;
|
|
58
58
|
}
|
|
59
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
60
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
59
61
|
function wait(ms) {
|
|
60
62
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
61
63
|
}
|
|
@@ -63,6 +65,46 @@ function formatEndpoint(endpoint) {
|
|
|
63
65
|
if (!endpoint) return void 0;
|
|
64
66
|
return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
|
65
67
|
}
|
|
68
|
+
function redactUrlForLog(url) {
|
|
69
|
+
try {
|
|
70
|
+
const parsed = new URL(url);
|
|
71
|
+
parsed.username = "";
|
|
72
|
+
parsed.password = "";
|
|
73
|
+
parsed.search = "";
|
|
74
|
+
parsed.hash = "";
|
|
75
|
+
return parsed.toString();
|
|
76
|
+
} catch (e) {
|
|
77
|
+
return "<unparseable-url>";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
|
|
81
|
+
var rateLimitedLogLast = /* @__PURE__ */ new Map();
|
|
82
|
+
function rateLimitedLog(key, log) {
|
|
83
|
+
const now = Date.now();
|
|
84
|
+
const last = rateLimitedLogLast.get(key);
|
|
85
|
+
if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
rateLimitedLogLast.set(key, now);
|
|
89
|
+
log();
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
async function raceWithTimeout(promise, timeoutMs) {
|
|
93
|
+
let timer;
|
|
94
|
+
const settledInTime = await Promise.race([
|
|
95
|
+
promise.then(
|
|
96
|
+
() => true,
|
|
97
|
+
() => true
|
|
98
|
+
),
|
|
99
|
+
new Promise((resolve) => {
|
|
100
|
+
var _a;
|
|
101
|
+
timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
|
|
102
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
|
103
|
+
})
|
|
104
|
+
]);
|
|
105
|
+
if (timer) clearTimeout(timer);
|
|
106
|
+
return settledInTime;
|
|
107
|
+
}
|
|
66
108
|
function parseRetryAfter(headers) {
|
|
67
109
|
var _a;
|
|
68
110
|
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
@@ -79,12 +121,12 @@ function parseRetryAfter(headers) {
|
|
|
79
121
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
80
122
|
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
81
123
|
const v = previousError.retryAfterMs;
|
|
82
|
-
if (typeof v === "number") return Math.max(0, v);
|
|
124
|
+
if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
|
|
83
125
|
}
|
|
84
126
|
if (attemptNumber <= 1) return 0;
|
|
85
127
|
const base = 500;
|
|
86
128
|
const factor = Math.pow(2, attemptNumber - 2);
|
|
87
|
-
return base * factor;
|
|
129
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
88
130
|
}
|
|
89
131
|
async function withRetry(operation, opName, opts) {
|
|
90
132
|
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
@@ -119,7 +161,9 @@ async function withRetry(operation, opName, opts) {
|
|
|
119
161
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
120
162
|
}
|
|
121
163
|
async function postJson(url, body, headers, opts) {
|
|
122
|
-
|
|
164
|
+
var _a;
|
|
165
|
+
const opName = `POST ${redactUrlForLog(url)}`;
|
|
166
|
+
const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
123
167
|
await withRetry(
|
|
124
168
|
async () => {
|
|
125
169
|
const resp = await fetch(url, {
|
|
@@ -128,7 +172,8 @@ async function postJson(url, body, headers, opts) {
|
|
|
128
172
|
"Content-Type": "application/json",
|
|
129
173
|
...headers
|
|
130
174
|
},
|
|
131
|
-
body: JSON.stringify(body)
|
|
175
|
+
body: JSON.stringify(body),
|
|
176
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
132
177
|
});
|
|
133
178
|
if (!resp.ok) {
|
|
134
179
|
const text = await resp.text().catch(() => "");
|
|
@@ -145,6 +190,27 @@ async function postJson(url, body, headers, opts) {
|
|
|
145
190
|
opts
|
|
146
191
|
);
|
|
147
192
|
}
|
|
193
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
|
|
194
|
+
var TRUNCATION_MARKER = "...[truncated by raindrop]";
|
|
195
|
+
var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
|
|
196
|
+
function resolveMaxTextFieldChars(value) {
|
|
197
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
198
|
+
return Math.floor(value);
|
|
199
|
+
}
|
|
200
|
+
return currentDefaultMaxTextFieldChars;
|
|
201
|
+
}
|
|
202
|
+
function truncateToLimit(text, limit) {
|
|
203
|
+
if (limit > TRUNCATION_MARKER.length) {
|
|
204
|
+
return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
|
|
205
|
+
}
|
|
206
|
+
return text.slice(0, Math.max(0, limit));
|
|
207
|
+
}
|
|
208
|
+
function capText(value, limit) {
|
|
209
|
+
if (typeof value !== "string") return value;
|
|
210
|
+
const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
|
|
211
|
+
if (value.length <= max) return value;
|
|
212
|
+
return truncateToLimit(value, max);
|
|
213
|
+
}
|
|
148
214
|
var SpanStatusCode = {
|
|
149
215
|
UNSET: 0,
|
|
150
216
|
OK: 1,
|
|
@@ -202,6 +268,112 @@ function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", se
|
|
|
202
268
|
]
|
|
203
269
|
};
|
|
204
270
|
}
|
|
271
|
+
var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
|
|
272
|
+
var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
|
|
273
|
+
var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
|
|
274
|
+
function readEnvVar(name) {
|
|
275
|
+
var _a;
|
|
276
|
+
try {
|
|
277
|
+
const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
|
|
278
|
+
if (env && typeof env[name] === "string" && env[name].length > 0) {
|
|
279
|
+
return env[name];
|
|
280
|
+
}
|
|
281
|
+
} catch (e) {
|
|
282
|
+
}
|
|
283
|
+
return void 0;
|
|
284
|
+
}
|
|
285
|
+
function readWorkshopEnv() {
|
|
286
|
+
const raw = readEnvVar(WORKSHOP_ENV_VAR);
|
|
287
|
+
if (raw === void 0) return void 0;
|
|
288
|
+
const trimmed = raw.trim();
|
|
289
|
+
if (trimmed.length === 0) return void 0;
|
|
290
|
+
if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
|
|
291
|
+
if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
|
|
292
|
+
if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
|
|
293
|
+
return void 0;
|
|
294
|
+
}
|
|
295
|
+
function isLocalDevHost(hostname) {
|
|
296
|
+
if (!hostname) return false;
|
|
297
|
+
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
if (hostname.endsWith(".localhost")) return true;
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
function readRuntimeHostname() {
|
|
304
|
+
try {
|
|
305
|
+
const loc = globalThis == null ? void 0 : globalThis.location;
|
|
306
|
+
if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
|
|
307
|
+
return loc.hostname;
|
|
308
|
+
}
|
|
309
|
+
} catch (e) {
|
|
310
|
+
}
|
|
311
|
+
return void 0;
|
|
312
|
+
}
|
|
313
|
+
function shouldAutoEnableLocalWorkshop() {
|
|
314
|
+
if (isLocalDevHost(readRuntimeHostname())) return true;
|
|
315
|
+
if (readEnvVar("NODE_ENV") === "development") return true;
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
function resolveLocalDebuggerBaseUrl(baseUrl) {
|
|
319
|
+
var _a, _b, _c;
|
|
320
|
+
if (baseUrl === null) return null;
|
|
321
|
+
if (typeof baseUrl === "string" && baseUrl.length > 0) {
|
|
322
|
+
return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
|
|
323
|
+
}
|
|
324
|
+
const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
|
|
325
|
+
if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
|
|
326
|
+
const workshopEnv = readWorkshopEnv();
|
|
327
|
+
if (workshopEnv === "disable") return null;
|
|
328
|
+
if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
|
|
329
|
+
if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
|
|
330
|
+
if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
function mirrorTraceExportToLocalDebugger(body, options = {}) {
|
|
334
|
+
var _a;
|
|
335
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
336
|
+
if (!baseUrl) return;
|
|
337
|
+
void postJson(`${baseUrl}traces`, body, {}, {
|
|
338
|
+
maxAttempts: 1,
|
|
339
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
340
|
+
sdkName: options.sdkName
|
|
341
|
+
}).catch(() => {
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function mirrorPartialEventToLocalDebugger(event, options = {}) {
|
|
345
|
+
var _a;
|
|
346
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
347
|
+
if (!baseUrl) return;
|
|
348
|
+
const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
|
|
349
|
+
void postJson(`${baseUrl}events/track_partial`, event, headers, {
|
|
350
|
+
maxAttempts: 1,
|
|
351
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
352
|
+
sdkName: options.sdkName
|
|
353
|
+
}).catch(() => {
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
|
|
357
|
+
var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
358
|
+
function isValidProjectIdSlug(value) {
|
|
359
|
+
return PROJECT_ID_SLUG_PATTERN.test(value);
|
|
360
|
+
}
|
|
361
|
+
function normalizeProjectId(raw, opts) {
|
|
362
|
+
if (typeof raw !== "string") return void 0;
|
|
363
|
+
const trimmed = raw.trim();
|
|
364
|
+
if (!trimmed) return void 0;
|
|
365
|
+
if (!isValidProjectIdSlug(trimmed) && opts.debug) {
|
|
366
|
+
console.warn(
|
|
367
|
+
`${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
return trimmed;
|
|
371
|
+
}
|
|
372
|
+
function projectIdHeaders(projectId) {
|
|
373
|
+
return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
|
|
374
|
+
}
|
|
375
|
+
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
376
|
+
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
205
377
|
function mergePatches(target, source) {
|
|
206
378
|
var _a, _b, _c, _d;
|
|
207
379
|
const out = { ...target, ...source };
|
|
@@ -219,7 +391,8 @@ var EventShipper = class {
|
|
|
219
391
|
this.sticky = /* @__PURE__ */ new Map();
|
|
220
392
|
this.timers = /* @__PURE__ */ new Map();
|
|
221
393
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
222
|
-
|
|
394
|
+
this.hasShutdown = false;
|
|
395
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
223
396
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
224
397
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
225
398
|
this.enabled = opts.enabled !== false;
|
|
@@ -228,11 +401,20 @@ var EventShipper = class {
|
|
|
228
401
|
this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
|
|
229
402
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
230
403
|
this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
|
|
404
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
405
|
+
this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
|
|
406
|
+
if (this.debug && this.localDebuggerUrl) {
|
|
407
|
+
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
408
|
+
}
|
|
409
|
+
this.projectId = normalizeProjectId(opts.projectId, {
|
|
410
|
+
debug: this.debug,
|
|
411
|
+
prefix: this.prefix
|
|
412
|
+
});
|
|
231
413
|
const isNode = typeof process !== "undefined" && typeof process.version === "string";
|
|
232
414
|
this.context = {
|
|
233
415
|
library: {
|
|
234
|
-
name: (
|
|
235
|
-
version: (
|
|
416
|
+
name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
|
|
417
|
+
version: (_h = opts.libraryVersion) != null ? _h : "0.0.0"
|
|
236
418
|
},
|
|
237
419
|
metadata: {
|
|
238
420
|
jsRuntime: isNode ? "node" : "web",
|
|
@@ -246,10 +428,55 @@ var EventShipper = class {
|
|
|
246
428
|
authHeaders() {
|
|
247
429
|
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
248
430
|
}
|
|
431
|
+
requestHeaders() {
|
|
432
|
+
return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Build the retry/timeout options for one POST, honoring the shutdown
|
|
436
|
+
* deadline. Returns `null` when the shutdown drain window is exhausted —
|
|
437
|
+
* the caller must drop the payload (with a rate-limited warning) instead
|
|
438
|
+
* of issuing a request that could outlive process exit.
|
|
439
|
+
*
|
|
440
|
+
* Checked fresh on EVERY send, so a shutdown that begins while the flush
|
|
441
|
+
* path is mid-drain takes effect immediately: no further retries, and the
|
|
442
|
+
* per-attempt timeout is clamped to the remaining window. After
|
|
443
|
+
* `shutdown()` returns (deadline cleared, `hasShutdown` still set),
|
|
444
|
+
* sends — late callers, or flush work the deadline abandoned mid-drain —
|
|
445
|
+
* run as a single short attempt rather than regaining the full retry
|
|
446
|
+
* schedule.
|
|
447
|
+
*/
|
|
448
|
+
requestOpts() {
|
|
449
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
450
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
451
|
+
if (remainingMs <= 0) return null;
|
|
452
|
+
return {
|
|
453
|
+
maxAttempts: 1,
|
|
454
|
+
debug: this.debug,
|
|
455
|
+
sdkName: this.sdkName,
|
|
456
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
if (this.hasShutdown) {
|
|
460
|
+
return {
|
|
461
|
+
maxAttempts: 1,
|
|
462
|
+
debug: this.debug,
|
|
463
|
+
sdkName: this.sdkName,
|
|
464
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
468
|
+
}
|
|
249
469
|
async patch(eventId, patch) {
|
|
250
470
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
251
471
|
if (!this.enabled) return;
|
|
252
472
|
if (!eventId || !eventId.trim()) return;
|
|
473
|
+
const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
|
|
474
|
+
if (typeof patch.input === "string" && patch.input.length > maxChars) {
|
|
475
|
+
patch = { ...patch, input: capText(patch.input, maxChars) };
|
|
476
|
+
}
|
|
477
|
+
if (typeof patch.output === "string" && patch.output.length > maxChars) {
|
|
478
|
+
patch = { ...patch, output: capText(patch.output, maxChars) };
|
|
479
|
+
}
|
|
253
480
|
if (this.debug) {
|
|
254
481
|
console.log(`${this.prefix} queue patch`, {
|
|
255
482
|
eventId,
|
|
@@ -296,9 +523,16 @@ var EventShipper = class {
|
|
|
296
523
|
})));
|
|
297
524
|
}
|
|
298
525
|
async shutdown() {
|
|
299
|
-
|
|
300
|
-
this.
|
|
301
|
-
|
|
526
|
+
this.hasShutdown = true;
|
|
527
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
528
|
+
try {
|
|
529
|
+
for (const t of this.timers.values()) clearTimeout(t);
|
|
530
|
+
this.timers.clear();
|
|
531
|
+
const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
|
|
532
|
+
if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
|
|
533
|
+
} finally {
|
|
534
|
+
this.shutdownDeadlineAt = void 0;
|
|
535
|
+
}
|
|
302
536
|
}
|
|
303
537
|
async trackSignal(signal) {
|
|
304
538
|
var _a, _b;
|
|
@@ -318,16 +552,21 @@ var EventShipper = class {
|
|
|
318
552
|
}
|
|
319
553
|
}
|
|
320
554
|
];
|
|
555
|
+
if (!this.writeKey) return;
|
|
321
556
|
const url = `${this.baseUrl}signals/track`;
|
|
557
|
+
const opts = this.requestOpts();
|
|
558
|
+
if (!opts) {
|
|
559
|
+
this.warnShutdownDrop("signal");
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
322
562
|
try {
|
|
323
|
-
await postJson(url, body, this.
|
|
324
|
-
maxAttempts: 3,
|
|
325
|
-
debug: this.debug,
|
|
326
|
-
sdkName: this.sdkName
|
|
327
|
-
});
|
|
563
|
+
await postJson(url, body, this.requestHeaders(), opts);
|
|
328
564
|
} catch (err) {
|
|
329
565
|
const msg = err instanceof Error ? err.message : String(err);
|
|
330
|
-
|
|
566
|
+
rateLimitedLog(
|
|
567
|
+
`${this.prefix}.send_signal_failed`,
|
|
568
|
+
() => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
|
|
569
|
+
);
|
|
331
570
|
}
|
|
332
571
|
}
|
|
333
572
|
async identify(users) {
|
|
@@ -348,19 +587,30 @@ var EventShipper = class {
|
|
|
348
587
|
traits: (_a = user.traits) != null ? _a : {}
|
|
349
588
|
};
|
|
350
589
|
});
|
|
590
|
+
if (!this.writeKey) return;
|
|
351
591
|
if (body.length === 0) return;
|
|
352
592
|
const url = `${this.baseUrl}users/identify`;
|
|
593
|
+
const opts = this.requestOpts();
|
|
594
|
+
if (!opts) {
|
|
595
|
+
this.warnShutdownDrop("identify");
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
353
598
|
try {
|
|
354
|
-
await postJson(url, body, this.
|
|
355
|
-
maxAttempts: 3,
|
|
356
|
-
debug: this.debug,
|
|
357
|
-
sdkName: this.sdkName
|
|
358
|
-
});
|
|
599
|
+
await postJson(url, body, this.requestHeaders(), opts);
|
|
359
600
|
} catch (err) {
|
|
360
601
|
const msg = err instanceof Error ? err.message : String(err);
|
|
361
|
-
|
|
602
|
+
rateLimitedLog(
|
|
603
|
+
`${this.prefix}.send_identify_failed`,
|
|
604
|
+
() => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
|
|
605
|
+
);
|
|
362
606
|
}
|
|
363
607
|
}
|
|
608
|
+
warnShutdownDrop(what) {
|
|
609
|
+
rateLimitedLog(
|
|
610
|
+
`${this.prefix}.shutdown_deadline`,
|
|
611
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
|
|
612
|
+
);
|
|
613
|
+
}
|
|
364
614
|
async flushOne(eventId) {
|
|
365
615
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
366
616
|
if (!this.enabled) return;
|
|
@@ -424,11 +674,25 @@ var EventShipper = class {
|
|
|
424
674
|
endpoint: url
|
|
425
675
|
});
|
|
426
676
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
677
|
+
if (this.localDebuggerUrl) {
|
|
678
|
+
mirrorPartialEventToLocalDebugger(payload, {
|
|
679
|
+
baseUrl: this.localDebuggerUrl,
|
|
680
|
+
writeKey: this.writeKey,
|
|
681
|
+
debug: this.debug,
|
|
682
|
+
sdkName: this.sdkName
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
if (!this.writeKey) {
|
|
686
|
+
if (!isPending) this.sticky.delete(eventId);
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
const opts = this.requestOpts();
|
|
690
|
+
if (!opts) {
|
|
691
|
+
this.warnShutdownDrop(`track_partial ${eventId}`);
|
|
692
|
+
if (!isPending) this.sticky.delete(eventId);
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
const p = postJson(url, payload, this.requestHeaders(), opts);
|
|
432
696
|
this.inFlight.add(p);
|
|
433
697
|
try {
|
|
434
698
|
try {
|
|
@@ -438,7 +702,10 @@ var EventShipper = class {
|
|
|
438
702
|
}
|
|
439
703
|
} catch (err) {
|
|
440
704
|
const msg = err instanceof Error ? err.message : String(err);
|
|
441
|
-
|
|
705
|
+
rateLimitedLog(
|
|
706
|
+
`${this.prefix}.send_track_partial_failed`,
|
|
707
|
+
() => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
|
|
708
|
+
);
|
|
442
709
|
}
|
|
443
710
|
} finally {
|
|
444
711
|
this.inFlight.delete(p);
|
|
@@ -448,28 +715,114 @@ var EventShipper = class {
|
|
|
448
715
|
}
|
|
449
716
|
}
|
|
450
717
|
};
|
|
451
|
-
var
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
718
|
+
var DEFAULT_SECRET_KEY_NAMES = [
|
|
719
|
+
"apikey",
|
|
720
|
+
"apisecret",
|
|
721
|
+
"apitoken",
|
|
722
|
+
"secretaccesskey",
|
|
723
|
+
"sessiontoken",
|
|
724
|
+
"privatekey",
|
|
725
|
+
"privatekeyid",
|
|
726
|
+
"clientsecret",
|
|
727
|
+
"accesstoken",
|
|
728
|
+
"refreshtoken",
|
|
729
|
+
"oauthtoken",
|
|
730
|
+
"bearertoken",
|
|
731
|
+
"authorization",
|
|
732
|
+
"password",
|
|
733
|
+
"passphrase"
|
|
734
|
+
];
|
|
735
|
+
var REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
736
|
+
function normalizeKeyName(name) {
|
|
737
|
+
return name.toLowerCase().replace(/[-_.]/g, "");
|
|
456
738
|
}
|
|
457
|
-
function
|
|
458
|
-
var _a;
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
739
|
+
function redactSecretsInObject(value, options) {
|
|
740
|
+
var _a, _b;
|
|
741
|
+
const normalizedSecretSet = buildSecretSet((_a = options == null ? void 0 : options.secretKeyNames) != null ? _a : DEFAULT_SECRET_KEY_NAMES);
|
|
742
|
+
const placeholder = (_b = options == null ? void 0 : options.placeholder) != null ? _b : REDACTED_PLACEHOLDER;
|
|
743
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
744
|
+
const walk = (node) => {
|
|
745
|
+
if (node === null || typeof node !== "object") return node;
|
|
746
|
+
if (seen.has(node)) return "[CIRCULAR]";
|
|
747
|
+
seen.add(node);
|
|
748
|
+
if (Array.isArray(node)) {
|
|
749
|
+
return node.map((item) => walk(item));
|
|
750
|
+
}
|
|
751
|
+
const out = {};
|
|
752
|
+
for (const [k, v] of Object.entries(node)) {
|
|
753
|
+
if (normalizedSecretSet.has(normalizeKeyName(k))) {
|
|
754
|
+
out[k] = placeholder;
|
|
755
|
+
} else {
|
|
756
|
+
out[k] = walk(v);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return out;
|
|
760
|
+
};
|
|
761
|
+
return walk(value);
|
|
762
|
+
}
|
|
763
|
+
function buildSecretSet(names) {
|
|
764
|
+
const set = /* @__PURE__ */ new Set();
|
|
765
|
+
for (const name of names) set.add(normalizeKeyName(name));
|
|
766
|
+
return set;
|
|
767
|
+
}
|
|
768
|
+
var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
|
|
769
|
+
"ai.request.providerOptions",
|
|
770
|
+
"ai.response.providerMetadata"
|
|
771
|
+
];
|
|
772
|
+
function defaultTransformSpan(span) {
|
|
773
|
+
const attrs = span.attributes;
|
|
774
|
+
if (!attrs || attrs.length === 0) return span;
|
|
775
|
+
let nextAttrs;
|
|
776
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
777
|
+
const attr = attrs[i];
|
|
778
|
+
const redacted = redactJsonAttributeValue(attr.key, attr.value);
|
|
779
|
+
if (redacted === void 0) continue;
|
|
780
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
781
|
+
nextAttrs[i] = { key: attr.key, value: redacted };
|
|
782
|
+
}
|
|
783
|
+
if (!nextAttrs) return span;
|
|
784
|
+
return { ...span, attributes: nextAttrs };
|
|
785
|
+
}
|
|
786
|
+
var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
|
|
787
|
+
function redactJsonAttributeValue(key, value) {
|
|
788
|
+
if (!REDACT_JSON_ATTRIBUTE_KEYS.has(key)) return void 0;
|
|
789
|
+
const json = value.stringValue;
|
|
790
|
+
if (typeof json !== "string" || json.length === 0) return void 0;
|
|
791
|
+
let parsed;
|
|
792
|
+
try {
|
|
793
|
+
parsed = JSON.parse(json);
|
|
794
|
+
} catch (e) {
|
|
795
|
+
return void 0;
|
|
796
|
+
}
|
|
797
|
+
const scrubbed = redactSecretsInObject(parsed);
|
|
798
|
+
let scrubbedJson;
|
|
799
|
+
try {
|
|
800
|
+
scrubbedJson = JSON.stringify(scrubbed);
|
|
801
|
+
} catch (e) {
|
|
802
|
+
return void 0;
|
|
803
|
+
}
|
|
804
|
+
if (scrubbedJson === json) return void 0;
|
|
805
|
+
return { stringValue: scrubbedJson };
|
|
806
|
+
}
|
|
807
|
+
function applyOtelSpanAttributeLimit(limit) {
|
|
808
|
+
var _a, _b;
|
|
809
|
+
try {
|
|
810
|
+
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;
|
|
811
|
+
if (!raw) return limit;
|
|
812
|
+
const parsed = Number.parseInt(raw, 10);
|
|
813
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
814
|
+
return Math.min(limit, parsed);
|
|
815
|
+
}
|
|
816
|
+
} catch (e) {
|
|
817
|
+
}
|
|
818
|
+
return limit;
|
|
467
819
|
}
|
|
468
820
|
var TraceShipper = class {
|
|
469
821
|
constructor(opts) {
|
|
470
822
|
this.queue = [];
|
|
471
823
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
472
|
-
|
|
824
|
+
this.hasShutdown = false;
|
|
825
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
473
826
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
474
827
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
475
828
|
this.enabled = opts.enabled !== false;
|
|
@@ -482,13 +835,83 @@ var TraceShipper = class {
|
|
|
482
835
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
483
836
|
this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
|
|
484
837
|
this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
|
|
485
|
-
|
|
486
|
-
if (
|
|
487
|
-
this.
|
|
488
|
-
|
|
489
|
-
|
|
838
|
+
this.localDebuggerUrl = (_i = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _i : void 0;
|
|
839
|
+
if (this.debug && this.localDebuggerUrl) {
|
|
840
|
+
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
841
|
+
}
|
|
842
|
+
this.projectId = normalizeProjectId(opts.projectId, {
|
|
843
|
+
debug: this.debug,
|
|
844
|
+
prefix: this.prefix
|
|
845
|
+
});
|
|
846
|
+
this.transformSpanHook = opts.transformSpan;
|
|
847
|
+
this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
|
|
848
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Cap every string attribute value on the span. O(#attributes) length
|
|
852
|
+
* checks; only oversized values pay a slice. Runs AFTER the redaction
|
|
853
|
+
* pipeline so the default secret-scrub still sees parseable JSON in
|
|
854
|
+
* `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
|
|
855
|
+
* first could cut a JSON blob mid-way, fail the parse, and ship secrets
|
|
856
|
+
* in the surviving prefix).
|
|
857
|
+
*
|
|
858
|
+
* A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
|
|
859
|
+
* for span content, matching the Python SDK and the OTel SDK convention.
|
|
860
|
+
*/
|
|
861
|
+
capSpanAttributes(span) {
|
|
862
|
+
var _a;
|
|
863
|
+
const maxChars = applyOtelSpanAttributeLimit(
|
|
864
|
+
resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
|
|
865
|
+
);
|
|
866
|
+
const attrs = span.attributes;
|
|
867
|
+
if (!attrs || attrs.length === 0) return span;
|
|
868
|
+
let nextAttrs;
|
|
869
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
870
|
+
const attr = attrs[i];
|
|
871
|
+
const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
|
|
872
|
+
if (typeof value !== "string" || value.length <= maxChars) continue;
|
|
873
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
874
|
+
nextAttrs[i] = {
|
|
875
|
+
key: attr.key,
|
|
876
|
+
value: { ...attr.value, stringValue: capText(value, maxChars) }
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
if (!nextAttrs) return span;
|
|
880
|
+
return { ...span, attributes: nextAttrs };
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Apply the user `transformSpan` hook (if any) followed by the default
|
|
884
|
+
* redactor (unless disabled). Returns either the (possibly new) span to
|
|
885
|
+
* ship, or `null` to drop the span entirely.
|
|
886
|
+
*
|
|
887
|
+
* Ordering: user hook runs first so callers can rewrite the span freely
|
|
888
|
+
* (rename attrs, add new ones, scrub things the default doesn't know
|
|
889
|
+
* about). The default redactor then runs on whatever the user produced,
|
|
890
|
+
* acting as the always-on floor for documented BYOK secrets. If the user
|
|
891
|
+
* sets `disableDefaultRedaction: true`, the floor is skipped.
|
|
892
|
+
*
|
|
893
|
+
* Fail-closed: if the user hook throws, the span is dropped — a buggy
|
|
894
|
+
* hook can never accidentally ship raw, un-redacted spans.
|
|
895
|
+
*/
|
|
896
|
+
redactSpan(span) {
|
|
897
|
+
let current = span;
|
|
898
|
+
if (this.transformSpanHook) {
|
|
899
|
+
try {
|
|
900
|
+
const result = this.transformSpanHook(current);
|
|
901
|
+
if (result === null) return null;
|
|
902
|
+
if (result !== void 0) current = result;
|
|
903
|
+
} catch (err) {
|
|
904
|
+
if (this.debug) {
|
|
905
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
906
|
+
console.warn(`${this.prefix} transformSpan hook threw: ${msg}`);
|
|
907
|
+
}
|
|
908
|
+
return null;
|
|
490
909
|
}
|
|
491
910
|
}
|
|
911
|
+
if (!this.disableDefaultRedaction) {
|
|
912
|
+
current = defaultTransformSpan(current);
|
|
913
|
+
}
|
|
914
|
+
return this.capSpanAttributes(current);
|
|
492
915
|
}
|
|
493
916
|
isDebugEnabled() {
|
|
494
917
|
return this.debug;
|
|
@@ -496,6 +919,9 @@ var TraceShipper = class {
|
|
|
496
919
|
authHeaders() {
|
|
497
920
|
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
498
921
|
}
|
|
922
|
+
requestHeaders() {
|
|
923
|
+
return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
|
|
924
|
+
}
|
|
499
925
|
startSpan(args) {
|
|
500
926
|
var _a, _b;
|
|
501
927
|
const ids = createSpanIds(args.parent);
|
|
@@ -506,8 +932,8 @@ var TraceShipper = class {
|
|
|
506
932
|
];
|
|
507
933
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
508
934
|
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
509
|
-
|
|
510
|
-
|
|
935
|
+
this.mirrorToLocalDebugger(
|
|
936
|
+
buildOtlpSpan({
|
|
511
937
|
ids: span.ids,
|
|
512
938
|
name: span.name,
|
|
513
939
|
startTimeUnixNano: span.startTimeUnixNano,
|
|
@@ -515,16 +941,21 @@ var TraceShipper = class {
|
|
|
515
941
|
// placeholder — will be updated on endSpan
|
|
516
942
|
attributes: span.attributes,
|
|
517
943
|
status: { code: SpanStatusCode.UNSET }
|
|
518
|
-
})
|
|
519
|
-
|
|
520
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
521
|
-
baseUrl: this.localDebuggerUrl,
|
|
522
|
-
debug: false,
|
|
523
|
-
sdkName: this.sdkName
|
|
524
|
-
});
|
|
525
|
-
}
|
|
944
|
+
})
|
|
945
|
+
);
|
|
526
946
|
return span;
|
|
527
947
|
}
|
|
948
|
+
mirrorToLocalDebugger(span) {
|
|
949
|
+
if (!this.localDebuggerUrl) return;
|
|
950
|
+
const redacted = this.redactSpan(span);
|
|
951
|
+
if (redacted === null) return;
|
|
952
|
+
const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
|
|
953
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
954
|
+
baseUrl: this.localDebuggerUrl,
|
|
955
|
+
debug: false,
|
|
956
|
+
sdkName: this.sdkName
|
|
957
|
+
});
|
|
958
|
+
}
|
|
528
959
|
endSpan(span, extra) {
|
|
529
960
|
var _a, _b;
|
|
530
961
|
if (span.endTimeUnixNano) return;
|
|
@@ -546,14 +977,7 @@ var TraceShipper = class {
|
|
|
546
977
|
status
|
|
547
978
|
});
|
|
548
979
|
this.enqueue(otlp);
|
|
549
|
-
|
|
550
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
551
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
552
|
-
baseUrl: this.localDebuggerUrl,
|
|
553
|
-
debug: false,
|
|
554
|
-
sdkName: this.sdkName
|
|
555
|
-
});
|
|
556
|
-
}
|
|
980
|
+
this.mirrorToLocalDebugger(otlp);
|
|
557
981
|
}
|
|
558
982
|
createSpan(args) {
|
|
559
983
|
var _a;
|
|
@@ -571,14 +995,7 @@ var TraceShipper = class {
|
|
|
571
995
|
status: args.status
|
|
572
996
|
});
|
|
573
997
|
this.enqueue(otlp);
|
|
574
|
-
|
|
575
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
576
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
577
|
-
baseUrl: this.localDebuggerUrl,
|
|
578
|
-
debug: false,
|
|
579
|
-
sdkName: this.sdkName
|
|
580
|
-
});
|
|
581
|
-
}
|
|
998
|
+
this.mirrorToLocalDebugger(otlp);
|
|
582
999
|
}
|
|
583
1000
|
enqueue(span) {
|
|
584
1001
|
if (!this.enabled) return;
|
|
@@ -590,10 +1007,12 @@ var TraceShipper = class {
|
|
|
590
1007
|
)}`
|
|
591
1008
|
);
|
|
592
1009
|
}
|
|
1010
|
+
const redacted = this.redactSpan(span);
|
|
1011
|
+
if (redacted === null) return;
|
|
593
1012
|
if (this.queue.length >= this.maxQueueSize) {
|
|
594
1013
|
this.queue.shift();
|
|
595
1014
|
}
|
|
596
|
-
this.queue.push(
|
|
1015
|
+
this.queue.push(redacted);
|
|
597
1016
|
if (this.queue.length >= this.maxBatchSize) {
|
|
598
1017
|
void this.flush().catch(() => {
|
|
599
1018
|
});
|
|
@@ -615,6 +1034,17 @@ var TraceShipper = class {
|
|
|
615
1034
|
}
|
|
616
1035
|
while (this.queue.length > 0) {
|
|
617
1036
|
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
1037
|
+
if (!this.writeKey) continue;
|
|
1038
|
+
const opts = this.requestOpts();
|
|
1039
|
+
if (!opts) {
|
|
1040
|
+
rateLimitedLog(
|
|
1041
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1042
|
+
() => console.warn(
|
|
1043
|
+
`${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
|
|
1044
|
+
)
|
|
1045
|
+
);
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
618
1048
|
const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
|
|
619
1049
|
const url = `${this.baseUrl}traces`;
|
|
620
1050
|
if (this.debug) {
|
|
@@ -623,11 +1053,7 @@ var TraceShipper = class {
|
|
|
623
1053
|
endpoint: url
|
|
624
1054
|
});
|
|
625
1055
|
}
|
|
626
|
-
const p = postJson(url, body, this.
|
|
627
|
-
maxAttempts: 3,
|
|
628
|
-
debug: this.debug,
|
|
629
|
-
sdkName: this.sdkName
|
|
630
|
-
});
|
|
1056
|
+
const p = postJson(url, body, this.requestHeaders(), opts);
|
|
631
1057
|
this.inFlight.add(p);
|
|
632
1058
|
try {
|
|
633
1059
|
try {
|
|
@@ -635,21 +1061,61 @@ var TraceShipper = class {
|
|
|
635
1061
|
if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
|
|
636
1062
|
} catch (err) {
|
|
637
1063
|
const msg = err instanceof Error ? err.message : String(err);
|
|
638
|
-
|
|
1064
|
+
rateLimitedLog(
|
|
1065
|
+
`${this.prefix}.send_spans_failed`,
|
|
1066
|
+
() => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
|
|
1067
|
+
);
|
|
639
1068
|
}
|
|
640
1069
|
} finally {
|
|
641
1070
|
this.inFlight.delete(p);
|
|
642
1071
|
}
|
|
643
1072
|
}
|
|
644
1073
|
}
|
|
1074
|
+
/** See EventShipper.requestOpts — same shutdown-budget semantics. */
|
|
1075
|
+
requestOpts() {
|
|
1076
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
1077
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
1078
|
+
if (remainingMs <= 0) return null;
|
|
1079
|
+
return {
|
|
1080
|
+
maxAttempts: 1,
|
|
1081
|
+
debug: this.debug,
|
|
1082
|
+
sdkName: this.sdkName,
|
|
1083
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
if (this.hasShutdown) {
|
|
1087
|
+
return {
|
|
1088
|
+
maxAttempts: 1,
|
|
1089
|
+
debug: this.debug,
|
|
1090
|
+
sdkName: this.sdkName,
|
|
1091
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
1095
|
+
}
|
|
645
1096
|
async shutdown() {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
1097
|
+
this.hasShutdown = true;
|
|
1098
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
1099
|
+
try {
|
|
1100
|
+
if (this.timer) {
|
|
1101
|
+
clearTimeout(this.timer);
|
|
1102
|
+
this.timer = void 0;
|
|
1103
|
+
}
|
|
1104
|
+
const drain = async () => {
|
|
1105
|
+
await this.flush();
|
|
1106
|
+
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
1107
|
+
})));
|
|
1108
|
+
};
|
|
1109
|
+
const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
|
|
1110
|
+
if (!settled) {
|
|
1111
|
+
rateLimitedLog(
|
|
1112
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1113
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
} finally {
|
|
1117
|
+
this.shutdownDeadlineAt = void 0;
|
|
649
1118
|
}
|
|
650
|
-
await this.flush();
|
|
651
|
-
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
652
|
-
})));
|
|
653
1119
|
}
|
|
654
1120
|
};
|
|
655
1121
|
|
|
@@ -684,13 +1150,13 @@ function extractTextFromContent(content) {
|
|
|
684
1150
|
}
|
|
685
1151
|
}
|
|
686
1152
|
if (parts.length > 0) return parts.join("\n");
|
|
687
|
-
return safeStringify(content);
|
|
1153
|
+
return safeStringify(content, CHAIN_TEXT_MAX_LEN);
|
|
688
1154
|
}
|
|
689
1155
|
if (typeof content === "object") {
|
|
690
1156
|
const c = content;
|
|
691
1157
|
if (typeof c.text === "string") return c.text;
|
|
692
1158
|
if (typeof c.content === "string") return c.content;
|
|
693
|
-
return safeStringify(content);
|
|
1159
|
+
return safeStringify(content, CHAIN_TEXT_MAX_LEN);
|
|
694
1160
|
}
|
|
695
1161
|
return String(content);
|
|
696
1162
|
}
|
|
@@ -989,7 +1455,7 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
|
|
|
989
1455
|
try {
|
|
990
1456
|
const span = this.spans.get(runId);
|
|
991
1457
|
if (span) {
|
|
992
|
-
const outputStr = typeof output === "string" ? output : safeStringify(output);
|
|
1458
|
+
const outputStr = typeof output === "string" ? output : safeStringify(output, 4096);
|
|
993
1459
|
this.traceShipper.endSpan(span, {
|
|
994
1460
|
attributes: [attrString("tool.output", truncate(outputStr, 4096))]
|
|
995
1461
|
});
|
|
@@ -1035,7 +1501,7 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
|
|
|
1035
1501
|
attributes: [
|
|
1036
1502
|
attrString("ai.toolCall.name", action.tool),
|
|
1037
1503
|
attrString("agent.tool_input", truncate(
|
|
1038
|
-
typeof action.toolInput === "string" ? action.toolInput : safeStringify(action.toolInput),
|
|
1504
|
+
typeof action.toolInput === "string" ? action.toolInput : safeStringify(action.toolInput, 4096),
|
|
1039
1505
|
4096
|
|
1040
1506
|
))
|
|
1041
1507
|
]
|
|
@@ -1070,11 +1536,68 @@ function buildProperties(tags, metadata) {
|
|
|
1070
1536
|
if (metadata) Object.assign(props, metadata);
|
|
1071
1537
|
return props;
|
|
1072
1538
|
}
|
|
1073
|
-
|
|
1539
|
+
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1540
|
+
var MAX_TEXT_FIELD_CHARS = 1e6;
|
|
1541
|
+
var MAX_BOUNDED_DEPTH = 12;
|
|
1542
|
+
function boundedClone2(value, budget, depth) {
|
|
1543
|
+
if (budget.remaining <= 0) return TRUNCATION_MARKER2;
|
|
1544
|
+
if (typeof value === "string") {
|
|
1545
|
+
if (value.length > budget.remaining) {
|
|
1546
|
+
const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
|
|
1547
|
+
budget.remaining = 0;
|
|
1548
|
+
return taken;
|
|
1549
|
+
}
|
|
1550
|
+
budget.remaining -= Math.max(value.length, 1);
|
|
1551
|
+
return value;
|
|
1552
|
+
}
|
|
1553
|
+
if (value === null || typeof value === "number" || typeof value === "boolean") {
|
|
1554
|
+
budget.remaining -= 8;
|
|
1555
|
+
return value;
|
|
1556
|
+
}
|
|
1557
|
+
if (typeof value !== "object") {
|
|
1558
|
+
budget.remaining -= 8;
|
|
1559
|
+
return value;
|
|
1560
|
+
}
|
|
1561
|
+
if (depth >= MAX_BOUNDED_DEPTH) {
|
|
1562
|
+
budget.remaining -= 16;
|
|
1563
|
+
return `<max depth: ${TRUNCATION_MARKER2}>`;
|
|
1564
|
+
}
|
|
1565
|
+
const withToJson = value;
|
|
1566
|
+
if (typeof withToJson.toJSON === "function") {
|
|
1567
|
+
try {
|
|
1568
|
+
return boundedClone2(withToJson.toJSON(), budget, depth + 1);
|
|
1569
|
+
} catch {
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
if (Array.isArray(value)) {
|
|
1573
|
+
const out2 = [];
|
|
1574
|
+
for (const item of value) {
|
|
1575
|
+
if (budget.remaining <= 0) {
|
|
1576
|
+
out2.push(TRUNCATION_MARKER2);
|
|
1577
|
+
break;
|
|
1578
|
+
}
|
|
1579
|
+
out2.push(boundedClone2(item, budget, depth + 1));
|
|
1580
|
+
}
|
|
1581
|
+
return out2;
|
|
1582
|
+
}
|
|
1583
|
+
const out = {};
|
|
1584
|
+
for (const key of Object.keys(value)) {
|
|
1585
|
+
if (budget.remaining <= 0) {
|
|
1586
|
+
out["..."] = TRUNCATION_MARKER2;
|
|
1587
|
+
break;
|
|
1588
|
+
}
|
|
1589
|
+
budget.remaining -= Math.max(key.length, 1);
|
|
1590
|
+
out[key] = boundedClone2(value[key], budget, depth + 1);
|
|
1591
|
+
}
|
|
1592
|
+
return out;
|
|
1593
|
+
}
|
|
1594
|
+
function safeStringify(value, limit = MAX_TEXT_FIELD_CHARS) {
|
|
1074
1595
|
try {
|
|
1075
|
-
|
|
1596
|
+
const pruned = boundedClone2(value, { remaining: limit + TRUNCATION_MARKER2.length + 256 }, 0);
|
|
1597
|
+
return JSON.stringify(pruned) ?? "";
|
|
1076
1598
|
} catch {
|
|
1077
|
-
|
|
1599
|
+
const fallback = String(value);
|
|
1600
|
+
return fallback.length > limit ? fallback.slice(0, limit) : fallback;
|
|
1078
1601
|
}
|
|
1079
1602
|
}
|
|
1080
1603
|
function truncate(str, maxLen) {
|
|
@@ -1154,6 +1677,7 @@ function createRaindropDeepAgents(opts) {
|
|
|
1154
1677
|
endpoint: opts.endpoint,
|
|
1155
1678
|
enabled,
|
|
1156
1679
|
debug: opts.debug ?? false,
|
|
1680
|
+
projectId: opts.projectId,
|
|
1157
1681
|
sdkName: "deep-agents",
|
|
1158
1682
|
libraryName,
|
|
1159
1683
|
libraryVersion
|
|
@@ -1163,6 +1687,7 @@ function createRaindropDeepAgents(opts) {
|
|
|
1163
1687
|
endpoint: opts.endpoint,
|
|
1164
1688
|
enabled,
|
|
1165
1689
|
debug: opts.debug ?? false,
|
|
1690
|
+
projectId: opts.projectId,
|
|
1166
1691
|
sdkName: "deep-agents",
|
|
1167
1692
|
serviceName: "raindrop.deep-agents",
|
|
1168
1693
|
serviceVersion: libraryVersion
|