@raindrop-ai/opencode-plugin 0.0.12 → 0.0.13
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 +11 -0
- package/dist/index.cjs +522 -85
- package/dist/index.js +522 -85
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
5
5
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
-
// ../core/dist/chunk-
|
|
8
|
+
// ../core/dist/chunk-U5HUTMR5.js
|
|
9
9
|
function getCrypto() {
|
|
10
10
|
const c = globalThis.crypto;
|
|
11
11
|
return c;
|
|
@@ -52,6 +52,8 @@ function base64Encode(bytes) {
|
|
|
52
52
|
function generateId() {
|
|
53
53
|
return base64Encode(randomBytes(12)).replace(/[+/=]/g, "").slice(0, 16);
|
|
54
54
|
}
|
|
55
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
56
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
55
57
|
function wait(ms) {
|
|
56
58
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
57
59
|
}
|
|
@@ -59,6 +61,46 @@ function formatEndpoint(endpoint) {
|
|
|
59
61
|
if (!endpoint) return void 0;
|
|
60
62
|
return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
|
61
63
|
}
|
|
64
|
+
function redactUrlForLog(url) {
|
|
65
|
+
try {
|
|
66
|
+
const parsed = new URL(url);
|
|
67
|
+
parsed.username = "";
|
|
68
|
+
parsed.password = "";
|
|
69
|
+
parsed.search = "";
|
|
70
|
+
parsed.hash = "";
|
|
71
|
+
return parsed.toString();
|
|
72
|
+
} catch (e) {
|
|
73
|
+
return "<unparseable-url>";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
|
|
77
|
+
var rateLimitedLogLast = /* @__PURE__ */ new Map();
|
|
78
|
+
function rateLimitedLog(key, log) {
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
const last = rateLimitedLogLast.get(key);
|
|
81
|
+
if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
rateLimitedLogLast.set(key, now);
|
|
85
|
+
log();
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
async function raceWithTimeout(promise, timeoutMs) {
|
|
89
|
+
let timer;
|
|
90
|
+
const settledInTime = await Promise.race([
|
|
91
|
+
promise.then(
|
|
92
|
+
() => true,
|
|
93
|
+
() => true
|
|
94
|
+
),
|
|
95
|
+
new Promise((resolve) => {
|
|
96
|
+
var _a;
|
|
97
|
+
timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
|
|
98
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
|
99
|
+
})
|
|
100
|
+
]);
|
|
101
|
+
if (timer) clearTimeout(timer);
|
|
102
|
+
return settledInTime;
|
|
103
|
+
}
|
|
62
104
|
function parseRetryAfter(headers) {
|
|
63
105
|
var _a;
|
|
64
106
|
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
@@ -75,12 +117,12 @@ function parseRetryAfter(headers) {
|
|
|
75
117
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
76
118
|
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
77
119
|
const v = previousError.retryAfterMs;
|
|
78
|
-
if (typeof v === "number") return Math.max(0, v);
|
|
120
|
+
if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
|
|
79
121
|
}
|
|
80
122
|
if (attemptNumber <= 1) return 0;
|
|
81
123
|
const base = 500;
|
|
82
124
|
const factor = Math.pow(2, attemptNumber - 2);
|
|
83
|
-
return base * factor;
|
|
125
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
84
126
|
}
|
|
85
127
|
async function withRetry(operation, opName, opts) {
|
|
86
128
|
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
@@ -115,7 +157,9 @@ async function withRetry(operation, opName, opts) {
|
|
|
115
157
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
116
158
|
}
|
|
117
159
|
async function postJson(url, body, headers, opts) {
|
|
118
|
-
|
|
160
|
+
var _a;
|
|
161
|
+
const opName = `POST ${redactUrlForLog(url)}`;
|
|
162
|
+
const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
119
163
|
await withRetry(
|
|
120
164
|
async () => {
|
|
121
165
|
const resp = await fetch(url, {
|
|
@@ -124,7 +168,8 @@ async function postJson(url, body, headers, opts) {
|
|
|
124
168
|
"Content-Type": "application/json",
|
|
125
169
|
...headers
|
|
126
170
|
},
|
|
127
|
-
body: JSON.stringify(body)
|
|
171
|
+
body: JSON.stringify(body),
|
|
172
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
128
173
|
});
|
|
129
174
|
if (!resp.ok) {
|
|
130
175
|
const text = await resp.text().catch(() => "");
|
|
@@ -141,6 +186,27 @@ async function postJson(url, body, headers, opts) {
|
|
|
141
186
|
opts
|
|
142
187
|
);
|
|
143
188
|
}
|
|
189
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
|
|
190
|
+
var TRUNCATION_MARKER = "...[truncated by raindrop]";
|
|
191
|
+
var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
|
|
192
|
+
function resolveMaxTextFieldChars(value) {
|
|
193
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
194
|
+
return Math.floor(value);
|
|
195
|
+
}
|
|
196
|
+
return currentDefaultMaxTextFieldChars;
|
|
197
|
+
}
|
|
198
|
+
function truncateToLimit(text, limit) {
|
|
199
|
+
if (limit > TRUNCATION_MARKER.length) {
|
|
200
|
+
return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
|
|
201
|
+
}
|
|
202
|
+
return text.slice(0, Math.max(0, limit));
|
|
203
|
+
}
|
|
204
|
+
function capText(value, limit) {
|
|
205
|
+
if (typeof value !== "string") return value;
|
|
206
|
+
const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
|
|
207
|
+
if (value.length <= max) return value;
|
|
208
|
+
return truncateToLimit(value, max);
|
|
209
|
+
}
|
|
144
210
|
var SpanStatusCode = {
|
|
145
211
|
UNSET: 0,
|
|
146
212
|
OK: 1,
|
|
@@ -283,6 +349,8 @@ function mirrorPartialEventToLocalDebugger(event, options = {}) {
|
|
|
283
349
|
}).catch(() => {
|
|
284
350
|
});
|
|
285
351
|
}
|
|
352
|
+
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
353
|
+
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
286
354
|
function mergePatches(target, source) {
|
|
287
355
|
var _a, _b, _c, _d;
|
|
288
356
|
const out = { ...target, ...source };
|
|
@@ -300,6 +368,7 @@ var EventShipper = class {
|
|
|
300
368
|
this.sticky = /* @__PURE__ */ new Map();
|
|
301
369
|
this.timers = /* @__PURE__ */ new Map();
|
|
302
370
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
371
|
+
this.hasShutdown = false;
|
|
303
372
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
304
373
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
305
374
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
@@ -309,6 +378,7 @@ var EventShipper = class {
|
|
|
309
378
|
this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
|
|
310
379
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
311
380
|
this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
|
|
381
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
312
382
|
this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
|
|
313
383
|
if (this.debug && this.localDebuggerUrl) {
|
|
314
384
|
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
@@ -331,10 +401,52 @@ var EventShipper = class {
|
|
|
331
401
|
authHeaders() {
|
|
332
402
|
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
333
403
|
}
|
|
404
|
+
/**
|
|
405
|
+
* Build the retry/timeout options for one POST, honoring the shutdown
|
|
406
|
+
* deadline. Returns `null` when the shutdown drain window is exhausted —
|
|
407
|
+
* the caller must drop the payload (with a rate-limited warning) instead
|
|
408
|
+
* of issuing a request that could outlive process exit.
|
|
409
|
+
*
|
|
410
|
+
* Checked fresh on EVERY send, so a shutdown that begins while the flush
|
|
411
|
+
* path is mid-drain takes effect immediately: no further retries, and the
|
|
412
|
+
* per-attempt timeout is clamped to the remaining window. After
|
|
413
|
+
* `shutdown()` returns (deadline cleared, `hasShutdown` still set),
|
|
414
|
+
* sends — late callers, or flush work the deadline abandoned mid-drain —
|
|
415
|
+
* run as a single short attempt rather than regaining the full retry
|
|
416
|
+
* schedule.
|
|
417
|
+
*/
|
|
418
|
+
requestOpts() {
|
|
419
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
420
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
421
|
+
if (remainingMs <= 0) return null;
|
|
422
|
+
return {
|
|
423
|
+
maxAttempts: 1,
|
|
424
|
+
debug: this.debug,
|
|
425
|
+
sdkName: this.sdkName,
|
|
426
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
if (this.hasShutdown) {
|
|
430
|
+
return {
|
|
431
|
+
maxAttempts: 1,
|
|
432
|
+
debug: this.debug,
|
|
433
|
+
sdkName: this.sdkName,
|
|
434
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
438
|
+
}
|
|
334
439
|
async patch(eventId, patch) {
|
|
335
440
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
336
441
|
if (!this.enabled) return;
|
|
337
442
|
if (!eventId || !eventId.trim()) return;
|
|
443
|
+
const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
|
|
444
|
+
if (typeof patch.input === "string" && patch.input.length > maxChars) {
|
|
445
|
+
patch = { ...patch, input: capText(patch.input, maxChars) };
|
|
446
|
+
}
|
|
447
|
+
if (typeof patch.output === "string" && patch.output.length > maxChars) {
|
|
448
|
+
patch = { ...patch, output: capText(patch.output, maxChars) };
|
|
449
|
+
}
|
|
338
450
|
if (this.debug) {
|
|
339
451
|
console.log(`${this.prefix} queue patch`, {
|
|
340
452
|
eventId,
|
|
@@ -381,9 +493,16 @@ var EventShipper = class {
|
|
|
381
493
|
})));
|
|
382
494
|
}
|
|
383
495
|
async shutdown() {
|
|
384
|
-
|
|
385
|
-
this.
|
|
386
|
-
|
|
496
|
+
this.hasShutdown = true;
|
|
497
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
498
|
+
try {
|
|
499
|
+
for (const t of this.timers.values()) clearTimeout(t);
|
|
500
|
+
this.timers.clear();
|
|
501
|
+
const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
|
|
502
|
+
if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
|
|
503
|
+
} finally {
|
|
504
|
+
this.shutdownDeadlineAt = void 0;
|
|
505
|
+
}
|
|
387
506
|
}
|
|
388
507
|
async trackSignal(signal) {
|
|
389
508
|
var _a, _b;
|
|
@@ -405,15 +524,19 @@ var EventShipper = class {
|
|
|
405
524
|
];
|
|
406
525
|
if (!this.writeKey) return;
|
|
407
526
|
const url = `${this.baseUrl}signals/track`;
|
|
527
|
+
const opts = this.requestOpts();
|
|
528
|
+
if (!opts) {
|
|
529
|
+
this.warnShutdownDrop("signal");
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
408
532
|
try {
|
|
409
|
-
await postJson(url, body, this.authHeaders(),
|
|
410
|
-
maxAttempts: 3,
|
|
411
|
-
debug: this.debug,
|
|
412
|
-
sdkName: this.sdkName
|
|
413
|
-
});
|
|
533
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
414
534
|
} catch (err) {
|
|
415
535
|
const msg = err instanceof Error ? err.message : String(err);
|
|
416
|
-
|
|
536
|
+
rateLimitedLog(
|
|
537
|
+
`${this.prefix}.send_signal_failed`,
|
|
538
|
+
() => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
|
|
539
|
+
);
|
|
417
540
|
}
|
|
418
541
|
}
|
|
419
542
|
async identify(users) {
|
|
@@ -437,17 +560,27 @@ var EventShipper = class {
|
|
|
437
560
|
if (!this.writeKey) return;
|
|
438
561
|
if (body.length === 0) return;
|
|
439
562
|
const url = `${this.baseUrl}users/identify`;
|
|
563
|
+
const opts = this.requestOpts();
|
|
564
|
+
if (!opts) {
|
|
565
|
+
this.warnShutdownDrop("identify");
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
440
568
|
try {
|
|
441
|
-
await postJson(url, body, this.authHeaders(),
|
|
442
|
-
maxAttempts: 3,
|
|
443
|
-
debug: this.debug,
|
|
444
|
-
sdkName: this.sdkName
|
|
445
|
-
});
|
|
569
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
446
570
|
} catch (err) {
|
|
447
571
|
const msg = err instanceof Error ? err.message : String(err);
|
|
448
|
-
|
|
572
|
+
rateLimitedLog(
|
|
573
|
+
`${this.prefix}.send_identify_failed`,
|
|
574
|
+
() => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
|
|
575
|
+
);
|
|
449
576
|
}
|
|
450
577
|
}
|
|
578
|
+
warnShutdownDrop(what) {
|
|
579
|
+
rateLimitedLog(
|
|
580
|
+
`${this.prefix}.shutdown_deadline`,
|
|
581
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
|
|
582
|
+
);
|
|
583
|
+
}
|
|
451
584
|
async flushOne(eventId) {
|
|
452
585
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
453
586
|
if (!this.enabled) return;
|
|
@@ -523,11 +656,13 @@ var EventShipper = class {
|
|
|
523
656
|
if (!isPending) this.sticky.delete(eventId);
|
|
524
657
|
return;
|
|
525
658
|
}
|
|
526
|
-
const
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
659
|
+
const opts = this.requestOpts();
|
|
660
|
+
if (!opts) {
|
|
661
|
+
this.warnShutdownDrop(`track_partial ${eventId}`);
|
|
662
|
+
if (!isPending) this.sticky.delete(eventId);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const p = postJson(url, payload, this.authHeaders(), opts);
|
|
531
666
|
this.inFlight.add(p);
|
|
532
667
|
try {
|
|
533
668
|
try {
|
|
@@ -537,7 +672,10 @@ var EventShipper = class {
|
|
|
537
672
|
}
|
|
538
673
|
} catch (err) {
|
|
539
674
|
const msg = err instanceof Error ? err.message : String(err);
|
|
540
|
-
|
|
675
|
+
rateLimitedLog(
|
|
676
|
+
`${this.prefix}.send_track_partial_failed`,
|
|
677
|
+
() => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
|
|
678
|
+
);
|
|
541
679
|
}
|
|
542
680
|
} finally {
|
|
543
681
|
this.inFlight.delete(p);
|
|
@@ -547,10 +685,113 @@ var EventShipper = class {
|
|
|
547
685
|
}
|
|
548
686
|
}
|
|
549
687
|
};
|
|
688
|
+
var DEFAULT_SECRET_KEY_NAMES = [
|
|
689
|
+
"apikey",
|
|
690
|
+
"apisecret",
|
|
691
|
+
"apitoken",
|
|
692
|
+
"secretaccesskey",
|
|
693
|
+
"sessiontoken",
|
|
694
|
+
"privatekey",
|
|
695
|
+
"privatekeyid",
|
|
696
|
+
"clientsecret",
|
|
697
|
+
"accesstoken",
|
|
698
|
+
"refreshtoken",
|
|
699
|
+
"oauthtoken",
|
|
700
|
+
"bearertoken",
|
|
701
|
+
"authorization",
|
|
702
|
+
"password",
|
|
703
|
+
"passphrase"
|
|
704
|
+
];
|
|
705
|
+
var REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
706
|
+
function normalizeKeyName(name) {
|
|
707
|
+
return name.toLowerCase().replace(/[-_.]/g, "");
|
|
708
|
+
}
|
|
709
|
+
function redactSecretsInObject(value, options) {
|
|
710
|
+
var _a, _b;
|
|
711
|
+
const normalizedSecretSet = buildSecretSet((_a = options == null ? void 0 : options.secretKeyNames) != null ? _a : DEFAULT_SECRET_KEY_NAMES);
|
|
712
|
+
const placeholder = (_b = options == null ? void 0 : options.placeholder) != null ? _b : REDACTED_PLACEHOLDER;
|
|
713
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
714
|
+
const walk = (node) => {
|
|
715
|
+
if (node === null || typeof node !== "object") return node;
|
|
716
|
+
if (seen.has(node)) return "[CIRCULAR]";
|
|
717
|
+
seen.add(node);
|
|
718
|
+
if (Array.isArray(node)) {
|
|
719
|
+
return node.map((item) => walk(item));
|
|
720
|
+
}
|
|
721
|
+
const out = {};
|
|
722
|
+
for (const [k, v] of Object.entries(node)) {
|
|
723
|
+
if (normalizedSecretSet.has(normalizeKeyName(k))) {
|
|
724
|
+
out[k] = placeholder;
|
|
725
|
+
} else {
|
|
726
|
+
out[k] = walk(v);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return out;
|
|
730
|
+
};
|
|
731
|
+
return walk(value);
|
|
732
|
+
}
|
|
733
|
+
function buildSecretSet(names) {
|
|
734
|
+
const set = /* @__PURE__ */ new Set();
|
|
735
|
+
for (const name of names) set.add(normalizeKeyName(name));
|
|
736
|
+
return set;
|
|
737
|
+
}
|
|
738
|
+
var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
|
|
739
|
+
"ai.request.providerOptions",
|
|
740
|
+
"ai.response.providerMetadata"
|
|
741
|
+
];
|
|
742
|
+
function defaultTransformSpan(span) {
|
|
743
|
+
const attrs = span.attributes;
|
|
744
|
+
if (!attrs || attrs.length === 0) return span;
|
|
745
|
+
let nextAttrs;
|
|
746
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
747
|
+
const attr = attrs[i];
|
|
748
|
+
const redacted = redactJsonAttributeValue(attr.key, attr.value);
|
|
749
|
+
if (redacted === void 0) continue;
|
|
750
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
751
|
+
nextAttrs[i] = { key: attr.key, value: redacted };
|
|
752
|
+
}
|
|
753
|
+
if (!nextAttrs) return span;
|
|
754
|
+
return { ...span, attributes: nextAttrs };
|
|
755
|
+
}
|
|
756
|
+
var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
|
|
757
|
+
function redactJsonAttributeValue(key, value) {
|
|
758
|
+
if (!REDACT_JSON_ATTRIBUTE_KEYS.has(key)) return void 0;
|
|
759
|
+
const json = value.stringValue;
|
|
760
|
+
if (typeof json !== "string" || json.length === 0) return void 0;
|
|
761
|
+
let parsed;
|
|
762
|
+
try {
|
|
763
|
+
parsed = JSON.parse(json);
|
|
764
|
+
} catch (e) {
|
|
765
|
+
return void 0;
|
|
766
|
+
}
|
|
767
|
+
const scrubbed = redactSecretsInObject(parsed);
|
|
768
|
+
let scrubbedJson;
|
|
769
|
+
try {
|
|
770
|
+
scrubbedJson = JSON.stringify(scrubbed);
|
|
771
|
+
} catch (e) {
|
|
772
|
+
return void 0;
|
|
773
|
+
}
|
|
774
|
+
if (scrubbedJson === json) return void 0;
|
|
775
|
+
return { stringValue: scrubbedJson };
|
|
776
|
+
}
|
|
777
|
+
function applyOtelSpanAttributeLimit(limit) {
|
|
778
|
+
var _a, _b;
|
|
779
|
+
try {
|
|
780
|
+
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;
|
|
781
|
+
if (!raw) return limit;
|
|
782
|
+
const parsed = Number.parseInt(raw, 10);
|
|
783
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
784
|
+
return Math.min(limit, parsed);
|
|
785
|
+
}
|
|
786
|
+
} catch (e) {
|
|
787
|
+
}
|
|
788
|
+
return limit;
|
|
789
|
+
}
|
|
550
790
|
var TraceShipper = class {
|
|
551
791
|
constructor(opts) {
|
|
552
792
|
this.queue = [];
|
|
553
793
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
794
|
+
this.hasShutdown = false;
|
|
554
795
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
555
796
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
556
797
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
@@ -568,6 +809,75 @@ var TraceShipper = class {
|
|
|
568
809
|
if (this.debug && this.localDebuggerUrl) {
|
|
569
810
|
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
570
811
|
}
|
|
812
|
+
this.transformSpanHook = opts.transformSpan;
|
|
813
|
+
this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
|
|
814
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Cap every string attribute value on the span. O(#attributes) length
|
|
818
|
+
* checks; only oversized values pay a slice. Runs AFTER the redaction
|
|
819
|
+
* pipeline so the default secret-scrub still sees parseable JSON in
|
|
820
|
+
* `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
|
|
821
|
+
* first could cut a JSON blob mid-way, fail the parse, and ship secrets
|
|
822
|
+
* in the surviving prefix).
|
|
823
|
+
*
|
|
824
|
+
* A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
|
|
825
|
+
* for span content, matching the Python SDK and the OTel SDK convention.
|
|
826
|
+
*/
|
|
827
|
+
capSpanAttributes(span) {
|
|
828
|
+
var _a;
|
|
829
|
+
const maxChars = applyOtelSpanAttributeLimit(
|
|
830
|
+
resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
|
|
831
|
+
);
|
|
832
|
+
const attrs = span.attributes;
|
|
833
|
+
if (!attrs || attrs.length === 0) return span;
|
|
834
|
+
let nextAttrs;
|
|
835
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
836
|
+
const attr = attrs[i];
|
|
837
|
+
const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
|
|
838
|
+
if (typeof value !== "string" || value.length <= maxChars) continue;
|
|
839
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
840
|
+
nextAttrs[i] = {
|
|
841
|
+
key: attr.key,
|
|
842
|
+
value: { ...attr.value, stringValue: capText(value, maxChars) }
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
if (!nextAttrs) return span;
|
|
846
|
+
return { ...span, attributes: nextAttrs };
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Apply the user `transformSpan` hook (if any) followed by the default
|
|
850
|
+
* redactor (unless disabled). Returns either the (possibly new) span to
|
|
851
|
+
* ship, or `null` to drop the span entirely.
|
|
852
|
+
*
|
|
853
|
+
* Ordering: user hook runs first so callers can rewrite the span freely
|
|
854
|
+
* (rename attrs, add new ones, scrub things the default doesn't know
|
|
855
|
+
* about). The default redactor then runs on whatever the user produced,
|
|
856
|
+
* acting as the always-on floor for documented BYOK secrets. If the user
|
|
857
|
+
* sets `disableDefaultRedaction: true`, the floor is skipped.
|
|
858
|
+
*
|
|
859
|
+
* Fail-closed: if the user hook throws, the span is dropped — a buggy
|
|
860
|
+
* hook can never accidentally ship raw, un-redacted spans.
|
|
861
|
+
*/
|
|
862
|
+
redactSpan(span) {
|
|
863
|
+
let current = span;
|
|
864
|
+
if (this.transformSpanHook) {
|
|
865
|
+
try {
|
|
866
|
+
const result = this.transformSpanHook(current);
|
|
867
|
+
if (result === null) return null;
|
|
868
|
+
if (result !== void 0) current = result;
|
|
869
|
+
} catch (err) {
|
|
870
|
+
if (this.debug) {
|
|
871
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
872
|
+
console.warn(`${this.prefix} transformSpan hook threw: ${msg}`);
|
|
873
|
+
}
|
|
874
|
+
return null;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
if (!this.disableDefaultRedaction) {
|
|
878
|
+
current = defaultTransformSpan(current);
|
|
879
|
+
}
|
|
880
|
+
return this.capSpanAttributes(current);
|
|
571
881
|
}
|
|
572
882
|
isDebugEnabled() {
|
|
573
883
|
return this.debug;
|
|
@@ -585,8 +895,8 @@ var TraceShipper = class {
|
|
|
585
895
|
];
|
|
586
896
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
587
897
|
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
588
|
-
|
|
589
|
-
|
|
898
|
+
this.mirrorToLocalDebugger(
|
|
899
|
+
buildOtlpSpan({
|
|
590
900
|
ids: span.ids,
|
|
591
901
|
name: span.name,
|
|
592
902
|
startTimeUnixNano: span.startTimeUnixNano,
|
|
@@ -594,16 +904,21 @@ var TraceShipper = class {
|
|
|
594
904
|
// placeholder — will be updated on endSpan
|
|
595
905
|
attributes: span.attributes,
|
|
596
906
|
status: { code: SpanStatusCode.UNSET }
|
|
597
|
-
})
|
|
598
|
-
|
|
599
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
600
|
-
baseUrl: this.localDebuggerUrl,
|
|
601
|
-
debug: false,
|
|
602
|
-
sdkName: this.sdkName
|
|
603
|
-
});
|
|
604
|
-
}
|
|
907
|
+
})
|
|
908
|
+
);
|
|
605
909
|
return span;
|
|
606
910
|
}
|
|
911
|
+
mirrorToLocalDebugger(span) {
|
|
912
|
+
if (!this.localDebuggerUrl) return;
|
|
913
|
+
const redacted = this.redactSpan(span);
|
|
914
|
+
if (redacted === null) return;
|
|
915
|
+
const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
|
|
916
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
917
|
+
baseUrl: this.localDebuggerUrl,
|
|
918
|
+
debug: false,
|
|
919
|
+
sdkName: this.sdkName
|
|
920
|
+
});
|
|
921
|
+
}
|
|
607
922
|
endSpan(span, extra) {
|
|
608
923
|
var _a, _b;
|
|
609
924
|
if (span.endTimeUnixNano) return;
|
|
@@ -625,14 +940,7 @@ var TraceShipper = class {
|
|
|
625
940
|
status
|
|
626
941
|
});
|
|
627
942
|
this.enqueue(otlp);
|
|
628
|
-
|
|
629
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
630
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
631
|
-
baseUrl: this.localDebuggerUrl,
|
|
632
|
-
debug: false,
|
|
633
|
-
sdkName: this.sdkName
|
|
634
|
-
});
|
|
635
|
-
}
|
|
943
|
+
this.mirrorToLocalDebugger(otlp);
|
|
636
944
|
}
|
|
637
945
|
createSpan(args) {
|
|
638
946
|
var _a;
|
|
@@ -650,14 +958,7 @@ var TraceShipper = class {
|
|
|
650
958
|
status: args.status
|
|
651
959
|
});
|
|
652
960
|
this.enqueue(otlp);
|
|
653
|
-
|
|
654
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
655
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
656
|
-
baseUrl: this.localDebuggerUrl,
|
|
657
|
-
debug: false,
|
|
658
|
-
sdkName: this.sdkName
|
|
659
|
-
});
|
|
660
|
-
}
|
|
961
|
+
this.mirrorToLocalDebugger(otlp);
|
|
661
962
|
}
|
|
662
963
|
enqueue(span) {
|
|
663
964
|
if (!this.enabled) return;
|
|
@@ -669,10 +970,12 @@ var TraceShipper = class {
|
|
|
669
970
|
)}`
|
|
670
971
|
);
|
|
671
972
|
}
|
|
973
|
+
const redacted = this.redactSpan(span);
|
|
974
|
+
if (redacted === null) return;
|
|
672
975
|
if (this.queue.length >= this.maxQueueSize) {
|
|
673
976
|
this.queue.shift();
|
|
674
977
|
}
|
|
675
|
-
this.queue.push(
|
|
978
|
+
this.queue.push(redacted);
|
|
676
979
|
if (this.queue.length >= this.maxBatchSize) {
|
|
677
980
|
void this.flush().catch(() => {
|
|
678
981
|
});
|
|
@@ -695,6 +998,16 @@ var TraceShipper = class {
|
|
|
695
998
|
while (this.queue.length > 0) {
|
|
696
999
|
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
697
1000
|
if (!this.writeKey) continue;
|
|
1001
|
+
const opts = this.requestOpts();
|
|
1002
|
+
if (!opts) {
|
|
1003
|
+
rateLimitedLog(
|
|
1004
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1005
|
+
() => console.warn(
|
|
1006
|
+
`${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
|
|
1007
|
+
)
|
|
1008
|
+
);
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
698
1011
|
const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
|
|
699
1012
|
const url = `${this.baseUrl}traces`;
|
|
700
1013
|
if (this.debug) {
|
|
@@ -703,11 +1016,7 @@ var TraceShipper = class {
|
|
|
703
1016
|
endpoint: url
|
|
704
1017
|
});
|
|
705
1018
|
}
|
|
706
|
-
const p = postJson(url, body, this.authHeaders(),
|
|
707
|
-
maxAttempts: 3,
|
|
708
|
-
debug: this.debug,
|
|
709
|
-
sdkName: this.sdkName
|
|
710
|
-
});
|
|
1019
|
+
const p = postJson(url, body, this.authHeaders(), opts);
|
|
711
1020
|
this.inFlight.add(p);
|
|
712
1021
|
try {
|
|
713
1022
|
try {
|
|
@@ -715,21 +1024,61 @@ var TraceShipper = class {
|
|
|
715
1024
|
if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
|
|
716
1025
|
} catch (err) {
|
|
717
1026
|
const msg = err instanceof Error ? err.message : String(err);
|
|
718
|
-
|
|
1027
|
+
rateLimitedLog(
|
|
1028
|
+
`${this.prefix}.send_spans_failed`,
|
|
1029
|
+
() => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
|
|
1030
|
+
);
|
|
719
1031
|
}
|
|
720
1032
|
} finally {
|
|
721
1033
|
this.inFlight.delete(p);
|
|
722
1034
|
}
|
|
723
1035
|
}
|
|
724
1036
|
}
|
|
1037
|
+
/** See EventShipper.requestOpts — same shutdown-budget semantics. */
|
|
1038
|
+
requestOpts() {
|
|
1039
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
1040
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
1041
|
+
if (remainingMs <= 0) return null;
|
|
1042
|
+
return {
|
|
1043
|
+
maxAttempts: 1,
|
|
1044
|
+
debug: this.debug,
|
|
1045
|
+
sdkName: this.sdkName,
|
|
1046
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
if (this.hasShutdown) {
|
|
1050
|
+
return {
|
|
1051
|
+
maxAttempts: 1,
|
|
1052
|
+
debug: this.debug,
|
|
1053
|
+
sdkName: this.sdkName,
|
|
1054
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
1058
|
+
}
|
|
725
1059
|
async shutdown() {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
1060
|
+
this.hasShutdown = true;
|
|
1061
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
1062
|
+
try {
|
|
1063
|
+
if (this.timer) {
|
|
1064
|
+
clearTimeout(this.timer);
|
|
1065
|
+
this.timer = void 0;
|
|
1066
|
+
}
|
|
1067
|
+
const drain = async () => {
|
|
1068
|
+
await this.flush();
|
|
1069
|
+
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
1070
|
+
})));
|
|
1071
|
+
};
|
|
1072
|
+
const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
|
|
1073
|
+
if (!settled) {
|
|
1074
|
+
rateLimitedLog(
|
|
1075
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1076
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
} finally {
|
|
1080
|
+
this.shutdownDeadlineAt = void 0;
|
|
729
1081
|
}
|
|
730
|
-
await this.flush();
|
|
731
|
-
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
732
|
-
})));
|
|
733
1082
|
}
|
|
734
1083
|
};
|
|
735
1084
|
|
|
@@ -790,7 +1139,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
790
1139
|
// package.json
|
|
791
1140
|
var package_default = {
|
|
792
1141
|
name: "@raindrop-ai/opencode-plugin",
|
|
793
|
-
version: "0.0.
|
|
1142
|
+
version: "0.0.13",
|
|
794
1143
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
795
1144
|
type: "module",
|
|
796
1145
|
main: "dist/index.js",
|
|
@@ -900,6 +1249,86 @@ var TraceShipper2 = class extends TraceShipper {
|
|
|
900
1249
|
}
|
|
901
1250
|
};
|
|
902
1251
|
|
|
1252
|
+
// src/bounded.ts
|
|
1253
|
+
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1254
|
+
var MAX_TEXT_FIELD_CHARS = 1e6;
|
|
1255
|
+
var MAX_BOUNDED_DEPTH = 12;
|
|
1256
|
+
function truncateToLimit2(text, limit) {
|
|
1257
|
+
if (text.length <= limit) return text;
|
|
1258
|
+
if (limit > TRUNCATION_MARKER2.length) {
|
|
1259
|
+
return text.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
|
|
1260
|
+
}
|
|
1261
|
+
return text.slice(0, limit);
|
|
1262
|
+
}
|
|
1263
|
+
function capText2(value, limit = MAX_TEXT_FIELD_CHARS) {
|
|
1264
|
+
if (value.length <= limit) return value;
|
|
1265
|
+
return truncateToLimit2(value, limit);
|
|
1266
|
+
}
|
|
1267
|
+
function boundedClone2(value, budget, depth) {
|
|
1268
|
+
if (budget.remaining <= 0) return TRUNCATION_MARKER2;
|
|
1269
|
+
if (typeof value === "string") {
|
|
1270
|
+
if (value.length > budget.remaining) {
|
|
1271
|
+
const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
|
|
1272
|
+
budget.remaining = 0;
|
|
1273
|
+
return taken;
|
|
1274
|
+
}
|
|
1275
|
+
budget.remaining -= Math.max(value.length, 1);
|
|
1276
|
+
return value;
|
|
1277
|
+
}
|
|
1278
|
+
if (value === null || typeof value === "number" || typeof value === "boolean") {
|
|
1279
|
+
budget.remaining -= 8;
|
|
1280
|
+
return value;
|
|
1281
|
+
}
|
|
1282
|
+
if (typeof value !== "object") {
|
|
1283
|
+
budget.remaining -= 8;
|
|
1284
|
+
return value;
|
|
1285
|
+
}
|
|
1286
|
+
if (depth >= MAX_BOUNDED_DEPTH) {
|
|
1287
|
+
budget.remaining -= 16;
|
|
1288
|
+
return `<max depth: ${TRUNCATION_MARKER2}>`;
|
|
1289
|
+
}
|
|
1290
|
+
const withToJson = value;
|
|
1291
|
+
if (typeof withToJson.toJSON === "function") {
|
|
1292
|
+
try {
|
|
1293
|
+
return boundedClone2(withToJson.toJSON(), budget, depth + 1);
|
|
1294
|
+
} catch (e) {
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
if (Array.isArray(value)) {
|
|
1298
|
+
const out2 = [];
|
|
1299
|
+
for (const item of value) {
|
|
1300
|
+
if (budget.remaining <= 0) {
|
|
1301
|
+
out2.push(TRUNCATION_MARKER2);
|
|
1302
|
+
break;
|
|
1303
|
+
}
|
|
1304
|
+
out2.push(boundedClone2(item, budget, depth + 1));
|
|
1305
|
+
}
|
|
1306
|
+
return out2;
|
|
1307
|
+
}
|
|
1308
|
+
const out = {};
|
|
1309
|
+
for (const key of Object.keys(value)) {
|
|
1310
|
+
if (budget.remaining <= 0) {
|
|
1311
|
+
out["..."] = TRUNCATION_MARKER2;
|
|
1312
|
+
break;
|
|
1313
|
+
}
|
|
1314
|
+
budget.remaining -= Math.max(key.length, 1);
|
|
1315
|
+
out[key] = boundedClone2(value[key], budget, depth + 1);
|
|
1316
|
+
}
|
|
1317
|
+
return out;
|
|
1318
|
+
}
|
|
1319
|
+
function boundedStringify(value, limit = MAX_TEXT_FIELD_CHARS) {
|
|
1320
|
+
var _a;
|
|
1321
|
+
try {
|
|
1322
|
+
if (typeof value === "string") {
|
|
1323
|
+
return truncateToLimit2(JSON.stringify(capText2(value, limit)), limit);
|
|
1324
|
+
}
|
|
1325
|
+
const pruned = boundedClone2(value, { remaining: limit + TRUNCATION_MARKER2.length + 256 }, 0);
|
|
1326
|
+
return truncateToLimit2((_a = JSON.stringify(pruned)) != null ? _a : "", limit);
|
|
1327
|
+
} catch (e) {
|
|
1328
|
+
return capText2(String(value), limit);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
|
|
903
1332
|
// src/tracing-linkage-helpers.ts
|
|
904
1333
|
function createSessionParentMapHelpers({
|
|
905
1334
|
sessions: sessions2,
|
|
@@ -977,6 +1406,15 @@ function createSessionParentMapHelpers({
|
|
|
977
1406
|
}
|
|
978
1407
|
|
|
979
1408
|
// src/tracing.ts
|
|
1409
|
+
var ERROR_LOG_INTERVAL_MS = 3e4;
|
|
1410
|
+
var lastErrorLogAt = /* @__PURE__ */ new Map();
|
|
1411
|
+
function rateLimitedErrorLog(key, message) {
|
|
1412
|
+
const now = Date.now();
|
|
1413
|
+
const last = lastErrorLogAt.get(key);
|
|
1414
|
+
if (last !== void 0 && now - last < ERROR_LOG_INTERVAL_MS) return;
|
|
1415
|
+
lastErrorLogAt.set(key, now);
|
|
1416
|
+
console.log(message);
|
|
1417
|
+
}
|
|
980
1418
|
var sessions = /* @__PURE__ */ new Map();
|
|
981
1419
|
var taskContexts = /* @__PURE__ */ new Map();
|
|
982
1420
|
var runningTaskCallsBySession = /* @__PURE__ */ new Map();
|
|
@@ -1129,7 +1567,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1129
1567
|
const state = sessions.get(partSessionID);
|
|
1130
1568
|
if (!state) return;
|
|
1131
1569
|
if (partObj["type"] === "text" && typeof partObj["text"] === "string") {
|
|
1132
|
-
state.outputParts.set(messageId, partObj["text"]);
|
|
1570
|
+
state.outputParts.set(messageId, capText2(partObj["text"]));
|
|
1133
1571
|
} else if (partObj["type"] === "tool" && messageId) {
|
|
1134
1572
|
const tool = partObj["tool"];
|
|
1135
1573
|
const callID = partObj["callID"];
|
|
@@ -1159,7 +1597,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1159
1597
|
if (typeof errorMessage !== "string" || errorMessage === "")
|
|
1160
1598
|
throw new Error("message.part.updated: error tool state must have error string");
|
|
1161
1599
|
const input = toolState["input"];
|
|
1162
|
-
const toolCallArgs = input === void 0 ? void 0 : typeof input === "string" ? input :
|
|
1600
|
+
const toolCallArgs = input === void 0 ? void 0 : typeof input === "string" ? capText2(input) : boundedStringify(input);
|
|
1163
1601
|
if (endPendingToolSpanWithError(
|
|
1164
1602
|
state,
|
|
1165
1603
|
callID,
|
|
@@ -1172,7 +1610,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1172
1610
|
}
|
|
1173
1611
|
}
|
|
1174
1612
|
} else if (partObj["type"] === "reasoning" && typeof partObj["text"] === "string") {
|
|
1175
|
-
state.reasoningParts.set(messageId, partObj["text"]);
|
|
1613
|
+
state.reasoningParts.set(messageId, capText2(partObj["text"]));
|
|
1176
1614
|
}
|
|
1177
1615
|
} else if (event.type === "message.updated") {
|
|
1178
1616
|
const msgInfo = props["info"];
|
|
@@ -1439,7 +1877,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1439
1877
|
sessions.delete(String(errorSessionID));
|
|
1440
1878
|
}
|
|
1441
1879
|
} catch (err) {
|
|
1442
|
-
|
|
1880
|
+
rateLimitedErrorLog(
|
|
1881
|
+
"event",
|
|
1443
1882
|
`[raindrop-ai/opencode-plugin] [error] Error in event hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1444
1883
|
);
|
|
1445
1884
|
}
|
|
@@ -1495,7 +1934,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1495
1934
|
...promptMeta,
|
|
1496
1935
|
properties: baseMeta.properties || promptMeta.properties ? { ...(_a = baseMeta.properties) != null ? _a : {}, ...(_b = promptMeta.properties) != null ? _b : {} } : void 0
|
|
1497
1936
|
};
|
|
1498
|
-
const userText = textParts.join("\n");
|
|
1937
|
+
const userText = capText2(textParts.join("\n"));
|
|
1499
1938
|
state.currentInput = userText;
|
|
1500
1939
|
if (state.parentId) {
|
|
1501
1940
|
if (!state.parentTaskSpanIds || !state.parentEventContext) {
|
|
@@ -1604,7 +2043,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1604
2043
|
});
|
|
1605
2044
|
}
|
|
1606
2045
|
} catch (err) {
|
|
1607
|
-
|
|
2046
|
+
rateLimitedErrorLog(
|
|
2047
|
+
"tool.execute.before",
|
|
1608
2048
|
`[raindrop-ai/opencode-plugin] [error] Error in tool.execute.before hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1609
2049
|
);
|
|
1610
2050
|
}
|
|
@@ -1619,7 +2059,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1619
2059
|
if (!state || !state.currentEventId) return;
|
|
1620
2060
|
if (args === void 0 || args === null)
|
|
1621
2061
|
throw new Error("tool.execute.after: args is required");
|
|
1622
|
-
const toolCallArgs = typeof args === "string" ? args :
|
|
2062
|
+
const toolCallArgs = typeof args === "string" ? capText2(args) : boundedStringify(args);
|
|
1623
2063
|
const startInfo = state.toolSpanStarts.get(callID);
|
|
1624
2064
|
state.toolSpanStarts.delete(callID);
|
|
1625
2065
|
const resultMetadata = result.metadata;
|
|
@@ -1632,13 +2072,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1632
2072
|
const endTimeUnixNano = nowUnixNanoString();
|
|
1633
2073
|
if (!("output" in result) || result.output === void 0)
|
|
1634
2074
|
throw new Error("tool.execute.after: result.output is required");
|
|
1635
|
-
|
|
1636
|
-
try {
|
|
1637
|
-
toolResult = JSON.stringify(result.output);
|
|
1638
|
-
} catch (err) {
|
|
1639
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1640
|
-
throw new Error(`tool.execute.after: result.output must be JSON-serializable: ${msg}`);
|
|
1641
|
-
}
|
|
2075
|
+
const toolResult = boundedStringify(result.output);
|
|
1642
2076
|
if (startInfo.liveSpan) {
|
|
1643
2077
|
traceShipper.endSpan(startInfo.liveSpan, {
|
|
1644
2078
|
attributes: [
|
|
@@ -1667,7 +2101,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1667
2101
|
markTaskCallFinished(sessionID, callID);
|
|
1668
2102
|
}
|
|
1669
2103
|
} catch (err) {
|
|
1670
|
-
|
|
2104
|
+
rateLimitedErrorLog(
|
|
2105
|
+
"tool.execute.after",
|
|
1671
2106
|
`[raindrop-ai/opencode-plugin] [error] Error in tool.execute.after hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1672
2107
|
);
|
|
1673
2108
|
}
|
|
@@ -1684,7 +2119,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1684
2119
|
state.isCompacting = true;
|
|
1685
2120
|
}
|
|
1686
2121
|
} catch (err) {
|
|
1687
|
-
|
|
2122
|
+
rateLimitedErrorLog(
|
|
2123
|
+
"experimental.session.compacting",
|
|
1688
2124
|
`[raindrop-ai/opencode-plugin] [error] Error in experimental.session.compacting hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1689
2125
|
);
|
|
1690
2126
|
}
|
|
@@ -1703,11 +2139,12 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1703
2139
|
sessions.set(sessionID, state);
|
|
1704
2140
|
}
|
|
1705
2141
|
const MAX_SYSTEM_PROMPT_LENGTH = 32768;
|
|
1706
|
-
const
|
|
2142
|
+
const TRUNCATION_MARKER3 = "\n...[truncated]";
|
|
1707
2143
|
const joined = output.system.join("\n\n");
|
|
1708
|
-
state.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH -
|
|
2144
|
+
state.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH - TRUNCATION_MARKER3.length) + TRUNCATION_MARKER3 : joined;
|
|
1709
2145
|
} catch (err) {
|
|
1710
|
-
|
|
2146
|
+
rateLimitedErrorLog(
|
|
2147
|
+
"experimental.chat.system.transform",
|
|
1711
2148
|
`[raindrop-ai/opencode-plugin] [error] Error in experimental.chat.system.transform hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1712
2149
|
);
|
|
1713
2150
|
}
|