@raindrop-ai/opencode-plugin 0.0.6 → 0.0.8
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/dist/index.cjs +158 -18
- package/dist/index.js +158 -18
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -29,7 +29,7 @@ var import_node_fs = require("fs");
|
|
|
29
29
|
var import_node_os = require("os");
|
|
30
30
|
var import_node_path = require("path");
|
|
31
31
|
function loadConfig(projectDirectory) {
|
|
32
|
-
var _a, _b, _c, _d, _e, _f;
|
|
32
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
33
33
|
let merged = {};
|
|
34
34
|
const configPaths = [
|
|
35
35
|
(0, import_node_path.join)((0, import_node_os.homedir)(), ".config", "opencode", "raindrop.json"),
|
|
@@ -58,6 +58,7 @@ function loadConfig(projectDirectory) {
|
|
|
58
58
|
endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : merged.api_url) != null ? _d : "https://api.raindrop.ai/v1",
|
|
59
59
|
eventName: (_e = merged.event_name) != null ? _e : "opencode_session",
|
|
60
60
|
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false,
|
|
61
|
+
captureSystemPrompt: process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] !== void 0 ? process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] === "true" : (_g = merged.capture_system_prompt) != null ? _g : false,
|
|
61
62
|
eventMetadata
|
|
62
63
|
};
|
|
63
64
|
}
|
|
@@ -65,7 +66,7 @@ function loadConfig(projectDirectory) {
|
|
|
65
66
|
// package.json
|
|
66
67
|
var package_default = {
|
|
67
68
|
name: "@raindrop-ai/opencode-plugin",
|
|
68
|
-
version: "0.0.
|
|
69
|
+
version: "0.0.8",
|
|
69
70
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
70
71
|
type: "module",
|
|
71
72
|
main: "dist/index.cjs",
|
|
@@ -89,8 +90,8 @@ var package_default = {
|
|
|
89
90
|
test: "cd tests && pnpm test"
|
|
90
91
|
},
|
|
91
92
|
peerDependencies: {
|
|
92
|
-
"@opencode-ai/plugin": ">=1.
|
|
93
|
-
"@opencode-ai/sdk": ">=1.
|
|
93
|
+
"@opencode-ai/plugin": ">=1.3.0",
|
|
94
|
+
"@opencode-ai/sdk": ">=1.3.0"
|
|
94
95
|
},
|
|
95
96
|
peerDependenciesMeta: {
|
|
96
97
|
"@opencode-ai/sdk": {
|
|
@@ -99,8 +100,8 @@ var package_default = {
|
|
|
99
100
|
},
|
|
100
101
|
devDependencies: {
|
|
101
102
|
"@raindrop-ai/core": "workspace:*",
|
|
102
|
-
"@opencode-ai/plugin": "^1.
|
|
103
|
-
"@opencode-ai/sdk": "^1.
|
|
103
|
+
"@opencode-ai/plugin": "^1.3.3",
|
|
104
|
+
"@opencode-ai/sdk": "^1.3.3",
|
|
104
105
|
"@types/node": "^20.11.17",
|
|
105
106
|
tsup: "^8.4.0",
|
|
106
107
|
typescript: "^5.3.3"
|
|
@@ -130,7 +131,7 @@ var package_default = {
|
|
|
130
131
|
var PLUGIN_NAME = package_default.name;
|
|
131
132
|
var PLUGIN_VERSION = package_default.version;
|
|
132
133
|
|
|
133
|
-
// ../core/dist/chunk-
|
|
134
|
+
// ../core/dist/chunk-4UCYIEH4.js
|
|
134
135
|
function getCrypto() {
|
|
135
136
|
const c = globalThis.crypto;
|
|
136
137
|
return c;
|
|
@@ -375,6 +376,7 @@ var EventShipper = class {
|
|
|
375
376
|
console.log(`${this.prefix} queue patch`, {
|
|
376
377
|
eventId,
|
|
377
378
|
userId: patch.userId,
|
|
379
|
+
convoId: patch.convoId,
|
|
378
380
|
eventName: patch.eventName,
|
|
379
381
|
hasInput: typeof patch.input === "string" && patch.input.length > 0,
|
|
380
382
|
hasOutput: typeof patch.output === "string" && patch.output.length > 0,
|
|
@@ -503,16 +505,18 @@ var EventShipper = class {
|
|
|
503
505
|
return;
|
|
504
506
|
}
|
|
505
507
|
const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
|
|
508
|
+
const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
|
|
509
|
+
const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
|
|
506
510
|
const payload = {
|
|
507
511
|
event_id: eventId,
|
|
508
512
|
user_id: userId,
|
|
509
513
|
event: eventName,
|
|
510
|
-
timestamp: (
|
|
514
|
+
timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
|
|
511
515
|
ai_data: {
|
|
512
516
|
input: accumulated.input,
|
|
513
517
|
output: accumulated.output,
|
|
514
518
|
model: accumulated.model,
|
|
515
|
-
convo_id:
|
|
519
|
+
convo_id: convoId
|
|
516
520
|
},
|
|
517
521
|
properties: {
|
|
518
522
|
...restProperties,
|
|
@@ -520,7 +524,7 @@ var EventShipper = class {
|
|
|
520
524
|
$context: this.context
|
|
521
525
|
},
|
|
522
526
|
attachments: accumulated.attachments,
|
|
523
|
-
is_pending:
|
|
527
|
+
is_pending: isPending
|
|
524
528
|
};
|
|
525
529
|
const url = `${this.baseUrl}events/track_partial`;
|
|
526
530
|
if (this.debug) {
|
|
@@ -528,7 +532,8 @@ var EventShipper = class {
|
|
|
528
532
|
eventId,
|
|
529
533
|
eventName,
|
|
530
534
|
userId,
|
|
531
|
-
|
|
535
|
+
convoId,
|
|
536
|
+
isPending,
|
|
532
537
|
inputPreview: typeof accumulated.input === "string" ? accumulated.input.slice(0, 120) : void 0,
|
|
533
538
|
outputPreview: typeof accumulated.output === "string" ? accumulated.output.slice(0, 120) : void 0,
|
|
534
539
|
attachments: (_k = (_j = accumulated.attachments) == null ? void 0 : _j.length) != null ? _k : 0,
|
|
@@ -560,17 +565,33 @@ var EventShipper = class {
|
|
|
560
565
|
} finally {
|
|
561
566
|
this.inFlight.delete(p);
|
|
562
567
|
}
|
|
563
|
-
const isPending = payload.is_pending;
|
|
564
568
|
if (!isPending) {
|
|
565
569
|
this.sticky.delete(eventId);
|
|
566
570
|
}
|
|
567
571
|
}
|
|
568
572
|
};
|
|
573
|
+
var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
|
|
574
|
+
function resolveLocalDebuggerBaseUrl(baseUrl) {
|
|
575
|
+
var _a, _b, _c;
|
|
576
|
+
const resolved = (_b = baseUrl != null ? baseUrl : typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[LOCAL_DEBUGGER_ENV_VAR] : void 0) != null ? _b : null;
|
|
577
|
+
return resolved ? (_c = formatEndpoint(resolved)) != null ? _c : null : null;
|
|
578
|
+
}
|
|
579
|
+
function mirrorTraceExportToLocalDebugger(body, options = {}) {
|
|
580
|
+
var _a;
|
|
581
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
582
|
+
if (!baseUrl) return;
|
|
583
|
+
void postJson(`${baseUrl}traces`, body, {}, {
|
|
584
|
+
maxAttempts: 1,
|
|
585
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
586
|
+
sdkName: options.sdkName
|
|
587
|
+
}).catch(() => {
|
|
588
|
+
});
|
|
589
|
+
}
|
|
569
590
|
var TraceShipper = class {
|
|
570
591
|
constructor(opts) {
|
|
571
592
|
this.queue = [];
|
|
572
593
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
573
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
594
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
574
595
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
575
596
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
576
597
|
this.enabled = opts.enabled !== false;
|
|
@@ -583,6 +604,13 @@ var TraceShipper = class {
|
|
|
583
604
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
584
605
|
this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
|
|
585
606
|
this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
|
|
607
|
+
const localDebugger = typeof process !== "undefined" ? (_i = process.env) == null ? void 0 : _i.RAINDROP_LOCAL_DEBUGGER : void 0;
|
|
608
|
+
if (localDebugger) {
|
|
609
|
+
this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(localDebugger)) != null ? _j : void 0;
|
|
610
|
+
if (this.debug) {
|
|
611
|
+
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
586
614
|
}
|
|
587
615
|
isDebugEnabled() {
|
|
588
616
|
return this.debug;
|
|
@@ -599,7 +627,25 @@ var TraceShipper = class {
|
|
|
599
627
|
attrString("ai.operationId", args.operationId)
|
|
600
628
|
];
|
|
601
629
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
602
|
-
|
|
630
|
+
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
631
|
+
if (this.localDebuggerUrl) {
|
|
632
|
+
const openSpan = buildOtlpSpan({
|
|
633
|
+
ids: span.ids,
|
|
634
|
+
name: span.name,
|
|
635
|
+
startTimeUnixNano: span.startTimeUnixNano,
|
|
636
|
+
endTimeUnixNano: span.startTimeUnixNano,
|
|
637
|
+
// placeholder — will be updated on endSpan
|
|
638
|
+
attributes: span.attributes,
|
|
639
|
+
status: { code: SpanStatusCode.UNSET }
|
|
640
|
+
});
|
|
641
|
+
const body = buildExportTraceServiceRequest([openSpan], this.serviceName, this.serviceVersion);
|
|
642
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
643
|
+
baseUrl: this.localDebuggerUrl,
|
|
644
|
+
debug: false,
|
|
645
|
+
sdkName: this.sdkName
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
return span;
|
|
603
649
|
}
|
|
604
650
|
endSpan(span, extra) {
|
|
605
651
|
var _a, _b;
|
|
@@ -622,6 +668,14 @@ var TraceShipper = class {
|
|
|
622
668
|
status
|
|
623
669
|
});
|
|
624
670
|
this.enqueue(otlp);
|
|
671
|
+
if (this.localDebuggerUrl) {
|
|
672
|
+
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
673
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
674
|
+
baseUrl: this.localDebuggerUrl,
|
|
675
|
+
debug: false,
|
|
676
|
+
sdkName: this.sdkName
|
|
677
|
+
});
|
|
678
|
+
}
|
|
625
679
|
}
|
|
626
680
|
createSpan(args) {
|
|
627
681
|
var _a;
|
|
@@ -639,6 +693,14 @@ var TraceShipper = class {
|
|
|
639
693
|
status: args.status
|
|
640
694
|
});
|
|
641
695
|
this.enqueue(otlp);
|
|
696
|
+
if (this.localDebuggerUrl) {
|
|
697
|
+
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
698
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
699
|
+
baseUrl: this.localDebuggerUrl,
|
|
700
|
+
debug: false,
|
|
701
|
+
sdkName: this.sdkName
|
|
702
|
+
});
|
|
703
|
+
}
|
|
642
704
|
}
|
|
643
705
|
enqueue(span) {
|
|
644
706
|
if (!this.enabled) return;
|
|
@@ -713,6 +775,10 @@ var TraceShipper = class {
|
|
|
713
775
|
}
|
|
714
776
|
};
|
|
715
777
|
|
|
778
|
+
// ../core/dist/index.node.js
|
|
779
|
+
var import_async_hooks = require("async_hooks");
|
|
780
|
+
globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
|
|
781
|
+
|
|
716
782
|
// src/shipper.ts
|
|
717
783
|
var EventShipper2 = class extends EventShipper {
|
|
718
784
|
constructor(opts) {
|
|
@@ -891,7 +957,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
891
957
|
const props = event.properties;
|
|
892
958
|
const info = props["info"];
|
|
893
959
|
const sessionID = (_b = (_a = props["sessionID"]) != null ? _a : info == null ? void 0 : info["id"]) != null ? _b : props["id"];
|
|
894
|
-
const sessionEventTypes = ["session.created", "session.idle", "session.deleted", "session.error"];
|
|
960
|
+
const sessionEventTypes = ["session.created", "session.compacted", "session.idle", "session.deleted", "session.error"];
|
|
895
961
|
if (sessionEventTypes.includes(event.type) && (sessionID === void 0 || sessionID === "")) {
|
|
896
962
|
throw new Error(`event ${event.type}: missing sessionID in properties`);
|
|
897
963
|
}
|
|
@@ -906,6 +972,10 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
906
972
|
}
|
|
907
973
|
state.parentId = parentID;
|
|
908
974
|
if (state.parentId) mapChildSessionToParent.set(sessionID, { parentId: state.parentId });
|
|
975
|
+
} else if (event.type === "session.compacted") {
|
|
976
|
+
const state = sessions.get(String(sessionID));
|
|
977
|
+
if (!state) return;
|
|
978
|
+
state.isCompacting = true;
|
|
909
979
|
} else if (event.type === "message.part.updated") {
|
|
910
980
|
const part = props["part"];
|
|
911
981
|
if (part == null || typeof part !== "object")
|
|
@@ -1015,12 +1085,19 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1015
1085
|
attrString("ai.response.text", reasoningText),
|
|
1016
1086
|
attrString("gen_ai.system", providerID),
|
|
1017
1087
|
attrString("gen_ai.request.model", modelID),
|
|
1088
|
+
attrString("gen_ai.response.model", modelID),
|
|
1018
1089
|
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1019
1090
|
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
1020
1091
|
];
|
|
1021
1092
|
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1022
1093
|
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1023
1094
|
}
|
|
1095
|
+
if (state.currentSystemPrompt) {
|
|
1096
|
+
llmAttrs.push(
|
|
1097
|
+
attrString("gen_ai.prompt.0.role", "system"),
|
|
1098
|
+
attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1024
1101
|
const llmParent = state.parentId && state.parentTaskSpanIds ? state.parentTaskSpanIds : state.currentRootSpan.ids;
|
|
1025
1102
|
const llmSpan = traceShipper.startSpan({
|
|
1026
1103
|
name: modelName,
|
|
@@ -1037,6 +1114,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1037
1114
|
}
|
|
1038
1115
|
if (outputText === void 0) return;
|
|
1039
1116
|
const rootSpan = state.currentRootSpan;
|
|
1117
|
+
const isCompaction = state.isCompacting === true;
|
|
1040
1118
|
if (state.parentId) {
|
|
1041
1119
|
const llmAttrs = [
|
|
1042
1120
|
attrString("ai.operationId", "generateText"),
|
|
@@ -1044,12 +1122,19 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1044
1122
|
attrString("ai.response.text", outputText),
|
|
1045
1123
|
attrString("gen_ai.system", providerID),
|
|
1046
1124
|
attrString("gen_ai.request.model", modelID),
|
|
1125
|
+
attrString("gen_ai.response.model", modelID),
|
|
1047
1126
|
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1048
1127
|
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
1049
1128
|
];
|
|
1050
1129
|
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1051
1130
|
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1052
1131
|
}
|
|
1132
|
+
if (state.currentSystemPrompt) {
|
|
1133
|
+
llmAttrs.push(
|
|
1134
|
+
attrString("gen_ai.prompt.0.role", "system"),
|
|
1135
|
+
attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1053
1138
|
if (!state.parentTaskSpanIds) {
|
|
1054
1139
|
traceShipper.endSpan(rootSpan, {
|
|
1055
1140
|
error: "Missing strict parent task for child session"
|
|
@@ -1077,23 +1162,36 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1077
1162
|
error: errorForSpan
|
|
1078
1163
|
});
|
|
1079
1164
|
} else {
|
|
1080
|
-
rootSpan.name = modelName;
|
|
1165
|
+
rootSpan.name = isCompaction ? "ai.compaction" : modelName;
|
|
1081
1166
|
const rootAttrs = [
|
|
1082
1167
|
attrString("ai.operationId", "generateText"),
|
|
1083
1168
|
attrString("ai.prompt", state.currentInput),
|
|
1084
1169
|
attrString("ai.response.text", outputText),
|
|
1085
1170
|
attrString("gen_ai.system", providerID),
|
|
1086
1171
|
attrString("gen_ai.request.model", modelID),
|
|
1172
|
+
attrString("gen_ai.response.model", modelID),
|
|
1087
1173
|
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1088
1174
|
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
1089
1175
|
];
|
|
1176
|
+
if (isCompaction) {
|
|
1177
|
+
rootAttrs.push(attrString("is_compaction", "true"));
|
|
1178
|
+
}
|
|
1090
1179
|
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1091
1180
|
rootAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1092
1181
|
}
|
|
1182
|
+
if (state.currentSystemPrompt) {
|
|
1183
|
+
rootAttrs.push(
|
|
1184
|
+
attrString("gen_ai.prompt.0.role", "system"),
|
|
1185
|
+
attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1093
1188
|
traceShipper.endSpan(rootSpan, {
|
|
1094
1189
|
attributes: rootAttrs,
|
|
1095
1190
|
error: errorForSpan
|
|
1096
1191
|
});
|
|
1192
|
+
if (isCompaction) {
|
|
1193
|
+
state.isCompacting = false;
|
|
1194
|
+
}
|
|
1097
1195
|
}
|
|
1098
1196
|
state.currentRootSpan = void 0;
|
|
1099
1197
|
const hasAssistantResponse = outputText.trim().length > 0;
|
|
@@ -1108,7 +1206,8 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1108
1206
|
model: modelName,
|
|
1109
1207
|
output: outputText,
|
|
1110
1208
|
properties: {
|
|
1111
|
-
plugin_version: PLUGIN_VERSION
|
|
1209
|
+
plugin_version: PLUGIN_VERSION,
|
|
1210
|
+
...isCompaction ? { is_compaction: true } : {}
|
|
1112
1211
|
}
|
|
1113
1212
|
});
|
|
1114
1213
|
}
|
|
@@ -1125,6 +1224,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1125
1224
|
if (state.currentEventId) {
|
|
1126
1225
|
state.currentEventId = void 0;
|
|
1127
1226
|
}
|
|
1227
|
+
state.isCompacting = false;
|
|
1128
1228
|
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
1129
1229
|
if (state.parentId) {
|
|
1130
1230
|
cleanupSessionParentMap(String(sessionID));
|
|
@@ -1252,7 +1352,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1252
1352
|
}
|
|
1253
1353
|
state.currentEventId = generateId();
|
|
1254
1354
|
state.currentRootSpan = traceShipper.startSpan({
|
|
1255
|
-
name: "ai.event",
|
|
1355
|
+
name: state.isCompacting ? "ai.compaction" : "ai.event",
|
|
1256
1356
|
eventId: state.currentEventId,
|
|
1257
1357
|
attributes: [
|
|
1258
1358
|
attrString("workspace", worktree),
|
|
@@ -1408,6 +1508,46 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1408
1508
|
`[raindrop-ai/opencode-plugin] [error] Error in tool.execute.after hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1409
1509
|
);
|
|
1410
1510
|
}
|
|
1511
|
+
},
|
|
1512
|
+
// ------------------------------------------------------------------
|
|
1513
|
+
// experimental.session.compacting — fires before compaction LLM call
|
|
1514
|
+
// ------------------------------------------------------------------
|
|
1515
|
+
"experimental.session.compacting": async (input, _output) => {
|
|
1516
|
+
try {
|
|
1517
|
+
const sessionID = input.sessionID;
|
|
1518
|
+
if (!sessionID) return;
|
|
1519
|
+
const state = sessions.get(sessionID);
|
|
1520
|
+
if (state) {
|
|
1521
|
+
state.isCompacting = true;
|
|
1522
|
+
}
|
|
1523
|
+
} catch (err) {
|
|
1524
|
+
console.log(
|
|
1525
|
+
`[raindrop-ai/opencode-plugin] [error] Error in experimental.session.compacting hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
},
|
|
1529
|
+
// ------------------------------------------------------------------
|
|
1530
|
+
// experimental.chat.system.transform — capture system prompt (read-only)
|
|
1531
|
+
// ------------------------------------------------------------------
|
|
1532
|
+
"experimental.chat.system.transform": async (input, output) => {
|
|
1533
|
+
try {
|
|
1534
|
+
if (!config.captureSystemPrompt) return;
|
|
1535
|
+
const sessionID = input.sessionID;
|
|
1536
|
+
if (!sessionID) return;
|
|
1537
|
+
let state = sessions.get(sessionID);
|
|
1538
|
+
if (!state) {
|
|
1539
|
+
state = createSessionState(sessionID);
|
|
1540
|
+
sessions.set(sessionID, state);
|
|
1541
|
+
}
|
|
1542
|
+
const MAX_SYSTEM_PROMPT_LENGTH = 32768;
|
|
1543
|
+
const TRUNCATION_MARKER = "\n...[truncated]";
|
|
1544
|
+
const joined = output.system.join("\n\n");
|
|
1545
|
+
state.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH - TRUNCATION_MARKER.length) + TRUNCATION_MARKER : joined;
|
|
1546
|
+
} catch (err) {
|
|
1547
|
+
console.log(
|
|
1548
|
+
`[raindrop-ai/opencode-plugin] [error] Error in experimental.chat.system.transform hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1549
|
+
);
|
|
1550
|
+
}
|
|
1411
1551
|
}
|
|
1412
1552
|
};
|
|
1413
1553
|
}
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { existsSync, readFileSync } from "fs";
|
|
|
10
10
|
import { homedir } from "os";
|
|
11
11
|
import { join } from "path";
|
|
12
12
|
function loadConfig(projectDirectory) {
|
|
13
|
-
var _a, _b, _c, _d, _e, _f;
|
|
13
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
14
14
|
let merged = {};
|
|
15
15
|
const configPaths = [
|
|
16
16
|
join(homedir(), ".config", "opencode", "raindrop.json"),
|
|
@@ -39,6 +39,7 @@ function loadConfig(projectDirectory) {
|
|
|
39
39
|
endpoint: (_d = (_c = process.env["RAINDROP_API_URL"]) != null ? _c : merged.api_url) != null ? _d : "https://api.raindrop.ai/v1",
|
|
40
40
|
eventName: (_e = merged.event_name) != null ? _e : "opencode_session",
|
|
41
41
|
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_f = merged.debug) != null ? _f : false,
|
|
42
|
+
captureSystemPrompt: process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] !== void 0 ? process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] === "true" : (_g = merged.capture_system_prompt) != null ? _g : false,
|
|
42
43
|
eventMetadata
|
|
43
44
|
};
|
|
44
45
|
}
|
|
@@ -46,7 +47,7 @@ function loadConfig(projectDirectory) {
|
|
|
46
47
|
// package.json
|
|
47
48
|
var package_default = {
|
|
48
49
|
name: "@raindrop-ai/opencode-plugin",
|
|
49
|
-
version: "0.0.
|
|
50
|
+
version: "0.0.8",
|
|
50
51
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
51
52
|
type: "module",
|
|
52
53
|
main: "dist/index.cjs",
|
|
@@ -70,8 +71,8 @@ var package_default = {
|
|
|
70
71
|
test: "cd tests && pnpm test"
|
|
71
72
|
},
|
|
72
73
|
peerDependencies: {
|
|
73
|
-
"@opencode-ai/plugin": ">=1.
|
|
74
|
-
"@opencode-ai/sdk": ">=1.
|
|
74
|
+
"@opencode-ai/plugin": ">=1.3.0",
|
|
75
|
+
"@opencode-ai/sdk": ">=1.3.0"
|
|
75
76
|
},
|
|
76
77
|
peerDependenciesMeta: {
|
|
77
78
|
"@opencode-ai/sdk": {
|
|
@@ -80,8 +81,8 @@ var package_default = {
|
|
|
80
81
|
},
|
|
81
82
|
devDependencies: {
|
|
82
83
|
"@raindrop-ai/core": "workspace:*",
|
|
83
|
-
"@opencode-ai/plugin": "^1.
|
|
84
|
-
"@opencode-ai/sdk": "^1.
|
|
84
|
+
"@opencode-ai/plugin": "^1.3.3",
|
|
85
|
+
"@opencode-ai/sdk": "^1.3.3",
|
|
85
86
|
"@types/node": "^20.11.17",
|
|
86
87
|
tsup: "^8.4.0",
|
|
87
88
|
typescript: "^5.3.3"
|
|
@@ -111,7 +112,7 @@ var package_default = {
|
|
|
111
112
|
var PLUGIN_NAME = package_default.name;
|
|
112
113
|
var PLUGIN_VERSION = package_default.version;
|
|
113
114
|
|
|
114
|
-
// ../core/dist/chunk-
|
|
115
|
+
// ../core/dist/chunk-4UCYIEH4.js
|
|
115
116
|
function getCrypto() {
|
|
116
117
|
const c = globalThis.crypto;
|
|
117
118
|
return c;
|
|
@@ -356,6 +357,7 @@ var EventShipper = class {
|
|
|
356
357
|
console.log(`${this.prefix} queue patch`, {
|
|
357
358
|
eventId,
|
|
358
359
|
userId: patch.userId,
|
|
360
|
+
convoId: patch.convoId,
|
|
359
361
|
eventName: patch.eventName,
|
|
360
362
|
hasInput: typeof patch.input === "string" && patch.input.length > 0,
|
|
361
363
|
hasOutput: typeof patch.output === "string" && patch.output.length > 0,
|
|
@@ -484,16 +486,18 @@ var EventShipper = class {
|
|
|
484
486
|
return;
|
|
485
487
|
}
|
|
486
488
|
const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
|
|
489
|
+
const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
|
|
490
|
+
const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
|
|
487
491
|
const payload = {
|
|
488
492
|
event_id: eventId,
|
|
489
493
|
user_id: userId,
|
|
490
494
|
event: eventName,
|
|
491
|
-
timestamp: (
|
|
495
|
+
timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
|
|
492
496
|
ai_data: {
|
|
493
497
|
input: accumulated.input,
|
|
494
498
|
output: accumulated.output,
|
|
495
499
|
model: accumulated.model,
|
|
496
|
-
convo_id:
|
|
500
|
+
convo_id: convoId
|
|
497
501
|
},
|
|
498
502
|
properties: {
|
|
499
503
|
...restProperties,
|
|
@@ -501,7 +505,7 @@ var EventShipper = class {
|
|
|
501
505
|
$context: this.context
|
|
502
506
|
},
|
|
503
507
|
attachments: accumulated.attachments,
|
|
504
|
-
is_pending:
|
|
508
|
+
is_pending: isPending
|
|
505
509
|
};
|
|
506
510
|
const url = `${this.baseUrl}events/track_partial`;
|
|
507
511
|
if (this.debug) {
|
|
@@ -509,7 +513,8 @@ var EventShipper = class {
|
|
|
509
513
|
eventId,
|
|
510
514
|
eventName,
|
|
511
515
|
userId,
|
|
512
|
-
|
|
516
|
+
convoId,
|
|
517
|
+
isPending,
|
|
513
518
|
inputPreview: typeof accumulated.input === "string" ? accumulated.input.slice(0, 120) : void 0,
|
|
514
519
|
outputPreview: typeof accumulated.output === "string" ? accumulated.output.slice(0, 120) : void 0,
|
|
515
520
|
attachments: (_k = (_j = accumulated.attachments) == null ? void 0 : _j.length) != null ? _k : 0,
|
|
@@ -541,17 +546,33 @@ var EventShipper = class {
|
|
|
541
546
|
} finally {
|
|
542
547
|
this.inFlight.delete(p);
|
|
543
548
|
}
|
|
544
|
-
const isPending = payload.is_pending;
|
|
545
549
|
if (!isPending) {
|
|
546
550
|
this.sticky.delete(eventId);
|
|
547
551
|
}
|
|
548
552
|
}
|
|
549
553
|
};
|
|
554
|
+
var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
|
|
555
|
+
function resolveLocalDebuggerBaseUrl(baseUrl) {
|
|
556
|
+
var _a, _b, _c;
|
|
557
|
+
const resolved = (_b = baseUrl != null ? baseUrl : typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[LOCAL_DEBUGGER_ENV_VAR] : void 0) != null ? _b : null;
|
|
558
|
+
return resolved ? (_c = formatEndpoint(resolved)) != null ? _c : null : null;
|
|
559
|
+
}
|
|
560
|
+
function mirrorTraceExportToLocalDebugger(body, options = {}) {
|
|
561
|
+
var _a;
|
|
562
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
563
|
+
if (!baseUrl) return;
|
|
564
|
+
void postJson(`${baseUrl}traces`, body, {}, {
|
|
565
|
+
maxAttempts: 1,
|
|
566
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
567
|
+
sdkName: options.sdkName
|
|
568
|
+
}).catch(() => {
|
|
569
|
+
});
|
|
570
|
+
}
|
|
550
571
|
var TraceShipper = class {
|
|
551
572
|
constructor(opts) {
|
|
552
573
|
this.queue = [];
|
|
553
574
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
554
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
575
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
555
576
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
556
577
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
557
578
|
this.enabled = opts.enabled !== false;
|
|
@@ -564,6 +585,13 @@ var TraceShipper = class {
|
|
|
564
585
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
565
586
|
this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
|
|
566
587
|
this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
|
|
588
|
+
const localDebugger = typeof process !== "undefined" ? (_i = process.env) == null ? void 0 : _i.RAINDROP_LOCAL_DEBUGGER : void 0;
|
|
589
|
+
if (localDebugger) {
|
|
590
|
+
this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(localDebugger)) != null ? _j : void 0;
|
|
591
|
+
if (this.debug) {
|
|
592
|
+
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
567
595
|
}
|
|
568
596
|
isDebugEnabled() {
|
|
569
597
|
return this.debug;
|
|
@@ -580,7 +608,25 @@ var TraceShipper = class {
|
|
|
580
608
|
attrString("ai.operationId", args.operationId)
|
|
581
609
|
];
|
|
582
610
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
583
|
-
|
|
611
|
+
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
612
|
+
if (this.localDebuggerUrl) {
|
|
613
|
+
const openSpan = buildOtlpSpan({
|
|
614
|
+
ids: span.ids,
|
|
615
|
+
name: span.name,
|
|
616
|
+
startTimeUnixNano: span.startTimeUnixNano,
|
|
617
|
+
endTimeUnixNano: span.startTimeUnixNano,
|
|
618
|
+
// placeholder — will be updated on endSpan
|
|
619
|
+
attributes: span.attributes,
|
|
620
|
+
status: { code: SpanStatusCode.UNSET }
|
|
621
|
+
});
|
|
622
|
+
const body = buildExportTraceServiceRequest([openSpan], this.serviceName, this.serviceVersion);
|
|
623
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
624
|
+
baseUrl: this.localDebuggerUrl,
|
|
625
|
+
debug: false,
|
|
626
|
+
sdkName: this.sdkName
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
return span;
|
|
584
630
|
}
|
|
585
631
|
endSpan(span, extra) {
|
|
586
632
|
var _a, _b;
|
|
@@ -603,6 +649,14 @@ var TraceShipper = class {
|
|
|
603
649
|
status
|
|
604
650
|
});
|
|
605
651
|
this.enqueue(otlp);
|
|
652
|
+
if (this.localDebuggerUrl) {
|
|
653
|
+
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
654
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
655
|
+
baseUrl: this.localDebuggerUrl,
|
|
656
|
+
debug: false,
|
|
657
|
+
sdkName: this.sdkName
|
|
658
|
+
});
|
|
659
|
+
}
|
|
606
660
|
}
|
|
607
661
|
createSpan(args) {
|
|
608
662
|
var _a;
|
|
@@ -620,6 +674,14 @@ var TraceShipper = class {
|
|
|
620
674
|
status: args.status
|
|
621
675
|
});
|
|
622
676
|
this.enqueue(otlp);
|
|
677
|
+
if (this.localDebuggerUrl) {
|
|
678
|
+
const body = buildExportTraceServiceRequest([otlp], this.serviceName, this.serviceVersion);
|
|
679
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
680
|
+
baseUrl: this.localDebuggerUrl,
|
|
681
|
+
debug: false,
|
|
682
|
+
sdkName: this.sdkName
|
|
683
|
+
});
|
|
684
|
+
}
|
|
623
685
|
}
|
|
624
686
|
enqueue(span) {
|
|
625
687
|
if (!this.enabled) return;
|
|
@@ -694,6 +756,10 @@ var TraceShipper = class {
|
|
|
694
756
|
}
|
|
695
757
|
};
|
|
696
758
|
|
|
759
|
+
// ../core/dist/index.node.js
|
|
760
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
761
|
+
globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
|
|
762
|
+
|
|
697
763
|
// src/shipper.ts
|
|
698
764
|
var EventShipper2 = class extends EventShipper {
|
|
699
765
|
constructor(opts) {
|
|
@@ -872,7 +938,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
872
938
|
const props = event.properties;
|
|
873
939
|
const info = props["info"];
|
|
874
940
|
const sessionID = (_b = (_a = props["sessionID"]) != null ? _a : info == null ? void 0 : info["id"]) != null ? _b : props["id"];
|
|
875
|
-
const sessionEventTypes = ["session.created", "session.idle", "session.deleted", "session.error"];
|
|
941
|
+
const sessionEventTypes = ["session.created", "session.compacted", "session.idle", "session.deleted", "session.error"];
|
|
876
942
|
if (sessionEventTypes.includes(event.type) && (sessionID === void 0 || sessionID === "")) {
|
|
877
943
|
throw new Error(`event ${event.type}: missing sessionID in properties`);
|
|
878
944
|
}
|
|
@@ -887,6 +953,10 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
887
953
|
}
|
|
888
954
|
state.parentId = parentID;
|
|
889
955
|
if (state.parentId) mapChildSessionToParent.set(sessionID, { parentId: state.parentId });
|
|
956
|
+
} else if (event.type === "session.compacted") {
|
|
957
|
+
const state = sessions.get(String(sessionID));
|
|
958
|
+
if (!state) return;
|
|
959
|
+
state.isCompacting = true;
|
|
890
960
|
} else if (event.type === "message.part.updated") {
|
|
891
961
|
const part = props["part"];
|
|
892
962
|
if (part == null || typeof part !== "object")
|
|
@@ -996,12 +1066,19 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
996
1066
|
attrString("ai.response.text", reasoningText),
|
|
997
1067
|
attrString("gen_ai.system", providerID),
|
|
998
1068
|
attrString("gen_ai.request.model", modelID),
|
|
1069
|
+
attrString("gen_ai.response.model", modelID),
|
|
999
1070
|
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1000
1071
|
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
1001
1072
|
];
|
|
1002
1073
|
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1003
1074
|
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1004
1075
|
}
|
|
1076
|
+
if (state.currentSystemPrompt) {
|
|
1077
|
+
llmAttrs.push(
|
|
1078
|
+
attrString("gen_ai.prompt.0.role", "system"),
|
|
1079
|
+
attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1005
1082
|
const llmParent = state.parentId && state.parentTaskSpanIds ? state.parentTaskSpanIds : state.currentRootSpan.ids;
|
|
1006
1083
|
const llmSpan = traceShipper.startSpan({
|
|
1007
1084
|
name: modelName,
|
|
@@ -1018,6 +1095,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1018
1095
|
}
|
|
1019
1096
|
if (outputText === void 0) return;
|
|
1020
1097
|
const rootSpan = state.currentRootSpan;
|
|
1098
|
+
const isCompaction = state.isCompacting === true;
|
|
1021
1099
|
if (state.parentId) {
|
|
1022
1100
|
const llmAttrs = [
|
|
1023
1101
|
attrString("ai.operationId", "generateText"),
|
|
@@ -1025,12 +1103,19 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1025
1103
|
attrString("ai.response.text", outputText),
|
|
1026
1104
|
attrString("gen_ai.system", providerID),
|
|
1027
1105
|
attrString("gen_ai.request.model", modelID),
|
|
1106
|
+
attrString("gen_ai.response.model", modelID),
|
|
1028
1107
|
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1029
1108
|
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
1030
1109
|
];
|
|
1031
1110
|
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1032
1111
|
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1033
1112
|
}
|
|
1113
|
+
if (state.currentSystemPrompt) {
|
|
1114
|
+
llmAttrs.push(
|
|
1115
|
+
attrString("gen_ai.prompt.0.role", "system"),
|
|
1116
|
+
attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1034
1119
|
if (!state.parentTaskSpanIds) {
|
|
1035
1120
|
traceShipper.endSpan(rootSpan, {
|
|
1036
1121
|
error: "Missing strict parent task for child session"
|
|
@@ -1058,23 +1143,36 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1058
1143
|
error: errorForSpan
|
|
1059
1144
|
});
|
|
1060
1145
|
} else {
|
|
1061
|
-
rootSpan.name = modelName;
|
|
1146
|
+
rootSpan.name = isCompaction ? "ai.compaction" : modelName;
|
|
1062
1147
|
const rootAttrs = [
|
|
1063
1148
|
attrString("ai.operationId", "generateText"),
|
|
1064
1149
|
attrString("ai.prompt", state.currentInput),
|
|
1065
1150
|
attrString("ai.response.text", outputText),
|
|
1066
1151
|
attrString("gen_ai.system", providerID),
|
|
1067
1152
|
attrString("gen_ai.request.model", modelID),
|
|
1153
|
+
attrString("gen_ai.response.model", modelID),
|
|
1068
1154
|
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1069
1155
|
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
1070
1156
|
];
|
|
1157
|
+
if (isCompaction) {
|
|
1158
|
+
rootAttrs.push(attrString("is_compaction", "true"));
|
|
1159
|
+
}
|
|
1071
1160
|
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1072
1161
|
rootAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1073
1162
|
}
|
|
1163
|
+
if (state.currentSystemPrompt) {
|
|
1164
|
+
rootAttrs.push(
|
|
1165
|
+
attrString("gen_ai.prompt.0.role", "system"),
|
|
1166
|
+
attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1074
1169
|
traceShipper.endSpan(rootSpan, {
|
|
1075
1170
|
attributes: rootAttrs,
|
|
1076
1171
|
error: errorForSpan
|
|
1077
1172
|
});
|
|
1173
|
+
if (isCompaction) {
|
|
1174
|
+
state.isCompacting = false;
|
|
1175
|
+
}
|
|
1078
1176
|
}
|
|
1079
1177
|
state.currentRootSpan = void 0;
|
|
1080
1178
|
const hasAssistantResponse = outputText.trim().length > 0;
|
|
@@ -1089,7 +1187,8 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1089
1187
|
model: modelName,
|
|
1090
1188
|
output: outputText,
|
|
1091
1189
|
properties: {
|
|
1092
|
-
plugin_version: PLUGIN_VERSION
|
|
1190
|
+
plugin_version: PLUGIN_VERSION,
|
|
1191
|
+
...isCompaction ? { is_compaction: true } : {}
|
|
1093
1192
|
}
|
|
1094
1193
|
});
|
|
1095
1194
|
}
|
|
@@ -1106,6 +1205,7 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1106
1205
|
if (state.currentEventId) {
|
|
1107
1206
|
state.currentEventId = void 0;
|
|
1108
1207
|
}
|
|
1208
|
+
state.isCompacting = false;
|
|
1109
1209
|
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
1110
1210
|
if (state.parentId) {
|
|
1111
1211
|
cleanupSessionParentMap(String(sessionID));
|
|
@@ -1233,7 +1333,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1233
1333
|
}
|
|
1234
1334
|
state.currentEventId = generateId();
|
|
1235
1335
|
state.currentRootSpan = traceShipper.startSpan({
|
|
1236
|
-
name: "ai.event",
|
|
1336
|
+
name: state.isCompacting ? "ai.compaction" : "ai.event",
|
|
1237
1337
|
eventId: state.currentEventId,
|
|
1238
1338
|
attributes: [
|
|
1239
1339
|
attrString("workspace", worktree),
|
|
@@ -1389,6 +1489,46 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
1389
1489
|
`[raindrop-ai/opencode-plugin] [error] Error in tool.execute.after hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1390
1490
|
);
|
|
1391
1491
|
}
|
|
1492
|
+
},
|
|
1493
|
+
// ------------------------------------------------------------------
|
|
1494
|
+
// experimental.session.compacting — fires before compaction LLM call
|
|
1495
|
+
// ------------------------------------------------------------------
|
|
1496
|
+
"experimental.session.compacting": async (input, _output) => {
|
|
1497
|
+
try {
|
|
1498
|
+
const sessionID = input.sessionID;
|
|
1499
|
+
if (!sessionID) return;
|
|
1500
|
+
const state = sessions.get(sessionID);
|
|
1501
|
+
if (state) {
|
|
1502
|
+
state.isCompacting = true;
|
|
1503
|
+
}
|
|
1504
|
+
} catch (err) {
|
|
1505
|
+
console.log(
|
|
1506
|
+
`[raindrop-ai/opencode-plugin] [error] Error in experimental.session.compacting hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
},
|
|
1510
|
+
// ------------------------------------------------------------------
|
|
1511
|
+
// experimental.chat.system.transform — capture system prompt (read-only)
|
|
1512
|
+
// ------------------------------------------------------------------
|
|
1513
|
+
"experimental.chat.system.transform": async (input, output) => {
|
|
1514
|
+
try {
|
|
1515
|
+
if (!config.captureSystemPrompt) return;
|
|
1516
|
+
const sessionID = input.sessionID;
|
|
1517
|
+
if (!sessionID) return;
|
|
1518
|
+
let state = sessions.get(sessionID);
|
|
1519
|
+
if (!state) {
|
|
1520
|
+
state = createSessionState(sessionID);
|
|
1521
|
+
sessions.set(sessionID, state);
|
|
1522
|
+
}
|
|
1523
|
+
const MAX_SYSTEM_PROMPT_LENGTH = 32768;
|
|
1524
|
+
const TRUNCATION_MARKER = "\n...[truncated]";
|
|
1525
|
+
const joined = output.system.join("\n\n");
|
|
1526
|
+
state.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH - TRUNCATION_MARKER.length) + TRUNCATION_MARKER : joined;
|
|
1527
|
+
} catch (err) {
|
|
1528
|
+
console.log(
|
|
1529
|
+
`[raindrop-ai/opencode-plugin] [error] Error in experimental.chat.system.transform hook: ${err instanceof Error ? err.message : String(err)}`
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1392
1532
|
}
|
|
1393
1533
|
};
|
|
1394
1534
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raindrop-ai/opencode-plugin",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Raindrop observability plugin for OpenCode — automatic session/event/span tracing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"test": "cd tests && pnpm test"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"@opencode-ai/plugin": ">=1.
|
|
28
|
-
"@opencode-ai/sdk": ">=1.
|
|
27
|
+
"@opencode-ai/plugin": ">=1.3.0",
|
|
28
|
+
"@opencode-ai/sdk": ">=1.3.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependenciesMeta": {
|
|
31
31
|
"@opencode-ai/sdk": {
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@raindrop-ai/core": "workspace:*",
|
|
37
|
-
"@opencode-ai/plugin": "^1.
|
|
38
|
-
"@opencode-ai/sdk": "^1.
|
|
37
|
+
"@opencode-ai/plugin": "^1.3.3",
|
|
38
|
+
"@opencode-ai/sdk": "^1.3.3",
|
|
39
39
|
"@types/node": "^20.11.17",
|
|
40
40
|
"tsup": "^8.4.0",
|
|
41
41
|
"typescript": "^5.3.3"
|