@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.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;
|
|
@@ -71,6 +71,8 @@ function base64Encode(bytes) {
|
|
|
71
71
|
function generateId() {
|
|
72
72
|
return base64Encode(randomBytes(12)).replace(/[+/=]/g, "").slice(0, 16);
|
|
73
73
|
}
|
|
74
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
75
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
74
76
|
function wait(ms) {
|
|
75
77
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
76
78
|
}
|
|
@@ -78,6 +80,46 @@ function formatEndpoint(endpoint) {
|
|
|
78
80
|
if (!endpoint) return void 0;
|
|
79
81
|
return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
|
80
82
|
}
|
|
83
|
+
function redactUrlForLog(url) {
|
|
84
|
+
try {
|
|
85
|
+
const parsed = new URL(url);
|
|
86
|
+
parsed.username = "";
|
|
87
|
+
parsed.password = "";
|
|
88
|
+
parsed.search = "";
|
|
89
|
+
parsed.hash = "";
|
|
90
|
+
return parsed.toString();
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return "<unparseable-url>";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
|
|
96
|
+
var rateLimitedLogLast = /* @__PURE__ */ new Map();
|
|
97
|
+
function rateLimitedLog(key, log) {
|
|
98
|
+
const now = Date.now();
|
|
99
|
+
const last = rateLimitedLogLast.get(key);
|
|
100
|
+
if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
rateLimitedLogLast.set(key, now);
|
|
104
|
+
log();
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
async function raceWithTimeout(promise, timeoutMs) {
|
|
108
|
+
let timer;
|
|
109
|
+
const settledInTime = await Promise.race([
|
|
110
|
+
promise.then(
|
|
111
|
+
() => true,
|
|
112
|
+
() => true
|
|
113
|
+
),
|
|
114
|
+
new Promise((resolve) => {
|
|
115
|
+
var _a;
|
|
116
|
+
timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
|
|
117
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
|
118
|
+
})
|
|
119
|
+
]);
|
|
120
|
+
if (timer) clearTimeout(timer);
|
|
121
|
+
return settledInTime;
|
|
122
|
+
}
|
|
81
123
|
function parseRetryAfter(headers) {
|
|
82
124
|
var _a;
|
|
83
125
|
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
@@ -94,12 +136,12 @@ function parseRetryAfter(headers) {
|
|
|
94
136
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
95
137
|
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
96
138
|
const v = previousError.retryAfterMs;
|
|
97
|
-
if (typeof v === "number") return Math.max(0, v);
|
|
139
|
+
if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
|
|
98
140
|
}
|
|
99
141
|
if (attemptNumber <= 1) return 0;
|
|
100
142
|
const base = 500;
|
|
101
143
|
const factor = Math.pow(2, attemptNumber - 2);
|
|
102
|
-
return base * factor;
|
|
144
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
103
145
|
}
|
|
104
146
|
async function withRetry(operation, opName, opts) {
|
|
105
147
|
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
@@ -134,7 +176,9 @@ async function withRetry(operation, opName, opts) {
|
|
|
134
176
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
135
177
|
}
|
|
136
178
|
async function postJson(url, body, headers, opts) {
|
|
137
|
-
|
|
179
|
+
var _a;
|
|
180
|
+
const opName = `POST ${redactUrlForLog(url)}`;
|
|
181
|
+
const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
138
182
|
await withRetry(
|
|
139
183
|
async () => {
|
|
140
184
|
const resp = await fetch(url, {
|
|
@@ -143,7 +187,8 @@ async function postJson(url, body, headers, opts) {
|
|
|
143
187
|
"Content-Type": "application/json",
|
|
144
188
|
...headers
|
|
145
189
|
},
|
|
146
|
-
body: JSON.stringify(body)
|
|
190
|
+
body: JSON.stringify(body),
|
|
191
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
147
192
|
});
|
|
148
193
|
if (!resp.ok) {
|
|
149
194
|
const text = await resp.text().catch(() => "");
|
|
@@ -160,6 +205,27 @@ async function postJson(url, body, headers, opts) {
|
|
|
160
205
|
opts
|
|
161
206
|
);
|
|
162
207
|
}
|
|
208
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
|
|
209
|
+
var TRUNCATION_MARKER = "...[truncated by raindrop]";
|
|
210
|
+
var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
|
|
211
|
+
function resolveMaxTextFieldChars(value) {
|
|
212
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
213
|
+
return Math.floor(value);
|
|
214
|
+
}
|
|
215
|
+
return currentDefaultMaxTextFieldChars;
|
|
216
|
+
}
|
|
217
|
+
function truncateToLimit(text, limit) {
|
|
218
|
+
if (limit > TRUNCATION_MARKER.length) {
|
|
219
|
+
return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
|
|
220
|
+
}
|
|
221
|
+
return text.slice(0, Math.max(0, limit));
|
|
222
|
+
}
|
|
223
|
+
function capText(value, limit) {
|
|
224
|
+
if (typeof value !== "string") return value;
|
|
225
|
+
const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
|
|
226
|
+
if (value.length <= max) return value;
|
|
227
|
+
return truncateToLimit(value, max);
|
|
228
|
+
}
|
|
163
229
|
var SpanStatusCode = {
|
|
164
230
|
UNSET: 0,
|
|
165
231
|
OK: 1,
|
|
@@ -302,6 +368,8 @@ function mirrorPartialEventToLocalDebugger(event, options = {}) {
|
|
|
302
368
|
}).catch(() => {
|
|
303
369
|
});
|
|
304
370
|
}
|
|
371
|
+
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
372
|
+
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
305
373
|
function mergePatches(target, source) {
|
|
306
374
|
var _a, _b, _c, _d;
|
|
307
375
|
const out = { ...target, ...source };
|
|
@@ -319,6 +387,7 @@ var EventShipper = class {
|
|
|
319
387
|
this.sticky = /* @__PURE__ */ new Map();
|
|
320
388
|
this.timers = /* @__PURE__ */ new Map();
|
|
321
389
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
390
|
+
this.hasShutdown = false;
|
|
322
391
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
323
392
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
324
393
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
@@ -328,6 +397,7 @@ var EventShipper = class {
|
|
|
328
397
|
this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
|
|
329
398
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
330
399
|
this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
|
|
400
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
331
401
|
this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
|
|
332
402
|
if (this.debug && this.localDebuggerUrl) {
|
|
333
403
|
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
@@ -350,10 +420,52 @@ var EventShipper = class {
|
|
|
350
420
|
authHeaders() {
|
|
351
421
|
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
352
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* Build the retry/timeout options for one POST, honoring the shutdown
|
|
425
|
+
* deadline. Returns `null` when the shutdown drain window is exhausted —
|
|
426
|
+
* the caller must drop the payload (with a rate-limited warning) instead
|
|
427
|
+
* of issuing a request that could outlive process exit.
|
|
428
|
+
*
|
|
429
|
+
* Checked fresh on EVERY send, so a shutdown that begins while the flush
|
|
430
|
+
* path is mid-drain takes effect immediately: no further retries, and the
|
|
431
|
+
* per-attempt timeout is clamped to the remaining window. After
|
|
432
|
+
* `shutdown()` returns (deadline cleared, `hasShutdown` still set),
|
|
433
|
+
* sends — late callers, or flush work the deadline abandoned mid-drain —
|
|
434
|
+
* run as a single short attempt rather than regaining the full retry
|
|
435
|
+
* schedule.
|
|
436
|
+
*/
|
|
437
|
+
requestOpts() {
|
|
438
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
439
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
440
|
+
if (remainingMs <= 0) return null;
|
|
441
|
+
return {
|
|
442
|
+
maxAttempts: 1,
|
|
443
|
+
debug: this.debug,
|
|
444
|
+
sdkName: this.sdkName,
|
|
445
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
if (this.hasShutdown) {
|
|
449
|
+
return {
|
|
450
|
+
maxAttempts: 1,
|
|
451
|
+
debug: this.debug,
|
|
452
|
+
sdkName: this.sdkName,
|
|
453
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
457
|
+
}
|
|
353
458
|
async patch(eventId, patch) {
|
|
354
459
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
355
460
|
if (!this.enabled) return;
|
|
356
461
|
if (!eventId || !eventId.trim()) return;
|
|
462
|
+
const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
|
|
463
|
+
if (typeof patch.input === "string" && patch.input.length > maxChars) {
|
|
464
|
+
patch = { ...patch, input: capText(patch.input, maxChars) };
|
|
465
|
+
}
|
|
466
|
+
if (typeof patch.output === "string" && patch.output.length > maxChars) {
|
|
467
|
+
patch = { ...patch, output: capText(patch.output, maxChars) };
|
|
468
|
+
}
|
|
357
469
|
if (this.debug) {
|
|
358
470
|
console.log(`${this.prefix} queue patch`, {
|
|
359
471
|
eventId,
|
|
@@ -400,9 +512,16 @@ var EventShipper = class {
|
|
|
400
512
|
})));
|
|
401
513
|
}
|
|
402
514
|
async shutdown() {
|
|
403
|
-
|
|
404
|
-
this.
|
|
405
|
-
|
|
515
|
+
this.hasShutdown = true;
|
|
516
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
517
|
+
try {
|
|
518
|
+
for (const t of this.timers.values()) clearTimeout(t);
|
|
519
|
+
this.timers.clear();
|
|
520
|
+
const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
|
|
521
|
+
if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
|
|
522
|
+
} finally {
|
|
523
|
+
this.shutdownDeadlineAt = void 0;
|
|
524
|
+
}
|
|
406
525
|
}
|
|
407
526
|
async trackSignal(signal) {
|
|
408
527
|
var _a, _b;
|
|
@@ -424,15 +543,19 @@ var EventShipper = class {
|
|
|
424
543
|
];
|
|
425
544
|
if (!this.writeKey) return;
|
|
426
545
|
const url = `${this.baseUrl}signals/track`;
|
|
546
|
+
const opts = this.requestOpts();
|
|
547
|
+
if (!opts) {
|
|
548
|
+
this.warnShutdownDrop("signal");
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
427
551
|
try {
|
|
428
|
-
await postJson(url, body, this.authHeaders(),
|
|
429
|
-
maxAttempts: 3,
|
|
430
|
-
debug: this.debug,
|
|
431
|
-
sdkName: this.sdkName
|
|
432
|
-
});
|
|
552
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
433
553
|
} catch (err) {
|
|
434
554
|
const msg = err instanceof Error ? err.message : String(err);
|
|
435
|
-
|
|
555
|
+
rateLimitedLog(
|
|
556
|
+
`${this.prefix}.send_signal_failed`,
|
|
557
|
+
() => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
|
|
558
|
+
);
|
|
436
559
|
}
|
|
437
560
|
}
|
|
438
561
|
async identify(users) {
|
|
@@ -456,17 +579,27 @@ var EventShipper = class {
|
|
|
456
579
|
if (!this.writeKey) return;
|
|
457
580
|
if (body.length === 0) return;
|
|
458
581
|
const url = `${this.baseUrl}users/identify`;
|
|
582
|
+
const opts = this.requestOpts();
|
|
583
|
+
if (!opts) {
|
|
584
|
+
this.warnShutdownDrop("identify");
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
459
587
|
try {
|
|
460
|
-
await postJson(url, body, this.authHeaders(),
|
|
461
|
-
maxAttempts: 3,
|
|
462
|
-
debug: this.debug,
|
|
463
|
-
sdkName: this.sdkName
|
|
464
|
-
});
|
|
588
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
465
589
|
} catch (err) {
|
|
466
590
|
const msg = err instanceof Error ? err.message : String(err);
|
|
467
|
-
|
|
591
|
+
rateLimitedLog(
|
|
592
|
+
`${this.prefix}.send_identify_failed`,
|
|
593
|
+
() => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
|
|
594
|
+
);
|
|
468
595
|
}
|
|
469
596
|
}
|
|
597
|
+
warnShutdownDrop(what) {
|
|
598
|
+
rateLimitedLog(
|
|
599
|
+
`${this.prefix}.shutdown_deadline`,
|
|
600
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
|
|
601
|
+
);
|
|
602
|
+
}
|
|
470
603
|
async flushOne(eventId) {
|
|
471
604
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
472
605
|
if (!this.enabled) return;
|
|
@@ -542,11 +675,13 @@ var EventShipper = class {
|
|
|
542
675
|
if (!isPending) this.sticky.delete(eventId);
|
|
543
676
|
return;
|
|
544
677
|
}
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
678
|
+
const opts = this.requestOpts();
|
|
679
|
+
if (!opts) {
|
|
680
|
+
this.warnShutdownDrop(`track_partial ${eventId}`);
|
|
681
|
+
if (!isPending) this.sticky.delete(eventId);
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
const p = postJson(url, payload, this.authHeaders(), opts);
|
|
550
685
|
this.inFlight.add(p);
|
|
551
686
|
try {
|
|
552
687
|
try {
|
|
@@ -556,7 +691,10 @@ var EventShipper = class {
|
|
|
556
691
|
}
|
|
557
692
|
} catch (err) {
|
|
558
693
|
const msg = err instanceof Error ? err.message : String(err);
|
|
559
|
-
|
|
694
|
+
rateLimitedLog(
|
|
695
|
+
`${this.prefix}.send_track_partial_failed`,
|
|
696
|
+
() => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
|
|
697
|
+
);
|
|
560
698
|
}
|
|
561
699
|
} finally {
|
|
562
700
|
this.inFlight.delete(p);
|
|
@@ -566,10 +704,113 @@ var EventShipper = class {
|
|
|
566
704
|
}
|
|
567
705
|
}
|
|
568
706
|
};
|
|
707
|
+
var DEFAULT_SECRET_KEY_NAMES = [
|
|
708
|
+
"apikey",
|
|
709
|
+
"apisecret",
|
|
710
|
+
"apitoken",
|
|
711
|
+
"secretaccesskey",
|
|
712
|
+
"sessiontoken",
|
|
713
|
+
"privatekey",
|
|
714
|
+
"privatekeyid",
|
|
715
|
+
"clientsecret",
|
|
716
|
+
"accesstoken",
|
|
717
|
+
"refreshtoken",
|
|
718
|
+
"oauthtoken",
|
|
719
|
+
"bearertoken",
|
|
720
|
+
"authorization",
|
|
721
|
+
"password",
|
|
722
|
+
"passphrase"
|
|
723
|
+
];
|
|
724
|
+
var REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
725
|
+
function normalizeKeyName(name) {
|
|
726
|
+
return name.toLowerCase().replace(/[-_.]/g, "");
|
|
727
|
+
}
|
|
728
|
+
function redactSecretsInObject(value, options) {
|
|
729
|
+
var _a, _b;
|
|
730
|
+
const normalizedSecretSet = buildSecretSet((_a = options == null ? void 0 : options.secretKeyNames) != null ? _a : DEFAULT_SECRET_KEY_NAMES);
|
|
731
|
+
const placeholder = (_b = options == null ? void 0 : options.placeholder) != null ? _b : REDACTED_PLACEHOLDER;
|
|
732
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
733
|
+
const walk = (node) => {
|
|
734
|
+
if (node === null || typeof node !== "object") return node;
|
|
735
|
+
if (seen.has(node)) return "[CIRCULAR]";
|
|
736
|
+
seen.add(node);
|
|
737
|
+
if (Array.isArray(node)) {
|
|
738
|
+
return node.map((item) => walk(item));
|
|
739
|
+
}
|
|
740
|
+
const out = {};
|
|
741
|
+
for (const [k, v] of Object.entries(node)) {
|
|
742
|
+
if (normalizedSecretSet.has(normalizeKeyName(k))) {
|
|
743
|
+
out[k] = placeholder;
|
|
744
|
+
} else {
|
|
745
|
+
out[k] = walk(v);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return out;
|
|
749
|
+
};
|
|
750
|
+
return walk(value);
|
|
751
|
+
}
|
|
752
|
+
function buildSecretSet(names) {
|
|
753
|
+
const set = /* @__PURE__ */ new Set();
|
|
754
|
+
for (const name of names) set.add(normalizeKeyName(name));
|
|
755
|
+
return set;
|
|
756
|
+
}
|
|
757
|
+
var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
|
|
758
|
+
"ai.request.providerOptions",
|
|
759
|
+
"ai.response.providerMetadata"
|
|
760
|
+
];
|
|
761
|
+
function defaultTransformSpan(span) {
|
|
762
|
+
const attrs = span.attributes;
|
|
763
|
+
if (!attrs || attrs.length === 0) return span;
|
|
764
|
+
let nextAttrs;
|
|
765
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
766
|
+
const attr = attrs[i];
|
|
767
|
+
const redacted = redactJsonAttributeValue(attr.key, attr.value);
|
|
768
|
+
if (redacted === void 0) continue;
|
|
769
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
770
|
+
nextAttrs[i] = { key: attr.key, value: redacted };
|
|
771
|
+
}
|
|
772
|
+
if (!nextAttrs) return span;
|
|
773
|
+
return { ...span, attributes: nextAttrs };
|
|
774
|
+
}
|
|
775
|
+
var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
|
|
776
|
+
function redactJsonAttributeValue(key, value) {
|
|
777
|
+
if (!REDACT_JSON_ATTRIBUTE_KEYS.has(key)) return void 0;
|
|
778
|
+
const json = value.stringValue;
|
|
779
|
+
if (typeof json !== "string" || json.length === 0) return void 0;
|
|
780
|
+
let parsed;
|
|
781
|
+
try {
|
|
782
|
+
parsed = JSON.parse(json);
|
|
783
|
+
} catch (e) {
|
|
784
|
+
return void 0;
|
|
785
|
+
}
|
|
786
|
+
const scrubbed = redactSecretsInObject(parsed);
|
|
787
|
+
let scrubbedJson;
|
|
788
|
+
try {
|
|
789
|
+
scrubbedJson = JSON.stringify(scrubbed);
|
|
790
|
+
} catch (e) {
|
|
791
|
+
return void 0;
|
|
792
|
+
}
|
|
793
|
+
if (scrubbedJson === json) return void 0;
|
|
794
|
+
return { stringValue: scrubbedJson };
|
|
795
|
+
}
|
|
796
|
+
function applyOtelSpanAttributeLimit(limit) {
|
|
797
|
+
var _a, _b;
|
|
798
|
+
try {
|
|
799
|
+
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;
|
|
800
|
+
if (!raw) return limit;
|
|
801
|
+
const parsed = Number.parseInt(raw, 10);
|
|
802
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
803
|
+
return Math.min(limit, parsed);
|
|
804
|
+
}
|
|
805
|
+
} catch (e) {
|
|
806
|
+
}
|
|
807
|
+
return limit;
|
|
808
|
+
}
|
|
569
809
|
var TraceShipper = class {
|
|
570
810
|
constructor(opts) {
|
|
571
811
|
this.queue = [];
|
|
572
812
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
813
|
+
this.hasShutdown = false;
|
|
573
814
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
574
815
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
575
816
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
@@ -587,6 +828,75 @@ var TraceShipper = class {
|
|
|
587
828
|
if (this.debug && this.localDebuggerUrl) {
|
|
588
829
|
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
589
830
|
}
|
|
831
|
+
this.transformSpanHook = opts.transformSpan;
|
|
832
|
+
this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
|
|
833
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Cap every string attribute value on the span. O(#attributes) length
|
|
837
|
+
* checks; only oversized values pay a slice. Runs AFTER the redaction
|
|
838
|
+
* pipeline so the default secret-scrub still sees parseable JSON in
|
|
839
|
+
* `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
|
|
840
|
+
* first could cut a JSON blob mid-way, fail the parse, and ship secrets
|
|
841
|
+
* in the surviving prefix).
|
|
842
|
+
*
|
|
843
|
+
* A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
|
|
844
|
+
* for span content, matching the Python SDK and the OTel SDK convention.
|
|
845
|
+
*/
|
|
846
|
+
capSpanAttributes(span) {
|
|
847
|
+
var _a;
|
|
848
|
+
const maxChars = applyOtelSpanAttributeLimit(
|
|
849
|
+
resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
|
|
850
|
+
);
|
|
851
|
+
const attrs = span.attributes;
|
|
852
|
+
if (!attrs || attrs.length === 0) return span;
|
|
853
|
+
let nextAttrs;
|
|
854
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
855
|
+
const attr = attrs[i];
|
|
856
|
+
const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
|
|
857
|
+
if (typeof value !== "string" || value.length <= maxChars) continue;
|
|
858
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
859
|
+
nextAttrs[i] = {
|
|
860
|
+
key: attr.key,
|
|
861
|
+
value: { ...attr.value, stringValue: capText(value, maxChars) }
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
if (!nextAttrs) return span;
|
|
865
|
+
return { ...span, attributes: nextAttrs };
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Apply the user `transformSpan` hook (if any) followed by the default
|
|
869
|
+
* redactor (unless disabled). Returns either the (possibly new) span to
|
|
870
|
+
* ship, or `null` to drop the span entirely.
|
|
871
|
+
*
|
|
872
|
+
* Ordering: user hook runs first so callers can rewrite the span freely
|
|
873
|
+
* (rename attrs, add new ones, scrub things the default doesn't know
|
|
874
|
+
* about). The default redactor then runs on whatever the user produced,
|
|
875
|
+
* acting as the always-on floor for documented BYOK secrets. If the user
|
|
876
|
+
* sets `disableDefaultRedaction: true`, the floor is skipped.
|
|
877
|
+
*
|
|
878
|
+
* Fail-closed: if the user hook throws, the span is dropped — a buggy
|
|
879
|
+
* hook can never accidentally ship raw, un-redacted spans.
|
|
880
|
+
*/
|
|
881
|
+
redactSpan(span) {
|
|
882
|
+
let current = span;
|
|
883
|
+
if (this.transformSpanHook) {
|
|
884
|
+
try {
|
|
885
|
+
const result = this.transformSpanHook(current);
|
|
886
|
+
if (result === null) return null;
|
|
887
|
+
if (result !== void 0) current = result;
|
|
888
|
+
} catch (err) {
|
|
889
|
+
if (this.debug) {
|
|
890
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
891
|
+
console.warn(`${this.prefix} transformSpan hook threw: ${msg}`);
|
|
892
|
+
}
|
|
893
|
+
return null;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (!this.disableDefaultRedaction) {
|
|
897
|
+
current = defaultTransformSpan(current);
|
|
898
|
+
}
|
|
899
|
+
return this.capSpanAttributes(current);
|
|
590
900
|
}
|
|
591
901
|
isDebugEnabled() {
|
|
592
902
|
return this.debug;
|
|
@@ -604,8 +914,8 @@ var TraceShipper = class {
|
|
|
604
914
|
];
|
|
605
915
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
606
916
|
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
607
|
-
|
|
608
|
-
|
|
917
|
+
this.mirrorToLocalDebugger(
|
|
918
|
+
buildOtlpSpan({
|
|
609
919
|
ids: span.ids,
|
|
610
920
|
name: span.name,
|
|
611
921
|
startTimeUnixNano: span.startTimeUnixNano,
|
|
@@ -613,16 +923,21 @@ var TraceShipper = class {
|
|
|
613
923
|
// placeholder — will be updated on endSpan
|
|
614
924
|
attributes: span.attributes,
|
|
615
925
|
status: { code: SpanStatusCode.UNSET }
|
|
616
|
-
})
|
|
617
|
-
|
|
618
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
619
|
-
baseUrl: this.localDebuggerUrl,
|
|
620
|
-
debug: false,
|
|
621
|
-
sdkName: this.sdkName
|
|
622
|
-
});
|
|
623
|
-
}
|
|
926
|
+
})
|
|
927
|
+
);
|
|
624
928
|
return span;
|
|
625
929
|
}
|
|
930
|
+
mirrorToLocalDebugger(span) {
|
|
931
|
+
if (!this.localDebuggerUrl) return;
|
|
932
|
+
const redacted = this.redactSpan(span);
|
|
933
|
+
if (redacted === null) return;
|
|
934
|
+
const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
|
|
935
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
936
|
+
baseUrl: this.localDebuggerUrl,
|
|
937
|
+
debug: false,
|
|
938
|
+
sdkName: this.sdkName
|
|
939
|
+
});
|
|
940
|
+
}
|
|
626
941
|
endSpan(span, extra) {
|
|
627
942
|
var _a, _b;
|
|
628
943
|
if (span.endTimeUnixNano) return;
|
|
@@ -644,14 +959,7 @@ var TraceShipper = class {
|
|
|
644
959
|
status
|
|
645
960
|
});
|
|
646
961
|
this.enqueue(otlp);
|
|
647
|
-
|
|
648
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
649
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
650
|
-
baseUrl: this.localDebuggerUrl,
|
|
651
|
-
debug: false,
|
|
652
|
-
sdkName: this.sdkName
|
|
653
|
-
});
|
|
654
|
-
}
|
|
962
|
+
this.mirrorToLocalDebugger(otlp);
|
|
655
963
|
}
|
|
656
964
|
createSpan(args) {
|
|
657
965
|
var _a;
|
|
@@ -669,14 +977,7 @@ var TraceShipper = class {
|
|
|
669
977
|
status: args.status
|
|
670
978
|
});
|
|
671
979
|
this.enqueue(otlp);
|
|
672
|
-
|
|
673
|
-
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
674
|
-
mirrorTraceExportToLocalDebugger(body, {
|
|
675
|
-
baseUrl: this.localDebuggerUrl,
|
|
676
|
-
debug: false,
|
|
677
|
-
sdkName: this.sdkName
|
|
678
|
-
});
|
|
679
|
-
}
|
|
980
|
+
this.mirrorToLocalDebugger(otlp);
|
|
680
981
|
}
|
|
681
982
|
enqueue(span) {
|
|
682
983
|
if (!this.enabled) return;
|
|
@@ -688,10 +989,12 @@ var TraceShipper = class {
|
|
|
688
989
|
)}`
|
|
689
990
|
);
|
|
690
991
|
}
|
|
992
|
+
const redacted = this.redactSpan(span);
|
|
993
|
+
if (redacted === null) return;
|
|
691
994
|
if (this.queue.length >= this.maxQueueSize) {
|
|
692
995
|
this.queue.shift();
|
|
693
996
|
}
|
|
694
|
-
this.queue.push(
|
|
997
|
+
this.queue.push(redacted);
|
|
695
998
|
if (this.queue.length >= this.maxBatchSize) {
|
|
696
999
|
void this.flush().catch(() => {
|
|
697
1000
|
});
|
|
@@ -714,6 +1017,16 @@ var TraceShipper = class {
|
|
|
714
1017
|
while (this.queue.length > 0) {
|
|
715
1018
|
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
716
1019
|
if (!this.writeKey) continue;
|
|
1020
|
+
const opts = this.requestOpts();
|
|
1021
|
+
if (!opts) {
|
|
1022
|
+
rateLimitedLog(
|
|
1023
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1024
|
+
() => console.warn(
|
|
1025
|
+
`${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
|
|
1026
|
+
)
|
|
1027
|
+
);
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
717
1030
|
const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
|
|
718
1031
|
const url = `${this.baseUrl}traces`;
|
|
719
1032
|
if (this.debug) {
|
|
@@ -722,11 +1035,7 @@ var TraceShipper = class {
|
|
|
722
1035
|
endpoint: url
|
|
723
1036
|
});
|
|
724
1037
|
}
|
|
725
|
-
const p = postJson(url, body, this.authHeaders(),
|
|
726
|
-
maxAttempts: 3,
|
|
727
|
-
debug: this.debug,
|
|
728
|
-
sdkName: this.sdkName
|
|
729
|
-
});
|
|
1038
|
+
const p = postJson(url, body, this.authHeaders(), opts);
|
|
730
1039
|
this.inFlight.add(p);
|
|
731
1040
|
try {
|
|
732
1041
|
try {
|
|
@@ -734,21 +1043,61 @@ var TraceShipper = class {
|
|
|
734
1043
|
if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
|
|
735
1044
|
} catch (err) {
|
|
736
1045
|
const msg = err instanceof Error ? err.message : String(err);
|
|
737
|
-
|
|
1046
|
+
rateLimitedLog(
|
|
1047
|
+
`${this.prefix}.send_spans_failed`,
|
|
1048
|
+
() => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
|
|
1049
|
+
);
|
|
738
1050
|
}
|
|
739
1051
|
} finally {
|
|
740
1052
|
this.inFlight.delete(p);
|
|
741
1053
|
}
|
|
742
1054
|
}
|
|
743
1055
|
}
|
|
1056
|
+
/** See EventShipper.requestOpts — same shutdown-budget semantics. */
|
|
1057
|
+
requestOpts() {
|
|
1058
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
1059
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
1060
|
+
if (remainingMs <= 0) return null;
|
|
1061
|
+
return {
|
|
1062
|
+
maxAttempts: 1,
|
|
1063
|
+
debug: this.debug,
|
|
1064
|
+
sdkName: this.sdkName,
|
|
1065
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
if (this.hasShutdown) {
|
|
1069
|
+
return {
|
|
1070
|
+
maxAttempts: 1,
|
|
1071
|
+
debug: this.debug,
|
|
1072
|
+
sdkName: this.sdkName,
|
|
1073
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
1077
|
+
}
|
|
744
1078
|
async shutdown() {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
1079
|
+
this.hasShutdown = true;
|
|
1080
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
1081
|
+
try {
|
|
1082
|
+
if (this.timer) {
|
|
1083
|
+
clearTimeout(this.timer);
|
|
1084
|
+
this.timer = void 0;
|
|
1085
|
+
}
|
|
1086
|
+
const drain = async () => {
|
|
1087
|
+
await this.flush();
|
|
1088
|
+
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
1089
|
+
})));
|
|
1090
|
+
};
|
|
1091
|
+
const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
|
|
1092
|
+
if (!settled) {
|
|
1093
|
+
rateLimitedLog(
|
|
1094
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1095
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
} finally {
|
|
1099
|
+
this.shutdownDeadlineAt = void 0;
|
|
748
1100
|
}
|
|
749
|
-
await this.flush();
|
|
750
|
-
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
751
|
-
})));
|
|
752
1101
|
}
|
|
753
1102
|
};
|
|
754
1103
|
|
|
@@ -809,7 +1158,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
809
1158
|
// package.json
|
|
810
1159
|
var package_default = {
|
|
811
1160
|
name: "@raindrop-ai/opencode-plugin",
|
|
812
|
-
version: "0.0.
|
|
1161
|
+
version: "0.0.13",
|
|
813
1162
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
814
1163
|
type: "module",
|
|
815
1164
|
main: "dist/index.js",
|
|
@@ -919,6 +1268,86 @@ var TraceShipper2 = class extends TraceShipper {
|
|
|
919
1268
|
}
|
|
920
1269
|
};
|
|
921
1270
|
|
|
1271
|
+
// src/bounded.ts
|
|
1272
|
+
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1273
|
+
var MAX_TEXT_FIELD_CHARS = 1e6;
|
|
1274
|
+
var MAX_BOUNDED_DEPTH = 12;
|
|
1275
|
+
function truncateToLimit2(text, limit) {
|
|
1276
|
+
if (text.length <= limit) return text;
|
|
1277
|
+
if (limit > TRUNCATION_MARKER2.length) {
|
|
1278
|
+
return text.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
|
|
1279
|
+
}
|
|
1280
|
+
return text.slice(0, limit);
|
|
1281
|
+
}
|
|
1282
|
+
function capText2(value, limit = MAX_TEXT_FIELD_CHARS) {
|
|
1283
|
+
if (value.length <= limit) return value;
|
|
1284
|
+
return truncateToLimit2(value, limit);
|
|
1285
|
+
}
|
|
1286
|
+
function boundedClone2(value, budget, depth) {
|
|
1287
|
+
if (budget.remaining <= 0) return TRUNCATION_MARKER2;
|
|
1288
|
+
if (typeof value === "string") {
|
|
1289
|
+
if (value.length > budget.remaining) {
|
|
1290
|
+
const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
|
|
1291
|
+
budget.remaining = 0;
|
|
1292
|
+
return taken;
|
|
1293
|
+
}
|
|
1294
|
+
budget.remaining -= Math.max(value.length, 1);
|
|
1295
|
+
return value;
|
|
1296
|
+
}
|
|
1297
|
+
if (value === null || typeof value === "number" || typeof value === "boolean") {
|
|
1298
|
+
budget.remaining -= 8;
|
|
1299
|
+
return value;
|
|
1300
|
+
}
|
|
1301
|
+
if (typeof value !== "object") {
|
|
1302
|
+
budget.remaining -= 8;
|
|
1303
|
+
return value;
|
|
1304
|
+
}
|
|
1305
|
+
if (depth >= MAX_BOUNDED_DEPTH) {
|
|
1306
|
+
budget.remaining -= 16;
|
|
1307
|
+
return `<max depth: ${TRUNCATION_MARKER2}>`;
|
|
1308
|
+
}
|
|
1309
|
+
const withToJson = value;
|
|
1310
|
+
if (typeof withToJson.toJSON === "function") {
|
|
1311
|
+
try {
|
|
1312
|
+
return boundedClone2(withToJson.toJSON(), budget, depth + 1);
|
|
1313
|
+
} catch (e) {
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
if (Array.isArray(value)) {
|
|
1317
|
+
const out2 = [];
|
|
1318
|
+
for (const item of value) {
|
|
1319
|
+
if (budget.remaining <= 0) {
|
|
1320
|
+
out2.push(TRUNCATION_MARKER2);
|
|
1321
|
+
break;
|
|
1322
|
+
}
|
|
1323
|
+
out2.push(boundedClone2(item, budget, depth + 1));
|
|
1324
|
+
}
|
|
1325
|
+
return out2;
|
|
1326
|
+
}
|
|
1327
|
+
const out = {};
|
|
1328
|
+
for (const key of Object.keys(value)) {
|
|
1329
|
+
if (budget.remaining <= 0) {
|
|
1330
|
+
out["..."] = TRUNCATION_MARKER2;
|
|
1331
|
+
break;
|
|
1332
|
+
}
|
|
1333
|
+
budget.remaining -= Math.max(key.length, 1);
|
|
1334
|
+
out[key] = boundedClone2(value[key], budget, depth + 1);
|
|
1335
|
+
}
|
|
1336
|
+
return out;
|
|
1337
|
+
}
|
|
1338
|
+
function boundedStringify(value, limit = MAX_TEXT_FIELD_CHARS) {
|
|
1339
|
+
var _a;
|
|
1340
|
+
try {
|
|
1341
|
+
if (typeof value === "string") {
|
|
1342
|
+
return truncateToLimit2(JSON.stringify(capText2(value, limit)), limit);
|
|
1343
|
+
}
|
|
1344
|
+
const pruned = boundedClone2(value, { remaining: limit + TRUNCATION_MARKER2.length + 256 }, 0);
|
|
1345
|
+
return truncateToLimit2((_a = JSON.stringify(pruned)) != null ? _a : "", limit);
|
|
1346
|
+
} catch (e) {
|
|
1347
|
+
return capText2(String(value), limit);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
922
1351
|
// src/tracing-linkage-helpers.ts
|
|
923
1352
|
function createSessionParentMapHelpers({
|
|
924
1353
|
sessions: sessions2,
|
|
@@ -996,6 +1425,15 @@ function createSessionParentMapHelpers({
|
|
|
996
1425
|
}
|
|
997
1426
|
|
|
998
1427
|
// src/tracing.ts
|
|
1428
|
+
var ERROR_LOG_INTERVAL_MS = 3e4;
|
|
1429
|
+
var lastErrorLogAt = /* @__PURE__ */ new Map();
|
|
1430
|
+
function rateLimitedErrorLog(key, message) {
|
|
1431
|
+
const now = Date.now();
|
|
1432
|
+
const last = lastErrorLogAt.get(key);
|
|
1433
|
+
if (last !== void 0 && now - last < ERROR_LOG_INTERVAL_MS) return;
|
|
1434
|
+
lastErrorLogAt.set(key, now);
|
|
1435
|
+
console.log(message);
|
|
1436
|
+
}
|
|
999
1437
|
var sessions = /* @__PURE__ */ new Map();
|
|
1000
1438
|
var taskContexts = /* @__PURE__ */ new Map();
|
|
1001
1439
|
var runningTaskCallsBySession = /* @__PURE__ */ new Map();
|
|
@@ -1148,7 +1586,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1148
1586
|
const state = sessions.get(partSessionID);
|
|
1149
1587
|
if (!state) return;
|
|
1150
1588
|
if (partObj["type"] === "text" && typeof partObj["text"] === "string") {
|
|
1151
|
-
state.outputParts.set(messageId, partObj["text"]);
|
|
1589
|
+
state.outputParts.set(messageId, capText2(partObj["text"]));
|
|
1152
1590
|
} else if (partObj["type"] === "tool" && messageId) {
|
|
1153
1591
|
const tool = partObj["tool"];
|
|
1154
1592
|
const callID = partObj["callID"];
|
|
@@ -1178,7 +1616,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1178
1616
|
if (typeof errorMessage !== "string" || errorMessage === "")
|
|
1179
1617
|
throw new Error("message.part.updated: error tool state must have error string");
|
|
1180
1618
|
const input = toolState["input"];
|
|
1181
|
-
const toolCallArgs = input === void 0 ? void 0 : typeof input === "string" ? input :
|
|
1619
|
+
const toolCallArgs = input === void 0 ? void 0 : typeof input === "string" ? capText2(input) : boundedStringify(input);
|
|
1182
1620
|
if (endPendingToolSpanWithError(
|
|
1183
1621
|
state,
|
|
1184
1622
|
callID,
|
|
@@ -1191,7 +1629,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1191
1629
|
}
|
|
1192
1630
|
}
|
|
1193
1631
|
} else if (partObj["type"] === "reasoning" && typeof partObj["text"] === "string") {
|
|
1194
|
-
state.reasoningParts.set(messageId, partObj["text"]);
|
|
1632
|
+
state.reasoningParts.set(messageId, capText2(partObj["text"]));
|
|
1195
1633
|
}
|
|
1196
1634
|
} else if (event.type === "message.updated") {
|
|
1197
1635
|
const msgInfo = props["info"];
|
|
@@ -1458,7 +1896,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1458
1896
|
sessions.delete(String(errorSessionID));
|
|
1459
1897
|
}
|
|
1460
1898
|
} catch (err) {
|
|
1461
|
-
|
|
1899
|
+
rateLimitedErrorLog(
|
|
1900
|
+
"event",
|
|
1462
1901
|
`[raindrop-ai/opencode-plugin] [error] Error in event hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1463
1902
|
);
|
|
1464
1903
|
}
|
|
@@ -1514,7 +1953,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1514
1953
|
...promptMeta,
|
|
1515
1954
|
properties: baseMeta.properties || promptMeta.properties ? { ...(_a = baseMeta.properties) != null ? _a : {}, ...(_b = promptMeta.properties) != null ? _b : {} } : void 0
|
|
1516
1955
|
};
|
|
1517
|
-
const userText = textParts.join("\n");
|
|
1956
|
+
const userText = capText2(textParts.join("\n"));
|
|
1518
1957
|
state.currentInput = userText;
|
|
1519
1958
|
if (state.parentId) {
|
|
1520
1959
|
if (!state.parentTaskSpanIds || !state.parentEventContext) {
|
|
@@ -1623,7 +2062,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1623
2062
|
});
|
|
1624
2063
|
}
|
|
1625
2064
|
} catch (err) {
|
|
1626
|
-
|
|
2065
|
+
rateLimitedErrorLog(
|
|
2066
|
+
"tool.execute.before",
|
|
1627
2067
|
`[raindrop-ai/opencode-plugin] [error] Error in tool.execute.before hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1628
2068
|
);
|
|
1629
2069
|
}
|
|
@@ -1638,7 +2078,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1638
2078
|
if (!state || !state.currentEventId) return;
|
|
1639
2079
|
if (args === void 0 || args === null)
|
|
1640
2080
|
throw new Error("tool.execute.after: args is required");
|
|
1641
|
-
const toolCallArgs = typeof args === "string" ? args :
|
|
2081
|
+
const toolCallArgs = typeof args === "string" ? capText2(args) : boundedStringify(args);
|
|
1642
2082
|
const startInfo = state.toolSpanStarts.get(callID);
|
|
1643
2083
|
state.toolSpanStarts.delete(callID);
|
|
1644
2084
|
const resultMetadata = result.metadata;
|
|
@@ -1651,13 +2091,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1651
2091
|
const endTimeUnixNano = nowUnixNanoString();
|
|
1652
2092
|
if (!("output" in result) || result.output === void 0)
|
|
1653
2093
|
throw new Error("tool.execute.after: result.output is required");
|
|
1654
|
-
|
|
1655
|
-
try {
|
|
1656
|
-
toolResult = JSON.stringify(result.output);
|
|
1657
|
-
} catch (err) {
|
|
1658
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1659
|
-
throw new Error(`tool.execute.after: result.output must be JSON-serializable: ${msg}`);
|
|
1660
|
-
}
|
|
2094
|
+
const toolResult = boundedStringify(result.output);
|
|
1661
2095
|
if (startInfo.liveSpan) {
|
|
1662
2096
|
traceShipper.endSpan(startInfo.liveSpan, {
|
|
1663
2097
|
attributes: [
|
|
@@ -1686,7 +2120,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1686
2120
|
markTaskCallFinished(sessionID, callID);
|
|
1687
2121
|
}
|
|
1688
2122
|
} catch (err) {
|
|
1689
|
-
|
|
2123
|
+
rateLimitedErrorLog(
|
|
2124
|
+
"tool.execute.after",
|
|
1690
2125
|
`[raindrop-ai/opencode-plugin] [error] Error in tool.execute.after hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1691
2126
|
);
|
|
1692
2127
|
}
|
|
@@ -1703,7 +2138,8 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1703
2138
|
state.isCompacting = true;
|
|
1704
2139
|
}
|
|
1705
2140
|
} catch (err) {
|
|
1706
|
-
|
|
2141
|
+
rateLimitedErrorLog(
|
|
2142
|
+
"experimental.session.compacting",
|
|
1707
2143
|
`[raindrop-ai/opencode-plugin] [error] Error in experimental.session.compacting hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1708
2144
|
);
|
|
1709
2145
|
}
|
|
@@ -1722,11 +2158,12 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1722
2158
|
sessions.set(sessionID, state);
|
|
1723
2159
|
}
|
|
1724
2160
|
const MAX_SYSTEM_PROMPT_LENGTH = 32768;
|
|
1725
|
-
const
|
|
2161
|
+
const TRUNCATION_MARKER3 = "\n...[truncated]";
|
|
1726
2162
|
const joined = output.system.join("\n\n");
|
|
1727
|
-
state.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH -
|
|
2163
|
+
state.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH - TRUNCATION_MARKER3.length) + TRUNCATION_MARKER3 : joined;
|
|
1728
2164
|
} catch (err) {
|
|
1729
|
-
|
|
2165
|
+
rateLimitedErrorLog(
|
|
2166
|
+
"experimental.chat.system.transform",
|
|
1730
2167
|
`[raindrop-ai/opencode-plugin] [error] Error in experimental.chat.system.transform hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1731
2168
|
);
|
|
1732
2169
|
}
|