@raindrop-ai/pi-agent 0.0.4 → 0.0.5
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-2NGOEVWI.js} +339 -44
- package/dist/extension.cjs +352 -48
- package/dist/extension.d.cts +1 -1
- package/dist/extension.d.ts +1 -1
- package/dist/extension.js +16 -5
- package/dist/index.cjs +342 -47
- package/dist/{index.d-Cxs_NTx0.d.cts → index.d-CRPiWjXE.d.cts} +73 -1
- package/dist/{index.d-Cxs_NTx0.d.ts → index.d-CRPiWjXE.d.ts} +73 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -4
- package/package.json +2 -2
package/dist/extension.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
TraceShipper,
|
|
4
4
|
attrInt,
|
|
5
5
|
attrString,
|
|
6
|
+
capText,
|
|
6
7
|
extractAssistantText,
|
|
7
8
|
formatToolSpanName,
|
|
8
9
|
generateId,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
resolveLocalDebuggerBaseUrl,
|
|
14
15
|
safeStringify,
|
|
15
16
|
truncate
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-2NGOEVWI.js";
|
|
17
18
|
|
|
18
19
|
// src/internal/config.ts
|
|
19
20
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -78,6 +79,15 @@ function safeParsArgs(argsStr) {
|
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
var MAX_SYSTEM_PROMPT_LENGTH = 32768;
|
|
82
|
+
var ERROR_LOG_INTERVAL_MS = 3e4;
|
|
83
|
+
var lastErrorLogAt = /* @__PURE__ */ new Map();
|
|
84
|
+
function rateLimitedErrorLog(key, message) {
|
|
85
|
+
const now = Date.now();
|
|
86
|
+
const last = lastErrorLogAt.get(key);
|
|
87
|
+
if (last !== void 0 && now - last < ERROR_LOG_INTERVAL_MS) return;
|
|
88
|
+
lastErrorLogAt.set(key, now);
|
|
89
|
+
console.log(message);
|
|
90
|
+
}
|
|
81
91
|
function createSessionState(sessionId) {
|
|
82
92
|
return {
|
|
83
93
|
sessionId,
|
|
@@ -142,7 +152,8 @@ function getState(stateRef, ctx) {
|
|
|
142
152
|
function registerTracing(pi, config, eventShipper, traceShipper) {
|
|
143
153
|
const stateRef = {};
|
|
144
154
|
function logError(hook, err) {
|
|
145
|
-
|
|
155
|
+
rateLimitedErrorLog(
|
|
156
|
+
hook,
|
|
146
157
|
`[raindrop-ai/pi-agent] [error] Error in ${hook}: ${err instanceof Error ? err.message : String(err)}`
|
|
147
158
|
);
|
|
148
159
|
}
|
|
@@ -195,7 +206,7 @@ function registerTracing(pi, config, eventShipper, traceShipper) {
|
|
|
195
206
|
}
|
|
196
207
|
state.toolSpanStarts.clear();
|
|
197
208
|
state.toolCallToLlmParent.clear();
|
|
198
|
-
state.currentInput = event.prompt;
|
|
209
|
+
state.currentInput = capText(event.prompt);
|
|
199
210
|
state.turnNumber = 0;
|
|
200
211
|
state.currentSystemPrompt = config.captureSystemPrompt ? truncateSystemPrompt(event.systemPrompt) : void 0;
|
|
201
212
|
state.currentEventRequestId = generateId();
|
|
@@ -216,7 +227,7 @@ function registerTracing(pi, config, eventShipper, traceShipper) {
|
|
|
216
227
|
userId: getUserId(state, config.eventMetadata),
|
|
217
228
|
convoId: state.sessionId,
|
|
218
229
|
eventName: getEventName(config),
|
|
219
|
-
input:
|
|
230
|
+
input: state.currentInput,
|
|
220
231
|
...attachments.length > 0 ? { attachments } : {},
|
|
221
232
|
properties: getBaseProperties(config, ctx)
|
|
222
233
|
});
|
|
@@ -267,7 +278,7 @@ function registerTracing(pi, config, eventShipper, traceShipper) {
|
|
|
267
278
|
const modelId = (_c = message.model) != null ? _c : "";
|
|
268
279
|
const modelName = provider && modelId ? `${provider}/${modelId}` : modelId || "llm";
|
|
269
280
|
const errorForSpan = getAssistantError(message);
|
|
270
|
-
const outputText = getAssistantText(message);
|
|
281
|
+
const outputText = capText(getAssistantText(message));
|
|
271
282
|
const inputTokens = (_d = message.usage) == null ? void 0 : _d.input;
|
|
272
283
|
const outputTokens = (_e = message.usage) == null ? void 0 : _e.output;
|
|
273
284
|
const cacheReadTokens = (_f = message.usage) == null ? void 0 : _f.cacheRead;
|
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-U5HUTMR5.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,8 @@ function mirrorPartialEventToLocalDebugger(event, options = {}) {
|
|
|
310
376
|
}).catch(() => {
|
|
311
377
|
});
|
|
312
378
|
}
|
|
379
|
+
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
380
|
+
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
313
381
|
function mergePatches(target, source) {
|
|
314
382
|
var _a, _b, _c, _d;
|
|
315
383
|
const out = { ...target, ...source };
|
|
@@ -327,6 +395,7 @@ var EventShipper = class {
|
|
|
327
395
|
this.sticky = /* @__PURE__ */ new Map();
|
|
328
396
|
this.timers = /* @__PURE__ */ new Map();
|
|
329
397
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
398
|
+
this.hasShutdown = false;
|
|
330
399
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
331
400
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
332
401
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
@@ -336,6 +405,7 @@ var EventShipper = class {
|
|
|
336
405
|
this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
|
|
337
406
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
338
407
|
this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
|
|
408
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
339
409
|
this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
|
|
340
410
|
if (this.debug && this.localDebuggerUrl) {
|
|
341
411
|
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
@@ -358,10 +428,52 @@ var EventShipper = class {
|
|
|
358
428
|
authHeaders() {
|
|
359
429
|
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
360
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* Build the retry/timeout options for one POST, honoring the shutdown
|
|
433
|
+
* deadline. Returns `null` when the shutdown drain window is exhausted —
|
|
434
|
+
* the caller must drop the payload (with a rate-limited warning) instead
|
|
435
|
+
* of issuing a request that could outlive process exit.
|
|
436
|
+
*
|
|
437
|
+
* Checked fresh on EVERY send, so a shutdown that begins while the flush
|
|
438
|
+
* path is mid-drain takes effect immediately: no further retries, and the
|
|
439
|
+
* per-attempt timeout is clamped to the remaining window. After
|
|
440
|
+
* `shutdown()` returns (deadline cleared, `hasShutdown` still set),
|
|
441
|
+
* sends — late callers, or flush work the deadline abandoned mid-drain —
|
|
442
|
+
* run as a single short attempt rather than regaining the full retry
|
|
443
|
+
* schedule.
|
|
444
|
+
*/
|
|
445
|
+
requestOpts() {
|
|
446
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
447
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
448
|
+
if (remainingMs <= 0) return null;
|
|
449
|
+
return {
|
|
450
|
+
maxAttempts: 1,
|
|
451
|
+
debug: this.debug,
|
|
452
|
+
sdkName: this.sdkName,
|
|
453
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
if (this.hasShutdown) {
|
|
457
|
+
return {
|
|
458
|
+
maxAttempts: 1,
|
|
459
|
+
debug: this.debug,
|
|
460
|
+
sdkName: this.sdkName,
|
|
461
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
465
|
+
}
|
|
361
466
|
async patch(eventId, patch) {
|
|
362
467
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
363
468
|
if (!this.enabled) return;
|
|
364
469
|
if (!eventId || !eventId.trim()) return;
|
|
470
|
+
const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
|
|
471
|
+
if (typeof patch.input === "string" && patch.input.length > maxChars) {
|
|
472
|
+
patch = { ...patch, input: capText(patch.input, maxChars) };
|
|
473
|
+
}
|
|
474
|
+
if (typeof patch.output === "string" && patch.output.length > maxChars) {
|
|
475
|
+
patch = { ...patch, output: capText(patch.output, maxChars) };
|
|
476
|
+
}
|
|
365
477
|
if (this.debug) {
|
|
366
478
|
console.log(`${this.prefix} queue patch`, {
|
|
367
479
|
eventId,
|
|
@@ -408,9 +520,16 @@ var EventShipper = class {
|
|
|
408
520
|
})));
|
|
409
521
|
}
|
|
410
522
|
async shutdown() {
|
|
411
|
-
|
|
412
|
-
this.
|
|
413
|
-
|
|
523
|
+
this.hasShutdown = true;
|
|
524
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
525
|
+
try {
|
|
526
|
+
for (const t of this.timers.values()) clearTimeout(t);
|
|
527
|
+
this.timers.clear();
|
|
528
|
+
const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
|
|
529
|
+
if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
|
|
530
|
+
} finally {
|
|
531
|
+
this.shutdownDeadlineAt = void 0;
|
|
532
|
+
}
|
|
414
533
|
}
|
|
415
534
|
async trackSignal(signal) {
|
|
416
535
|
var _a, _b;
|
|
@@ -432,15 +551,19 @@ var EventShipper = class {
|
|
|
432
551
|
];
|
|
433
552
|
if (!this.writeKey) return;
|
|
434
553
|
const url = `${this.baseUrl}signals/track`;
|
|
554
|
+
const opts = this.requestOpts();
|
|
555
|
+
if (!opts) {
|
|
556
|
+
this.warnShutdownDrop("signal");
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
435
559
|
try {
|
|
436
|
-
await postJson(url, body, this.authHeaders(),
|
|
437
|
-
maxAttempts: 3,
|
|
438
|
-
debug: this.debug,
|
|
439
|
-
sdkName: this.sdkName
|
|
440
|
-
});
|
|
560
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
441
561
|
} catch (err) {
|
|
442
562
|
const msg = err instanceof Error ? err.message : String(err);
|
|
443
|
-
|
|
563
|
+
rateLimitedLog(
|
|
564
|
+
`${this.prefix}.send_signal_failed`,
|
|
565
|
+
() => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
|
|
566
|
+
);
|
|
444
567
|
}
|
|
445
568
|
}
|
|
446
569
|
async identify(users) {
|
|
@@ -464,17 +587,27 @@ var EventShipper = class {
|
|
|
464
587
|
if (!this.writeKey) return;
|
|
465
588
|
if (body.length === 0) return;
|
|
466
589
|
const url = `${this.baseUrl}users/identify`;
|
|
590
|
+
const opts = this.requestOpts();
|
|
591
|
+
if (!opts) {
|
|
592
|
+
this.warnShutdownDrop("identify");
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
467
595
|
try {
|
|
468
|
-
await postJson(url, body, this.authHeaders(),
|
|
469
|
-
maxAttempts: 3,
|
|
470
|
-
debug: this.debug,
|
|
471
|
-
sdkName: this.sdkName
|
|
472
|
-
});
|
|
596
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
473
597
|
} catch (err) {
|
|
474
598
|
const msg = err instanceof Error ? err.message : String(err);
|
|
475
|
-
|
|
599
|
+
rateLimitedLog(
|
|
600
|
+
`${this.prefix}.send_identify_failed`,
|
|
601
|
+
() => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
|
|
602
|
+
);
|
|
476
603
|
}
|
|
477
604
|
}
|
|
605
|
+
warnShutdownDrop(what) {
|
|
606
|
+
rateLimitedLog(
|
|
607
|
+
`${this.prefix}.shutdown_deadline`,
|
|
608
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
|
|
609
|
+
);
|
|
610
|
+
}
|
|
478
611
|
async flushOne(eventId) {
|
|
479
612
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
480
613
|
if (!this.enabled) return;
|
|
@@ -550,11 +683,13 @@ var EventShipper = class {
|
|
|
550
683
|
if (!isPending) this.sticky.delete(eventId);
|
|
551
684
|
return;
|
|
552
685
|
}
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
686
|
+
const opts = this.requestOpts();
|
|
687
|
+
if (!opts) {
|
|
688
|
+
this.warnShutdownDrop(`track_partial ${eventId}`);
|
|
689
|
+
if (!isPending) this.sticky.delete(eventId);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const p = postJson(url, payload, this.authHeaders(), opts);
|
|
558
693
|
this.inFlight.add(p);
|
|
559
694
|
try {
|
|
560
695
|
try {
|
|
@@ -564,7 +699,10 @@ var EventShipper = class {
|
|
|
564
699
|
}
|
|
565
700
|
} catch (err) {
|
|
566
701
|
const msg = err instanceof Error ? err.message : String(err);
|
|
567
|
-
|
|
702
|
+
rateLimitedLog(
|
|
703
|
+
`${this.prefix}.send_track_partial_failed`,
|
|
704
|
+
() => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
|
|
705
|
+
);
|
|
568
706
|
}
|
|
569
707
|
} finally {
|
|
570
708
|
this.inFlight.delete(p);
|
|
@@ -663,10 +801,24 @@ function redactJsonAttributeValue(key, value) {
|
|
|
663
801
|
if (scrubbedJson === json) return void 0;
|
|
664
802
|
return { stringValue: scrubbedJson };
|
|
665
803
|
}
|
|
804
|
+
function applyOtelSpanAttributeLimit(limit) {
|
|
805
|
+
var _a, _b;
|
|
806
|
+
try {
|
|
807
|
+
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;
|
|
808
|
+
if (!raw) return limit;
|
|
809
|
+
const parsed = Number.parseInt(raw, 10);
|
|
810
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
811
|
+
return Math.min(limit, parsed);
|
|
812
|
+
}
|
|
813
|
+
} catch (e) {
|
|
814
|
+
}
|
|
815
|
+
return limit;
|
|
816
|
+
}
|
|
666
817
|
var TraceShipper = class {
|
|
667
818
|
constructor(opts) {
|
|
668
819
|
this.queue = [];
|
|
669
820
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
821
|
+
this.hasShutdown = false;
|
|
670
822
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
671
823
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
672
824
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
@@ -686,6 +838,39 @@ var TraceShipper = class {
|
|
|
686
838
|
}
|
|
687
839
|
this.transformSpanHook = opts.transformSpan;
|
|
688
840
|
this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
|
|
841
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Cap every string attribute value on the span. O(#attributes) length
|
|
845
|
+
* checks; only oversized values pay a slice. Runs AFTER the redaction
|
|
846
|
+
* pipeline so the default secret-scrub still sees parseable JSON in
|
|
847
|
+
* `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
|
|
848
|
+
* first could cut a JSON blob mid-way, fail the parse, and ship secrets
|
|
849
|
+
* in the surviving prefix).
|
|
850
|
+
*
|
|
851
|
+
* A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
|
|
852
|
+
* for span content, matching the Python SDK and the OTel SDK convention.
|
|
853
|
+
*/
|
|
854
|
+
capSpanAttributes(span) {
|
|
855
|
+
var _a;
|
|
856
|
+
const maxChars = applyOtelSpanAttributeLimit(
|
|
857
|
+
resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
|
|
858
|
+
);
|
|
859
|
+
const attrs = span.attributes;
|
|
860
|
+
if (!attrs || attrs.length === 0) return span;
|
|
861
|
+
let nextAttrs;
|
|
862
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
863
|
+
const attr = attrs[i];
|
|
864
|
+
const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
|
|
865
|
+
if (typeof value !== "string" || value.length <= maxChars) continue;
|
|
866
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
867
|
+
nextAttrs[i] = {
|
|
868
|
+
key: attr.key,
|
|
869
|
+
value: { ...attr.value, stringValue: capText(value, maxChars) }
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
if (!nextAttrs) return span;
|
|
873
|
+
return { ...span, attributes: nextAttrs };
|
|
689
874
|
}
|
|
690
875
|
/**
|
|
691
876
|
* Apply the user `transformSpan` hook (if any) followed by the default
|
|
@@ -719,7 +904,7 @@ var TraceShipper = class {
|
|
|
719
904
|
if (!this.disableDefaultRedaction) {
|
|
720
905
|
current = defaultTransformSpan(current);
|
|
721
906
|
}
|
|
722
|
-
return current;
|
|
907
|
+
return this.capSpanAttributes(current);
|
|
723
908
|
}
|
|
724
909
|
isDebugEnabled() {
|
|
725
910
|
return this.debug;
|
|
@@ -840,6 +1025,16 @@ var TraceShipper = class {
|
|
|
840
1025
|
while (this.queue.length > 0) {
|
|
841
1026
|
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
842
1027
|
if (!this.writeKey) continue;
|
|
1028
|
+
const opts = this.requestOpts();
|
|
1029
|
+
if (!opts) {
|
|
1030
|
+
rateLimitedLog(
|
|
1031
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1032
|
+
() => console.warn(
|
|
1033
|
+
`${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
|
|
1034
|
+
)
|
|
1035
|
+
);
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
843
1038
|
const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
|
|
844
1039
|
const url = `${this.baseUrl}traces`;
|
|
845
1040
|
if (this.debug) {
|
|
@@ -848,11 +1043,7 @@ var TraceShipper = class {
|
|
|
848
1043
|
endpoint: url
|
|
849
1044
|
});
|
|
850
1045
|
}
|
|
851
|
-
const p = postJson(url, body, this.authHeaders(),
|
|
852
|
-
maxAttempts: 3,
|
|
853
|
-
debug: this.debug,
|
|
854
|
-
sdkName: this.sdkName
|
|
855
|
-
});
|
|
1046
|
+
const p = postJson(url, body, this.authHeaders(), opts);
|
|
856
1047
|
this.inFlight.add(p);
|
|
857
1048
|
try {
|
|
858
1049
|
try {
|
|
@@ -860,21 +1051,61 @@ var TraceShipper = class {
|
|
|
860
1051
|
if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
|
|
861
1052
|
} catch (err) {
|
|
862
1053
|
const msg = err instanceof Error ? err.message : String(err);
|
|
863
|
-
|
|
1054
|
+
rateLimitedLog(
|
|
1055
|
+
`${this.prefix}.send_spans_failed`,
|
|
1056
|
+
() => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
|
|
1057
|
+
);
|
|
864
1058
|
}
|
|
865
1059
|
} finally {
|
|
866
1060
|
this.inFlight.delete(p);
|
|
867
1061
|
}
|
|
868
1062
|
}
|
|
869
1063
|
}
|
|
1064
|
+
/** See EventShipper.requestOpts — same shutdown-budget semantics. */
|
|
1065
|
+
requestOpts() {
|
|
1066
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
1067
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
1068
|
+
if (remainingMs <= 0) return null;
|
|
1069
|
+
return {
|
|
1070
|
+
maxAttempts: 1,
|
|
1071
|
+
debug: this.debug,
|
|
1072
|
+
sdkName: this.sdkName,
|
|
1073
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
if (this.hasShutdown) {
|
|
1077
|
+
return {
|
|
1078
|
+
maxAttempts: 1,
|
|
1079
|
+
debug: this.debug,
|
|
1080
|
+
sdkName: this.sdkName,
|
|
1081
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
1085
|
+
}
|
|
870
1086
|
async shutdown() {
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1087
|
+
this.hasShutdown = true;
|
|
1088
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
1089
|
+
try {
|
|
1090
|
+
if (this.timer) {
|
|
1091
|
+
clearTimeout(this.timer);
|
|
1092
|
+
this.timer = void 0;
|
|
1093
|
+
}
|
|
1094
|
+
const drain = async () => {
|
|
1095
|
+
await this.flush();
|
|
1096
|
+
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
1097
|
+
})));
|
|
1098
|
+
};
|
|
1099
|
+
const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
|
|
1100
|
+
if (!settled) {
|
|
1101
|
+
rateLimitedLog(
|
|
1102
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1103
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
} finally {
|
|
1107
|
+
this.shutdownDeadlineAt = void 0;
|
|
874
1108
|
}
|
|
875
|
-
await this.flush();
|
|
876
|
-
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
877
|
-
})));
|
|
878
1109
|
}
|
|
879
1110
|
};
|
|
880
1111
|
|
|
@@ -885,7 +1116,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
|
|
|
885
1116
|
// package.json
|
|
886
1117
|
var package_default = {
|
|
887
1118
|
name: "@raindrop-ai/pi-agent",
|
|
888
|
-
version: "0.0.
|
|
1119
|
+
version: "0.0.5",
|
|
889
1120
|
description: "Raindrop observability for Pi Agent \u2014 automatic tracing via subscriber or pi-coding-agent extension",
|
|
890
1121
|
type: "module",
|
|
891
1122
|
license: "MIT",
|
|
@@ -1083,19 +1314,82 @@ function formatToolSpanName(toolName, args) {
|
|
|
1083
1314
|
}
|
|
1084
1315
|
return toolName;
|
|
1085
1316
|
}
|
|
1086
|
-
|
|
1317
|
+
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1318
|
+
var MAX_TEXT_FIELD_CHARS = 1e6;
|
|
1319
|
+
var MAX_ATTR_LENGTH = 32768;
|
|
1320
|
+
var MAX_BOUNDED_DEPTH = 12;
|
|
1321
|
+
function capText2(value, limit = MAX_TEXT_FIELD_CHARS) {
|
|
1322
|
+
if (value.length <= limit) return value;
|
|
1323
|
+
if (limit > TRUNCATION_MARKER2.length) {
|
|
1324
|
+
return value.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
|
|
1325
|
+
}
|
|
1326
|
+
return value.slice(0, limit);
|
|
1327
|
+
}
|
|
1328
|
+
function boundedClone2(value, budget, depth) {
|
|
1329
|
+
if (budget.remaining <= 0) return TRUNCATION_MARKER2;
|
|
1330
|
+
if (typeof value === "string") {
|
|
1331
|
+
if (value.length > budget.remaining) {
|
|
1332
|
+
const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
|
|
1333
|
+
budget.remaining = 0;
|
|
1334
|
+
return taken;
|
|
1335
|
+
}
|
|
1336
|
+
budget.remaining -= Math.max(value.length, 1);
|
|
1337
|
+
return value;
|
|
1338
|
+
}
|
|
1339
|
+
if (value === null || typeof value === "number" || typeof value === "boolean") {
|
|
1340
|
+
budget.remaining -= 8;
|
|
1341
|
+
return value;
|
|
1342
|
+
}
|
|
1343
|
+
if (typeof value !== "object") {
|
|
1344
|
+
budget.remaining -= 8;
|
|
1345
|
+
return value;
|
|
1346
|
+
}
|
|
1347
|
+
if (depth >= MAX_BOUNDED_DEPTH) {
|
|
1348
|
+
budget.remaining -= 16;
|
|
1349
|
+
return `<max depth: ${TRUNCATION_MARKER2}>`;
|
|
1350
|
+
}
|
|
1351
|
+
const withToJson = value;
|
|
1352
|
+
if (typeof withToJson.toJSON === "function") {
|
|
1353
|
+
try {
|
|
1354
|
+
return boundedClone2(withToJson.toJSON(), budget, depth + 1);
|
|
1355
|
+
} catch (e) {
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
if (Array.isArray(value)) {
|
|
1359
|
+
const out2 = [];
|
|
1360
|
+
for (const item of value) {
|
|
1361
|
+
if (budget.remaining <= 0) {
|
|
1362
|
+
out2.push(TRUNCATION_MARKER2);
|
|
1363
|
+
break;
|
|
1364
|
+
}
|
|
1365
|
+
out2.push(boundedClone2(item, budget, depth + 1));
|
|
1366
|
+
}
|
|
1367
|
+
return out2;
|
|
1368
|
+
}
|
|
1369
|
+
const out = {};
|
|
1370
|
+
for (const key of Object.keys(value)) {
|
|
1371
|
+
if (budget.remaining <= 0) {
|
|
1372
|
+
out["..."] = TRUNCATION_MARKER2;
|
|
1373
|
+
break;
|
|
1374
|
+
}
|
|
1375
|
+
budget.remaining -= Math.max(key.length, 1);
|
|
1376
|
+
out[key] = boundedClone2(value[key], budget, depth + 1);
|
|
1377
|
+
}
|
|
1378
|
+
return out;
|
|
1379
|
+
}
|
|
1380
|
+
function safeStringify(value, limit = MAX_ATTR_LENGTH) {
|
|
1087
1381
|
if (value === void 0 || value === null) return void 0;
|
|
1088
1382
|
try {
|
|
1089
|
-
|
|
1383
|
+
const pruned = boundedClone2(value, { remaining: limit + TRUNCATION_MARKER2.length + 256 }, 0);
|
|
1384
|
+
return JSON.stringify(pruned);
|
|
1090
1385
|
} catch (e) {
|
|
1091
1386
|
try {
|
|
1092
|
-
return String(value);
|
|
1387
|
+
return capText2(String(value), limit);
|
|
1093
1388
|
} catch (e2) {
|
|
1094
1389
|
return "[unserializable]";
|
|
1095
1390
|
}
|
|
1096
1391
|
}
|
|
1097
1392
|
}
|
|
1098
|
-
var MAX_ATTR_LENGTH = 32768;
|
|
1099
1393
|
function truncate(value) {
|
|
1100
1394
|
if (value === void 0) return void 0;
|
|
1101
1395
|
if (value.length <= MAX_ATTR_LENGTH) return value;
|
|
@@ -1212,9 +1506,10 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
|
|
|
1212
1506
|
if (!currentRun) return;
|
|
1213
1507
|
const userText = extractUserText(message);
|
|
1214
1508
|
if (userText !== void 0) {
|
|
1215
|
-
|
|
1509
|
+
const cappedInput = capText2(userText);
|
|
1510
|
+
currentRun.currentInput = cappedInput;
|
|
1216
1511
|
if (eventShipper) {
|
|
1217
|
-
eventShipper.patch(currentRun.eventId, { input:
|
|
1512
|
+
eventShipper.patch(currentRun.eventId, { input: cappedInput }).catch(() => {
|
|
1218
1513
|
});
|
|
1219
1514
|
}
|
|
1220
1515
|
return;
|
|
@@ -1383,7 +1678,7 @@ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, opt
|
|
|
1383
1678
|
traceShipper.endSpan(run.currentTurnSpan);
|
|
1384
1679
|
run.currentTurnSpan = void 0;
|
|
1385
1680
|
}
|
|
1386
|
-
const outputText = run.outputParts.join("");
|
|
1681
|
+
const outputText = capText2(run.outputParts.join(""));
|
|
1387
1682
|
if (traceShipper && run.rootSpan) {
|
|
1388
1683
|
const rootAttrs = [
|
|
1389
1684
|
attrString("ai.operationId", "generateText")
|