@raindrop-ai/pi-agent 0.0.4 → 0.0.6
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/README.md +9 -0
- package/dist/{chunk-EWIO36KH.js → chunk-5F37XTG6.js} +372 -44
- package/dist/extension.cjs +392 -52
- package/dist/extension.d.cts +8 -1
- package/dist/extension.d.ts +8 -1
- package/dist/extension.js +23 -9
- package/dist/index.cjs +377 -47
- package/dist/{index.d-Cxs_NTx0.d.cts → index.d-D0J2tEXx.d.cts} +91 -1
- package/dist/{index.d-Cxs_NTx0.d.ts → index.d-D0J2tEXx.d.ts} +91 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +8 -4
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -24,7 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(index_exports);
|
|
26
26
|
|
|
27
|
-
// ../core/dist/chunk-
|
|
27
|
+
// ../core/dist/chunk-SK6EJEO7.js
|
|
28
28
|
function getCrypto() {
|
|
29
29
|
const c = globalThis.crypto;
|
|
30
30
|
return c;
|
|
@@ -79,6 +79,8 @@ function base64Encode(bytes) {
|
|
|
79
79
|
}
|
|
80
80
|
return out;
|
|
81
81
|
}
|
|
82
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
83
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
82
84
|
function wait(ms) {
|
|
83
85
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
84
86
|
}
|
|
@@ -86,6 +88,46 @@ function formatEndpoint(endpoint) {
|
|
|
86
88
|
if (!endpoint) return void 0;
|
|
87
89
|
return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
|
88
90
|
}
|
|
91
|
+
function redactUrlForLog(url) {
|
|
92
|
+
try {
|
|
93
|
+
const parsed = new URL(url);
|
|
94
|
+
parsed.username = "";
|
|
95
|
+
parsed.password = "";
|
|
96
|
+
parsed.search = "";
|
|
97
|
+
parsed.hash = "";
|
|
98
|
+
return parsed.toString();
|
|
99
|
+
} catch (e) {
|
|
100
|
+
return "<unparseable-url>";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
|
|
104
|
+
var rateLimitedLogLast = /* @__PURE__ */ new Map();
|
|
105
|
+
function rateLimitedLog(key, log) {
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
const last = rateLimitedLogLast.get(key);
|
|
108
|
+
if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
rateLimitedLogLast.set(key, now);
|
|
112
|
+
log();
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
async function raceWithTimeout(promise, timeoutMs) {
|
|
116
|
+
let timer;
|
|
117
|
+
const settledInTime = await Promise.race([
|
|
118
|
+
promise.then(
|
|
119
|
+
() => true,
|
|
120
|
+
() => true
|
|
121
|
+
),
|
|
122
|
+
new Promise((resolve) => {
|
|
123
|
+
var _a;
|
|
124
|
+
timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
|
|
125
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
|
126
|
+
})
|
|
127
|
+
]);
|
|
128
|
+
if (timer) clearTimeout(timer);
|
|
129
|
+
return settledInTime;
|
|
130
|
+
}
|
|
89
131
|
function parseRetryAfter(headers) {
|
|
90
132
|
var _a;
|
|
91
133
|
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
@@ -102,12 +144,12 @@ function parseRetryAfter(headers) {
|
|
|
102
144
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
103
145
|
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
104
146
|
const v = previousError.retryAfterMs;
|
|
105
|
-
if (typeof v === "number") return Math.max(0, v);
|
|
147
|
+
if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
|
|
106
148
|
}
|
|
107
149
|
if (attemptNumber <= 1) return 0;
|
|
108
150
|
const base = 500;
|
|
109
151
|
const factor = Math.pow(2, attemptNumber - 2);
|
|
110
|
-
return base * factor;
|
|
152
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
111
153
|
}
|
|
112
154
|
async function withRetry(operation, opName, opts) {
|
|
113
155
|
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
@@ -142,7 +184,9 @@ async function withRetry(operation, opName, opts) {
|
|
|
142
184
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
143
185
|
}
|
|
144
186
|
async function postJson(url, body, headers, opts) {
|
|
145
|
-
|
|
187
|
+
var _a;
|
|
188
|
+
const opName = `POST ${redactUrlForLog(url)}`;
|
|
189
|
+
const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
146
190
|
await withRetry(
|
|
147
191
|
async () => {
|
|
148
192
|
const resp = await fetch(url, {
|
|
@@ -151,7 +195,8 @@ async function postJson(url, body, headers, opts) {
|
|
|
151
195
|
"Content-Type": "application/json",
|
|
152
196
|
...headers
|
|
153
197
|
},
|
|
154
|
-
body: JSON.stringify(body)
|
|
198
|
+
body: JSON.stringify(body),
|
|
199
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
155
200
|
});
|
|
156
201
|
if (!resp.ok) {
|
|
157
202
|
const text = await resp.text().catch(() => "");
|
|
@@ -168,6 +213,27 @@ async function postJson(url, body, headers, opts) {
|
|
|
168
213
|
opts
|
|
169
214
|
);
|
|
170
215
|
}
|
|
216
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
|
|
217
|
+
var TRUNCATION_MARKER = "...[truncated by raindrop]";
|
|
218
|
+
var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
|
|
219
|
+
function resolveMaxTextFieldChars(value) {
|
|
220
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
221
|
+
return Math.floor(value);
|
|
222
|
+
}
|
|
223
|
+
return currentDefaultMaxTextFieldChars;
|
|
224
|
+
}
|
|
225
|
+
function truncateToLimit(text, limit) {
|
|
226
|
+
if (limit > TRUNCATION_MARKER.length) {
|
|
227
|
+
return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
|
|
228
|
+
}
|
|
229
|
+
return text.slice(0, Math.max(0, limit));
|
|
230
|
+
}
|
|
231
|
+
function capText(value, limit) {
|
|
232
|
+
if (typeof value !== "string") return value;
|
|
233
|
+
const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
|
|
234
|
+
if (value.length <= max) return value;
|
|
235
|
+
return truncateToLimit(value, max);
|
|
236
|
+
}
|
|
171
237
|
var SpanStatusCode = {
|
|
172
238
|
UNSET: 0,
|
|
173
239
|
OK: 1,
|
|
@@ -310,6 +376,27 @@ function mirrorPartialEventToLocalDebugger(event, options = {}) {
|
|
|
310
376
|
}).catch(() => {
|
|
311
377
|
});
|
|
312
378
|
}
|
|
379
|
+
var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
|
|
380
|
+
var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
381
|
+
function isValidProjectIdSlug(value) {
|
|
382
|
+
return PROJECT_ID_SLUG_PATTERN.test(value);
|
|
383
|
+
}
|
|
384
|
+
function normalizeProjectId(raw, opts) {
|
|
385
|
+
if (typeof raw !== "string") return void 0;
|
|
386
|
+
const trimmed = raw.trim();
|
|
387
|
+
if (!trimmed) return void 0;
|
|
388
|
+
if (!isValidProjectIdSlug(trimmed) && opts.debug) {
|
|
389
|
+
console.warn(
|
|
390
|
+
`${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
return trimmed;
|
|
394
|
+
}
|
|
395
|
+
function projectIdHeaders(projectId) {
|
|
396
|
+
return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
|
|
397
|
+
}
|
|
398
|
+
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
399
|
+
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
313
400
|
function mergePatches(target, source) {
|
|
314
401
|
var _a, _b, _c, _d;
|
|
315
402
|
const out = { ...target, ...source };
|
|
@@ -327,6 +414,7 @@ var EventShipper = class {
|
|
|
327
414
|
this.sticky = /* @__PURE__ */ new Map();
|
|
328
415
|
this.timers = /* @__PURE__ */ new Map();
|
|
329
416
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
417
|
+
this.hasShutdown = false;
|
|
330
418
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
331
419
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
332
420
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
@@ -336,10 +424,15 @@ var EventShipper = class {
|
|
|
336
424
|
this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
|
|
337
425
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
338
426
|
this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
|
|
427
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
339
428
|
this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
|
|
340
429
|
if (this.debug && this.localDebuggerUrl) {
|
|
341
430
|
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
342
431
|
}
|
|
432
|
+
this.projectId = normalizeProjectId(opts.projectId, {
|
|
433
|
+
debug: this.debug,
|
|
434
|
+
prefix: this.prefix
|
|
435
|
+
});
|
|
343
436
|
const isNode = typeof process !== "undefined" && typeof process.version === "string";
|
|
344
437
|
this.context = {
|
|
345
438
|
library: {
|
|
@@ -358,10 +451,55 @@ var EventShipper = class {
|
|
|
358
451
|
authHeaders() {
|
|
359
452
|
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
360
453
|
}
|
|
454
|
+
requestHeaders() {
|
|
455
|
+
return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Build the retry/timeout options for one POST, honoring the shutdown
|
|
459
|
+
* deadline. Returns `null` when the shutdown drain window is exhausted —
|
|
460
|
+
* the caller must drop the payload (with a rate-limited warning) instead
|
|
461
|
+
* of issuing a request that could outlive process exit.
|
|
462
|
+
*
|
|
463
|
+
* Checked fresh on EVERY send, so a shutdown that begins while the flush
|
|
464
|
+
* path is mid-drain takes effect immediately: no further retries, and the
|
|
465
|
+
* per-attempt timeout is clamped to the remaining window. After
|
|
466
|
+
* `shutdown()` returns (deadline cleared, `hasShutdown` still set),
|
|
467
|
+
* sends — late callers, or flush work the deadline abandoned mid-drain —
|
|
468
|
+
* run as a single short attempt rather than regaining the full retry
|
|
469
|
+
* schedule.
|
|
470
|
+
*/
|
|
471
|
+
requestOpts() {
|
|
472
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
473
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
474
|
+
if (remainingMs <= 0) return null;
|
|
475
|
+
return {
|
|
476
|
+
maxAttempts: 1,
|
|
477
|
+
debug: this.debug,
|
|
478
|
+
sdkName: this.sdkName,
|
|
479
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
if (this.hasShutdown) {
|
|
483
|
+
return {
|
|
484
|
+
maxAttempts: 1,
|
|
485
|
+
debug: this.debug,
|
|
486
|
+
sdkName: this.sdkName,
|
|
487
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
491
|
+
}
|
|
361
492
|
async patch(eventId, patch) {
|
|
362
493
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
363
494
|
if (!this.enabled) return;
|
|
364
495
|
if (!eventId || !eventId.trim()) return;
|
|
496
|
+
const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
|
|
497
|
+
if (typeof patch.input === "string" && patch.input.length > maxChars) {
|
|
498
|
+
patch = { ...patch, input: capText(patch.input, maxChars) };
|
|
499
|
+
}
|
|
500
|
+
if (typeof patch.output === "string" && patch.output.length > maxChars) {
|
|
501
|
+
patch = { ...patch, output: capText(patch.output, maxChars) };
|
|
502
|
+
}
|
|
365
503
|
if (this.debug) {
|
|
366
504
|
console.log(`${this.prefix} queue patch`, {
|
|
367
505
|
eventId,
|
|
@@ -408,9 +546,16 @@ var EventShipper = class {
|
|
|
408
546
|
})));
|
|
409
547
|
}
|
|
410
548
|
async shutdown() {
|
|
411
|
-
|
|
412
|
-
this.
|
|
413
|
-
|
|
549
|
+
this.hasShutdown = true;
|
|
550
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
551
|
+
try {
|
|
552
|
+
for (const t of this.timers.values()) clearTimeout(t);
|
|
553
|
+
this.timers.clear();
|
|
554
|
+
const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
|
|
555
|
+
if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
|
|
556
|
+
} finally {
|
|
557
|
+
this.shutdownDeadlineAt = void 0;
|
|
558
|
+
}
|
|
414
559
|
}
|
|
415
560
|
async trackSignal(signal) {
|
|
416
561
|
var _a, _b;
|
|
@@ -432,15 +577,19 @@ var EventShipper = class {
|
|
|
432
577
|
];
|
|
433
578
|
if (!this.writeKey) return;
|
|
434
579
|
const url = `${this.baseUrl}signals/track`;
|
|
580
|
+
const opts = this.requestOpts();
|
|
581
|
+
if (!opts) {
|
|
582
|
+
this.warnShutdownDrop("signal");
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
435
585
|
try {
|
|
436
|
-
await postJson(url, body, this.
|
|
437
|
-
maxAttempts: 3,
|
|
438
|
-
debug: this.debug,
|
|
439
|
-
sdkName: this.sdkName
|
|
440
|
-
});
|
|
586
|
+
await postJson(url, body, this.requestHeaders(), opts);
|
|
441
587
|
} catch (err) {
|
|
442
588
|
const msg = err instanceof Error ? err.message : String(err);
|
|
443
|
-
|
|
589
|
+
rateLimitedLog(
|
|
590
|
+
`${this.prefix}.send_signal_failed`,
|
|
591
|
+
() => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
|
|
592
|
+
);
|
|
444
593
|
}
|
|
445
594
|
}
|
|
446
595
|
async identify(users) {
|
|
@@ -464,17 +613,27 @@ var EventShipper = class {
|
|
|
464
613
|
if (!this.writeKey) return;
|
|
465
614
|
if (body.length === 0) return;
|
|
466
615
|
const url = `${this.baseUrl}users/identify`;
|
|
616
|
+
const opts = this.requestOpts();
|
|
617
|
+
if (!opts) {
|
|
618
|
+
this.warnShutdownDrop("identify");
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
467
621
|
try {
|
|
468
|
-
await postJson(url, body, this.
|
|
469
|
-
maxAttempts: 3,
|
|
470
|
-
debug: this.debug,
|
|
471
|
-
sdkName: this.sdkName
|
|
472
|
-
});
|
|
622
|
+
await postJson(url, body, this.requestHeaders(), opts);
|
|
473
623
|
} catch (err) {
|
|
474
624
|
const msg = err instanceof Error ? err.message : String(err);
|
|
475
|
-
|
|
625
|
+
rateLimitedLog(
|
|
626
|
+
`${this.prefix}.send_identify_failed`,
|
|
627
|
+
() => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
|
|
628
|
+
);
|
|
476
629
|
}
|
|
477
630
|
}
|
|
631
|
+
warnShutdownDrop(what) {
|
|
632
|
+
rateLimitedLog(
|
|
633
|
+
`${this.prefix}.shutdown_deadline`,
|
|
634
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
|
|
635
|
+
);
|
|
636
|
+
}
|
|
478
637
|
async flushOne(eventId) {
|
|
479
638
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
480
639
|
if (!this.enabled) return;
|
|
@@ -550,11 +709,13 @@ var EventShipper = class {
|
|
|
550
709
|
if (!isPending) this.sticky.delete(eventId);
|
|
551
710
|
return;
|
|
552
711
|
}
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
712
|
+
const opts = this.requestOpts();
|
|
713
|
+
if (!opts) {
|
|
714
|
+
this.warnShutdownDrop(`track_partial ${eventId}`);
|
|
715
|
+
if (!isPending) this.sticky.delete(eventId);
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
const p = postJson(url, payload, this.requestHeaders(), opts);
|
|
558
719
|
this.inFlight.add(p);
|
|
559
720
|
try {
|
|
560
721
|
try {
|
|
@@ -564,7 +725,10 @@ var EventShipper = class {
|
|
|
564
725
|
}
|
|
565
726
|
} catch (err) {
|
|
566
727
|
const msg = err instanceof Error ? err.message : String(err);
|
|
567
|
-
|
|
728
|
+
rateLimitedLog(
|
|
729
|
+
`${this.prefix}.send_track_partial_failed`,
|
|
730
|
+
() => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
|
|
731
|
+
);
|
|
568
732
|
}
|
|
569
733
|
} finally {
|
|
570
734
|
this.inFlight.delete(p);
|
|
@@ -663,10 +827,24 @@ function redactJsonAttributeValue(key, value) {
|
|
|
663
827
|
if (scrubbedJson === json) return void 0;
|
|
664
828
|
return { stringValue: scrubbedJson };
|
|
665
829
|
}
|
|
830
|
+
function applyOtelSpanAttributeLimit(limit) {
|
|
831
|
+
var _a, _b;
|
|
832
|
+
try {
|
|
833
|
+
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;
|
|
834
|
+
if (!raw) return limit;
|
|
835
|
+
const parsed = Number.parseInt(raw, 10);
|
|
836
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
837
|
+
return Math.min(limit, parsed);
|
|
838
|
+
}
|
|
839
|
+
} catch (e) {
|
|
840
|
+
}
|
|
841
|
+
return limit;
|
|
842
|
+
}
|
|
666
843
|
var TraceShipper = class {
|
|
667
844
|
constructor(opts) {
|
|
668
845
|
this.queue = [];
|
|
669
846
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
847
|
+
this.hasShutdown = false;
|
|
670
848
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
671
849
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
672
850
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
@@ -684,8 +862,45 @@ var TraceShipper = class {
|
|
|
684
862
|
if (this.debug && this.localDebuggerUrl) {
|
|
685
863
|
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
686
864
|
}
|
|
865
|
+
this.projectId = normalizeProjectId(opts.projectId, {
|
|
866
|
+
debug: this.debug,
|
|
867
|
+
prefix: this.prefix
|
|
868
|
+
});
|
|
687
869
|
this.transformSpanHook = opts.transformSpan;
|
|
688
870
|
this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
|
|
871
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Cap every string attribute value on the span. O(#attributes) length
|
|
875
|
+
* checks; only oversized values pay a slice. Runs AFTER the redaction
|
|
876
|
+
* pipeline so the default secret-scrub still sees parseable JSON in
|
|
877
|
+
* `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
|
|
878
|
+
* first could cut a JSON blob mid-way, fail the parse, and ship secrets
|
|
879
|
+
* in the surviving prefix).
|
|
880
|
+
*
|
|
881
|
+
* A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
|
|
882
|
+
* for span content, matching the Python SDK and the OTel SDK convention.
|
|
883
|
+
*/
|
|
884
|
+
capSpanAttributes(span) {
|
|
885
|
+
var _a;
|
|
886
|
+
const maxChars = applyOtelSpanAttributeLimit(
|
|
887
|
+
resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
|
|
888
|
+
);
|
|
889
|
+
const attrs = span.attributes;
|
|
890
|
+
if (!attrs || attrs.length === 0) return span;
|
|
891
|
+
let nextAttrs;
|
|
892
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
893
|
+
const attr = attrs[i];
|
|
894
|
+
const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
|
|
895
|
+
if (typeof value !== "string" || value.length <= maxChars) continue;
|
|
896
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
897
|
+
nextAttrs[i] = {
|
|
898
|
+
key: attr.key,
|
|
899
|
+
value: { ...attr.value, stringValue: capText(value, maxChars) }
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
if (!nextAttrs) return span;
|
|
903
|
+
return { ...span, attributes: nextAttrs };
|
|
689
904
|
}
|
|
690
905
|
/**
|
|
691
906
|
* Apply the user `transformSpan` hook (if any) followed by the default
|
|
@@ -719,7 +934,7 @@ var TraceShipper = class {
|
|
|
719
934
|
if (!this.disableDefaultRedaction) {
|
|
720
935
|
current = defaultTransformSpan(current);
|
|
721
936
|
}
|
|
722
|
-
return current;
|
|
937
|
+
return this.capSpanAttributes(current);
|
|
723
938
|
}
|
|
724
939
|
isDebugEnabled() {
|
|
725
940
|
return this.debug;
|
|
@@ -727,6 +942,9 @@ var TraceShipper = class {
|
|
|
727
942
|
authHeaders() {
|
|
728
943
|
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
729
944
|
}
|
|
945
|
+
requestHeaders() {
|
|
946
|
+
return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
|
|
947
|
+
}
|
|
730
948
|
startSpan(args) {
|
|
731
949
|
var _a, _b;
|
|
732
950
|
const ids = createSpanIds(args.parent);
|
|
@@ -840,6 +1058,16 @@ var TraceShipper = class {
|
|
|
840
1058
|
while (this.queue.length > 0) {
|
|
841
1059
|
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
842
1060
|
if (!this.writeKey) continue;
|
|
1061
|
+
const opts = this.requestOpts();
|
|
1062
|
+
if (!opts) {
|
|
1063
|
+
rateLimitedLog(
|
|
1064
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1065
|
+
() => console.warn(
|
|
1066
|
+
`${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
|
|
1067
|
+
)
|
|
1068
|
+
);
|
|
1069
|
+
continue;
|
|
1070
|
+
}
|
|
843
1071
|
const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
|
|
844
1072
|
const url = `${this.baseUrl}traces`;
|
|
845
1073
|
if (this.debug) {
|
|
@@ -848,11 +1076,7 @@ var TraceShipper = class {
|
|
|
848
1076
|
endpoint: url
|
|
849
1077
|
});
|
|
850
1078
|
}
|
|
851
|
-
const p = postJson(url, body, this.
|
|
852
|
-
maxAttempts: 3,
|
|
853
|
-
debug: this.debug,
|
|
854
|
-
sdkName: this.sdkName
|
|
855
|
-
});
|
|
1079
|
+
const p = postJson(url, body, this.requestHeaders(), opts);
|
|
856
1080
|
this.inFlight.add(p);
|
|
857
1081
|
try {
|
|
858
1082
|
try {
|
|
@@ -860,21 +1084,61 @@ var TraceShipper = class {
|
|
|
860
1084
|
if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
|
|
861
1085
|
} catch (err) {
|
|
862
1086
|
const msg = err instanceof Error ? err.message : String(err);
|
|
863
|
-
|
|
1087
|
+
rateLimitedLog(
|
|
1088
|
+
`${this.prefix}.send_spans_failed`,
|
|
1089
|
+
() => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
|
|
1090
|
+
);
|
|
864
1091
|
}
|
|
865
1092
|
} finally {
|
|
866
1093
|
this.inFlight.delete(p);
|
|
867
1094
|
}
|
|
868
1095
|
}
|
|
869
1096
|
}
|
|
1097
|
+
/** See EventShipper.requestOpts — same shutdown-budget semantics. */
|
|
1098
|
+
requestOpts() {
|
|
1099
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
1100
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
1101
|
+
if (remainingMs <= 0) return null;
|
|
1102
|
+
return {
|
|
1103
|
+
maxAttempts: 1,
|
|
1104
|
+
debug: this.debug,
|
|
1105
|
+
sdkName: this.sdkName,
|
|
1106
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
if (this.hasShutdown) {
|
|
1110
|
+
return {
|
|
1111
|
+
maxAttempts: 1,
|
|
1112
|
+
debug: this.debug,
|
|
1113
|
+
sdkName: this.sdkName,
|
|
1114
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
1118
|
+
}
|
|
870
1119
|
async shutdown() {
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1120
|
+
this.hasShutdown = true;
|
|
1121
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
1122
|
+
try {
|
|
1123
|
+
if (this.timer) {
|
|
1124
|
+
clearTimeout(this.timer);
|
|
1125
|
+
this.timer = void 0;
|
|
1126
|
+
}
|
|
1127
|
+
const drain = async () => {
|
|
1128
|
+
await this.flush();
|
|
1129
|
+
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
1130
|
+
})));
|
|
1131
|
+
};
|
|
1132
|
+
const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
|
|
1133
|
+
if (!settled) {
|
|
1134
|
+
rateLimitedLog(
|
|
1135
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1136
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
} finally {
|
|
1140
|
+
this.shutdownDeadlineAt = void 0;
|
|
874
1141
|
}
|
|
875
|
-
await this.flush();
|
|
876
|
-
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
877
|
-
})));
|
|
878
1142
|
}
|
|
879
1143
|
};
|
|
880
1144
|
|
|
@@ -885,7 +1149,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
|
|
|
885
1149
|
// package.json
|
|
886
1150
|
var package_default = {
|
|
887
1151
|
name: "@raindrop-ai/pi-agent",
|
|
888
|
-
version: "0.0.
|
|
1152
|
+
version: "0.0.6",
|
|
889
1153
|
description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
|
|
890
1154
|
type: "module",
|
|
891
1155
|
license: "MIT",
|
|
@@ -1083,19 +1347,82 @@ function formatToolSpanName(toolName, args) {
|
|
|
1083
1347
|
}
|
|
1084
1348
|
return toolName;
|
|
1085
1349
|
}
|
|
1086
|
-
|
|
1350
|
+
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1351
|
+
var MAX_TEXT_FIELD_CHARS = 1e6;
|
|
1352
|
+
var MAX_ATTR_LENGTH = 32768;
|
|
1353
|
+
var MAX_BOUNDED_DEPTH = 12;
|
|
1354
|
+
function capText2(value, limit = MAX_TEXT_FIELD_CHARS) {
|
|
1355
|
+
if (value.length <= limit) return value;
|
|
1356
|
+
if (limit > TRUNCATION_MARKER2.length) {
|
|
1357
|
+
return value.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
|
|
1358
|
+
}
|
|
1359
|
+
return value.slice(0, limit);
|
|
1360
|
+
}
|
|
1361
|
+
function boundedClone2(value, budget, depth) {
|
|
1362
|
+
if (budget.remaining <= 0) return TRUNCATION_MARKER2;
|
|
1363
|
+
if (typeof value === "string") {
|
|
1364
|
+
if (value.length > budget.remaining) {
|
|
1365
|
+
const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
|
|
1366
|
+
budget.remaining = 0;
|
|
1367
|
+
return taken;
|
|
1368
|
+
}
|
|
1369
|
+
budget.remaining -= Math.max(value.length, 1);
|
|
1370
|
+
return value;
|
|
1371
|
+
}
|
|
1372
|
+
if (value === null || typeof value === "number" || typeof value === "boolean") {
|
|
1373
|
+
budget.remaining -= 8;
|
|
1374
|
+
return value;
|
|
1375
|
+
}
|
|
1376
|
+
if (typeof value !== "object") {
|
|
1377
|
+
budget.remaining -= 8;
|
|
1378
|
+
return value;
|
|
1379
|
+
}
|
|
1380
|
+
if (depth >= MAX_BOUNDED_DEPTH) {
|
|
1381
|
+
budget.remaining -= 16;
|
|
1382
|
+
return `<max depth: ${TRUNCATION_MARKER2}>`;
|
|
1383
|
+
}
|
|
1384
|
+
const withToJson = value;
|
|
1385
|
+
if (typeof withToJson.toJSON === "function") {
|
|
1386
|
+
try {
|
|
1387
|
+
return boundedClone2(withToJson.toJSON(), budget, depth + 1);
|
|
1388
|
+
} catch (e) {
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
if (Array.isArray(value)) {
|
|
1392
|
+
const out2 = [];
|
|
1393
|
+
for (const item of value) {
|
|
1394
|
+
if (budget.remaining <= 0) {
|
|
1395
|
+
out2.push(TRUNCATION_MARKER2);
|
|
1396
|
+
break;
|
|
1397
|
+
}
|
|
1398
|
+
out2.push(boundedClone2(item, budget, depth + 1));
|
|
1399
|
+
}
|
|
1400
|
+
return out2;
|
|
1401
|
+
}
|
|
1402
|
+
const out = {};
|
|
1403
|
+
for (const key of Object.keys(value)) {
|
|
1404
|
+
if (budget.remaining <= 0) {
|
|
1405
|
+
out["..."] = TRUNCATION_MARKER2;
|
|
1406
|
+
break;
|
|
1407
|
+
}
|
|
1408
|
+
budget.remaining -= Math.max(key.length, 1);
|
|
1409
|
+
out[key] = boundedClone2(value[key], budget, depth + 1);
|
|
1410
|
+
}
|
|
1411
|
+
return out;
|
|
1412
|
+
}
|
|
1413
|
+
function safeStringify(value, limit = MAX_ATTR_LENGTH) {
|
|
1087
1414
|
if (value === void 0 || value === null) return void 0;
|
|
1088
1415
|
try {
|
|
1089
|
-
|
|
1416
|
+
const pruned = boundedClone2(value, { remaining: limit + TRUNCATION_MARKER2.length + 256 }, 0);
|
|
1417
|
+
return JSON.stringify(pruned);
|
|
1090
1418
|
} catch (e) {
|
|
1091
1419
|
try {
|
|
1092
|
-
return String(value);
|
|
1420
|
+
return capText2(String(value), limit);
|
|
1093
1421
|
} catch (e2) {
|
|
1094
1422
|
return "[unserializable]";
|
|
1095
1423
|
}
|
|
1096
1424
|
}
|
|
1097
1425
|
}
|
|
1098
|
-
var MAX_ATTR_LENGTH = 32768;
|
|
1099
1426
|
function truncate(value) {
|
|
1100
1427
|
if (value === void 0) return void 0;
|
|
1101
1428
|
if (value.length <= MAX_ATTR_LENGTH) return value;
|
|
@@ -1212,9 +1539,10 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
|
|
|
1212
1539
|
if (!currentRun) return;
|
|
1213
1540
|
const userText = extractUserText(message);
|
|
1214
1541
|
if (userText !== void 0) {
|
|
1215
|
-
|
|
1542
|
+
const cappedInput = capText2(userText);
|
|
1543
|
+
currentRun.currentInput = cappedInput;
|
|
1216
1544
|
if (eventShipper) {
|
|
1217
|
-
eventShipper.patch(currentRun.eventId, { input:
|
|
1545
|
+
eventShipper.patch(currentRun.eventId, { input: cappedInput }).catch(() => {
|
|
1218
1546
|
});
|
|
1219
1547
|
}
|
|
1220
1548
|
return;
|
|
@@ -1383,7 +1711,7 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
|
|
|
1383
1711
|
traceShipper.endSpan(run.currentTurnSpan);
|
|
1384
1712
|
run.currentTurnSpan = void 0;
|
|
1385
1713
|
}
|
|
1386
|
-
const outputText = run.outputParts.join("");
|
|
1714
|
+
const outputText = capText2(run.outputParts.join(""));
|
|
1387
1715
|
if (traceShipper && run.rootSpan) {
|
|
1388
1716
|
const rootAttrs = [
|
|
1389
1717
|
attrString("ai.operationId", "generateText")
|
|
@@ -1501,6 +1829,7 @@ function createRaindropPiAgent(opts) {
|
|
|
1501
1829
|
enabled: true,
|
|
1502
1830
|
debug: ((_e = opts.events) == null ? void 0 : _e.debug) === true || envDebug,
|
|
1503
1831
|
partialFlushMs: (_f = opts.events) == null ? void 0 : _f.partialFlushMs,
|
|
1832
|
+
projectId: opts.projectId,
|
|
1504
1833
|
localDebuggerUrl: opts.localWorkshopUrl
|
|
1505
1834
|
}) : null;
|
|
1506
1835
|
const traceShipper = tracesEnabled && (hasWriteKey || opts.endpoint || hasLocalDestination) ? new TraceShipper2({
|
|
@@ -1512,6 +1841,7 @@ function createRaindropPiAgent(opts) {
|
|
|
1512
1841
|
flushIntervalMs: (_i = opts.traces) == null ? void 0 : _i.flushIntervalMs,
|
|
1513
1842
|
maxBatchSize: (_j = opts.traces) == null ? void 0 : _j.maxBatchSize,
|
|
1514
1843
|
maxQueueSize: (_k = opts.traces) == null ? void 0 : _k.maxQueueSize,
|
|
1844
|
+
projectId: opts.projectId,
|
|
1515
1845
|
localDebuggerUrl: opts.localWorkshopUrl
|
|
1516
1846
|
}) : null;
|
|
1517
1847
|
const defaultOptions = {
|