@raindrop-ai/opencode-plugin 0.2.0 → 0.3.0
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 +39 -0
- package/dist/{chunk-4DAI6CYK.js → chunk-FCORH4YS.js} +217 -2
- package/dist/index.cjs +266 -23
- package/dist/index.d.cts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +78 -23
- package/dist/{index.d-vgBcEZbX.d.cts → self-diagnostics-NiAq-SZ8.d.cts} +34 -1
- package/dist/{index.d-vgBcEZbX.d.ts → self-diagnostics-NiAq-SZ8.d.ts} +34 -1
- package/dist/v2.cjs +253 -3
- package/dist/v2.d.cts +5 -2
- package/dist/v2.d.ts +5 -2
- package/dist/v2.js +46 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
EventShipper,
|
|
3
3
|
PLUGIN_NAME,
|
|
4
4
|
PLUGIN_VERSION,
|
|
5
|
+
SELF_DIAGNOSTICS_ACK,
|
|
5
6
|
TraceShipper,
|
|
6
7
|
attrString,
|
|
7
8
|
boundedStringify,
|
|
@@ -10,9 +11,14 @@ import {
|
|
|
10
11
|
createAppGitContext,
|
|
11
12
|
generateId,
|
|
12
13
|
loadConfig,
|
|
14
|
+
normalizeSelfDiagnosticsConfig,
|
|
13
15
|
nowUnixNanoString,
|
|
14
|
-
resolveLocalDebuggerBaseUrl
|
|
15
|
-
|
|
16
|
+
resolveLocalDebuggerBaseUrl,
|
|
17
|
+
trackSelfDiagnosticsSignal
|
|
18
|
+
} from "./chunk-FCORH4YS.js";
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
import { tool } from "@opencode-ai/plugin";
|
|
16
22
|
|
|
17
23
|
// src/tracing.ts
|
|
18
24
|
import { hostname as osHostname } from "os";
|
|
@@ -129,7 +135,7 @@ function markTaskCallFinished(sessionID, callID) {
|
|
|
129
135
|
running.delete(callID);
|
|
130
136
|
if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
|
|
131
137
|
}
|
|
132
|
-
function endPendingToolSpanWithError(state, callID,
|
|
138
|
+
function endPendingToolSpanWithError(state, callID, tool2, toolCallArgs, errorMessage, traceShipper) {
|
|
133
139
|
const startInfo = state.toolSpanStarts.get(callID);
|
|
134
140
|
if (!startInfo) return false;
|
|
135
141
|
state.toolSpanStarts.delete(callID);
|
|
@@ -151,7 +157,7 @@ function endPendingToolSpanWithError(state, callID, tool, toolCallArgs, errorMes
|
|
|
151
157
|
startTimeUnixNano: startInfo.startTimeUnixNano,
|
|
152
158
|
attributes: [
|
|
153
159
|
attrString("ai.operationId", "ai.toolCall"),
|
|
154
|
-
attrString("ai.toolCall.name",
|
|
160
|
+
attrString("ai.toolCall.name", tool2),
|
|
155
161
|
attrString("ai.toolCall.id", callID)
|
|
156
162
|
]
|
|
157
163
|
});
|
|
@@ -188,7 +194,7 @@ function getHostname() {
|
|
|
188
194
|
return h;
|
|
189
195
|
}
|
|
190
196
|
}
|
|
191
|
-
function
|
|
197
|
+
function createTracing(config, worktree, directory, eventShipper, traceShipper) {
|
|
192
198
|
function log(msg, data) {
|
|
193
199
|
if (!config.debug) return;
|
|
194
200
|
const prefix = `[raindrop-ai/opencode-plugin] [info] ${msg}`;
|
|
@@ -214,7 +220,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
214
220
|
callKey,
|
|
215
221
|
log
|
|
216
222
|
});
|
|
217
|
-
|
|
223
|
+
const hooks = {
|
|
218
224
|
// ------------------------------------------------------------------
|
|
219
225
|
// event — session lifecycle + streaming parts
|
|
220
226
|
// ------------------------------------------------------------------
|
|
@@ -259,14 +265,14 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
259
265
|
if (partObj["type"] === "text" && typeof partObj["text"] === "string") {
|
|
260
266
|
state.outputParts.set(messageId, capText(partObj["text"]));
|
|
261
267
|
} else if (partObj["type"] === "tool" && messageId) {
|
|
262
|
-
const
|
|
268
|
+
const tool2 = partObj["tool"];
|
|
263
269
|
const callID = partObj["callID"];
|
|
264
270
|
const partState = partObj["state"];
|
|
265
271
|
if (callID == null || callID === "")
|
|
266
272
|
throw new Error("message.part.updated: part.callID is required for tool part");
|
|
267
273
|
if (partState == null || typeof partState !== "object")
|
|
268
274
|
throw new Error("message.part.updated: part.state is required for tool part");
|
|
269
|
-
if (
|
|
275
|
+
if (tool2 === "task") {
|
|
270
276
|
const metadata = partState["metadata"];
|
|
271
277
|
const childSessionId = metadata != null && typeof metadata === "object" ? metadata["sessionId"] : void 0;
|
|
272
278
|
if (childSessionId != null && childSessionId !== "") {
|
|
@@ -291,11 +297,11 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
291
297
|
if (endPendingToolSpanWithError(
|
|
292
298
|
state,
|
|
293
299
|
callID,
|
|
294
|
-
|
|
300
|
+
tool2,
|
|
295
301
|
toolCallArgs,
|
|
296
302
|
errorMessage,
|
|
297
303
|
traceShipper
|
|
298
|
-
) &&
|
|
304
|
+
) && tool2 === "task") {
|
|
299
305
|
markTaskCallFinished(partSessionID, callID);
|
|
300
306
|
}
|
|
301
307
|
}
|
|
@@ -672,20 +678,20 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
672
678
|
"tool.execute.before": async (toolInput, _output) => {
|
|
673
679
|
var _a, _b, _c;
|
|
674
680
|
try {
|
|
675
|
-
const { tool, sessionID, callID } = toolInput;
|
|
681
|
+
const { tool: tool2, sessionID, callID } = toolInput;
|
|
676
682
|
const state = sessions.get(sessionID);
|
|
677
683
|
if (!state || !state.currentEventId) return;
|
|
678
684
|
const spanParent = state.parentId && state.parentTaskSpanIds ? state.parentTaskSpanIds : (_a = state.currentRootSpan) == null ? void 0 : _a.ids;
|
|
679
685
|
if (!spanParent) return;
|
|
680
686
|
const startTimeUnixNano = nowUnixNanoString();
|
|
681
|
-
if (
|
|
687
|
+
if (tool2 === "task") {
|
|
682
688
|
const liveTaskSpan = traceShipper.startSpan({
|
|
683
689
|
name: "ai.toolCall",
|
|
684
690
|
parent: spanParent,
|
|
685
691
|
eventId: state.currentEventId,
|
|
686
692
|
attributes: [
|
|
687
693
|
attrString("ai.operationId", "ai.toolCall"),
|
|
688
|
-
attrString("ai.toolCall.name",
|
|
694
|
+
attrString("ai.toolCall.name", tool2),
|
|
689
695
|
attrString("ai.toolCall.id", callID)
|
|
690
696
|
]
|
|
691
697
|
});
|
|
@@ -710,7 +716,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
710
716
|
startTimeUnixNano,
|
|
711
717
|
parent: spanParent,
|
|
712
718
|
eventId: state.currentEventId,
|
|
713
|
-
name:
|
|
719
|
+
name: tool2,
|
|
714
720
|
liveSpan: liveTaskSpan
|
|
715
721
|
});
|
|
716
722
|
} else {
|
|
@@ -718,7 +724,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
718
724
|
startTimeUnixNano,
|
|
719
725
|
parent: spanParent,
|
|
720
726
|
eventId: state.currentEventId,
|
|
721
|
-
name:
|
|
727
|
+
name: tool2
|
|
722
728
|
});
|
|
723
729
|
}
|
|
724
730
|
} catch (err) {
|
|
@@ -733,7 +739,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
733
739
|
// ------------------------------------------------------------------
|
|
734
740
|
"tool.execute.after": async (toolInput, result) => {
|
|
735
741
|
try {
|
|
736
|
-
const { tool, sessionID, callID, args } = toolInput;
|
|
742
|
+
const { tool: tool2, sessionID, callID, args } = toolInput;
|
|
737
743
|
const state = sessions.get(sessionID);
|
|
738
744
|
if (!state || !state.currentEventId) return;
|
|
739
745
|
if (args === void 0 || args === null)
|
|
@@ -743,7 +749,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
743
749
|
state.toolSpanStarts.delete(callID);
|
|
744
750
|
const resultMetadata = result.metadata;
|
|
745
751
|
const childSessionId = resultMetadata == null ? void 0 : resultMetadata["sessionId"];
|
|
746
|
-
if (
|
|
752
|
+
if (tool2 === "task" && childSessionId) {
|
|
747
753
|
mapChildSessionToParent.set(childSessionId, { parentId: sessionID });
|
|
748
754
|
attachChildSessionToParentTask(childSessionId, sessionID, callID, "tool.execute.after");
|
|
749
755
|
}
|
|
@@ -768,7 +774,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
768
774
|
endTimeUnixNano,
|
|
769
775
|
attributes: [
|
|
770
776
|
attrString("ai.operationId", "ai.toolCall"),
|
|
771
|
-
attrString("ai.toolCall.name",
|
|
777
|
+
attrString("ai.toolCall.name", tool2),
|
|
772
778
|
attrString("ai.toolCall.id", callID),
|
|
773
779
|
attrString("ai.toolCall.args", toolCallArgs),
|
|
774
780
|
attrString("ai.toolCall.result", toolResult)
|
|
@@ -776,7 +782,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
776
782
|
});
|
|
777
783
|
}
|
|
778
784
|
}
|
|
779
|
-
if (
|
|
785
|
+
if (tool2 === "task") {
|
|
780
786
|
markTaskCallFinished(sessionID, callID);
|
|
781
787
|
}
|
|
782
788
|
} catch (err) {
|
|
@@ -829,16 +835,24 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
829
835
|
}
|
|
830
836
|
}
|
|
831
837
|
};
|
|
838
|
+
return {
|
|
839
|
+
hooks,
|
|
840
|
+
currentEventId(sessionID) {
|
|
841
|
+
var _a;
|
|
842
|
+
return (_a = sessions.get(sessionID)) == null ? void 0 : _a.currentEventId;
|
|
843
|
+
}
|
|
844
|
+
};
|
|
832
845
|
}
|
|
833
846
|
|
|
834
847
|
// src/index.ts
|
|
835
|
-
function createRaindropHandle(eventShipper, traceShipper, appGitContext) {
|
|
848
|
+
function createRaindropHandle(eventShipper, traceShipper, appGitContext, currentEventId) {
|
|
836
849
|
return {
|
|
837
850
|
users: {
|
|
838
851
|
async identify(users) {
|
|
839
852
|
await eventShipper.identify(users);
|
|
840
853
|
}
|
|
841
854
|
},
|
|
855
|
+
currentEventId,
|
|
842
856
|
async flush() {
|
|
843
857
|
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
844
858
|
},
|
|
@@ -855,7 +869,7 @@ function createRaindropHandle(eventShipper, traceShipper, appGitContext) {
|
|
|
855
869
|
};
|
|
856
870
|
}
|
|
857
871
|
async function plugin(input, opts = {}) {
|
|
858
|
-
var _a, _b, _c;
|
|
872
|
+
var _a, _b, _c, _d;
|
|
859
873
|
const config = loadConfig(input.directory);
|
|
860
874
|
function appLog(level, message) {
|
|
861
875
|
console.log(`[raindrop-ai/opencode-plugin] [${level}] ${message}`);
|
|
@@ -899,14 +913,55 @@ async function plugin(input, opts = {}) {
|
|
|
899
913
|
appGitContext
|
|
900
914
|
});
|
|
901
915
|
const worktree = (_c = input.worktree) != null ? _c : input.directory;
|
|
902
|
-
const
|
|
916
|
+
const tracing = createTracing(
|
|
903
917
|
config,
|
|
904
918
|
worktree,
|
|
905
919
|
input.directory,
|
|
906
920
|
eventShipper,
|
|
907
921
|
traceShipper
|
|
908
922
|
);
|
|
909
|
-
|
|
923
|
+
const hooks = tracing.hooks;
|
|
924
|
+
const selfDiagnostics = normalizeSelfDiagnosticsConfig(
|
|
925
|
+
(_d = opts.selfDiagnostics) != null ? _d : config.selfDiagnostics
|
|
926
|
+
);
|
|
927
|
+
if (selfDiagnostics && !config.writeKey.trim()) {
|
|
928
|
+
appLog(
|
|
929
|
+
"warn",
|
|
930
|
+
`selfDiagnostics enabled but RAINDROP_WRITE_KEY is not set \u2014 the ${selfDiagnostics.toolName} tool is not registered (signals require the cloud destination).`
|
|
931
|
+
);
|
|
932
|
+
}
|
|
933
|
+
const [firstSignalKey, ...restSignalKeys] = config.writeKey.trim() && selfDiagnostics ? selfDiagnostics.signalKeys : [];
|
|
934
|
+
if (selfDiagnostics && firstSignalKey !== void 0) {
|
|
935
|
+
const cfg = selfDiagnostics;
|
|
936
|
+
hooks.tool = {
|
|
937
|
+
[cfg.toolName]: tool({
|
|
938
|
+
description: cfg.toolDescription,
|
|
939
|
+
args: {
|
|
940
|
+
category: tool.schema.enum([firstSignalKey, ...restSignalKeys]),
|
|
941
|
+
detail: tool.schema.string()
|
|
942
|
+
},
|
|
943
|
+
async execute(args, ctx) {
|
|
944
|
+
await trackSelfDiagnosticsSignal(
|
|
945
|
+
args,
|
|
946
|
+
cfg,
|
|
947
|
+
eventShipper,
|
|
948
|
+
tracing.currentEventId(ctx.sessionID),
|
|
949
|
+
config.debug
|
|
950
|
+
);
|
|
951
|
+
return SELF_DIAGNOSTICS_ACK;
|
|
952
|
+
}
|
|
953
|
+
})
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
return {
|
|
957
|
+
...hooks,
|
|
958
|
+
raindrop: createRaindropHandle(
|
|
959
|
+
eventShipper,
|
|
960
|
+
traceShipper,
|
|
961
|
+
appGitContext,
|
|
962
|
+
tracing.currentEventId
|
|
963
|
+
)
|
|
964
|
+
};
|
|
910
965
|
}
|
|
911
966
|
export {
|
|
912
967
|
plugin as default
|
|
@@ -86,4 +86,37 @@ declare global {
|
|
|
86
86
|
var RAINDROP_ASYNC_LOCAL_STORAGE: (new <T>() => AsyncLocalStorageLike<T>) | undefined;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
type SelfDiagnosticsSignalDefinition = {
|
|
90
|
+
/**
|
|
91
|
+
* Human-readable meaning of this signal.
|
|
92
|
+
* Used in the injected tool schema and stored in signal properties.
|
|
93
|
+
*/
|
|
94
|
+
description: string;
|
|
95
|
+
/**
|
|
96
|
+
* Optional default sentiment for this signal key.
|
|
97
|
+
* This can help dashboards classify negative/positive programmatic signals.
|
|
98
|
+
*/
|
|
99
|
+
sentiment?: "POSITIVE" | "NEGATIVE";
|
|
100
|
+
};
|
|
101
|
+
type SelfDiagnosticsSignalDefinitions = Record<string, SelfDiagnosticsSignalDefinition>;
|
|
102
|
+
type SelfDiagnosticsOptions = {
|
|
103
|
+
/** Enable automatic injection of the self diagnostics tool. Default: false */
|
|
104
|
+
enabled?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Signal keys and descriptions exposed to the model.
|
|
107
|
+
* Defaults to a built-in set when omitted.
|
|
108
|
+
*/
|
|
109
|
+
signals?: SelfDiagnosticsSignalDefinitions;
|
|
110
|
+
/**
|
|
111
|
+
* Optional extra guidance for when the model should emit self diagnostics.
|
|
112
|
+
* The SDK still generates a full tool prompt from `signals`.
|
|
113
|
+
*/
|
|
114
|
+
guidance?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Optional tool name override for the injected self diagnostics tool.
|
|
117
|
+
* Defaults to "__raindrop_report".
|
|
118
|
+
*/
|
|
119
|
+
toolName?: string;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export type { AppGitOptions as A, IdentifyInput as I, SelfDiagnosticsOptions as S, SelfDiagnosticsSignalDefinition as a, SelfDiagnosticsSignalDefinitions as b, SignalInput as c };
|
|
@@ -86,4 +86,37 @@ declare global {
|
|
|
86
86
|
var RAINDROP_ASYNC_LOCAL_STORAGE: (new <T>() => AsyncLocalStorageLike<T>) | undefined;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
type SelfDiagnosticsSignalDefinition = {
|
|
90
|
+
/**
|
|
91
|
+
* Human-readable meaning of this signal.
|
|
92
|
+
* Used in the injected tool schema and stored in signal properties.
|
|
93
|
+
*/
|
|
94
|
+
description: string;
|
|
95
|
+
/**
|
|
96
|
+
* Optional default sentiment for this signal key.
|
|
97
|
+
* This can help dashboards classify negative/positive programmatic signals.
|
|
98
|
+
*/
|
|
99
|
+
sentiment?: "POSITIVE" | "NEGATIVE";
|
|
100
|
+
};
|
|
101
|
+
type SelfDiagnosticsSignalDefinitions = Record<string, SelfDiagnosticsSignalDefinition>;
|
|
102
|
+
type SelfDiagnosticsOptions = {
|
|
103
|
+
/** Enable automatic injection of the self diagnostics tool. Default: false */
|
|
104
|
+
enabled?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Signal keys and descriptions exposed to the model.
|
|
107
|
+
* Defaults to a built-in set when omitted.
|
|
108
|
+
*/
|
|
109
|
+
signals?: SelfDiagnosticsSignalDefinitions;
|
|
110
|
+
/**
|
|
111
|
+
* Optional extra guidance for when the model should emit self diagnostics.
|
|
112
|
+
* The SDK still generates a full tool prompt from `signals`.
|
|
113
|
+
*/
|
|
114
|
+
guidance?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Optional tool name override for the injected self diagnostics tool.
|
|
117
|
+
* Defaults to "__raindrop_report".
|
|
118
|
+
*/
|
|
119
|
+
toolName?: string;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export type { AppGitOptions as A, IdentifyInput as I, SelfDiagnosticsOptions as S, SelfDiagnosticsSignalDefinition as a, SelfDiagnosticsSignalDefinitions as b, SignalInput as c };
|
package/dist/v2.cjs
CHANGED
|
@@ -2309,9 +2309,41 @@ function loadConfig(projectDirectory) {
|
|
|
2309
2309
|
captureSystemPrompt: process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] !== void 0 ? process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] === "true" : (_h = merged.capture_system_prompt) != null ? _h : false,
|
|
2310
2310
|
eventMetadata,
|
|
2311
2311
|
localWorkshopUrl: resolveLocalWorkshopUrl(merged.local_workshop_url),
|
|
2312
|
-
appGit: mapAppGit(merged.app_git)
|
|
2312
|
+
appGit: mapAppGit(merged.app_git),
|
|
2313
|
+
selfDiagnostics: mapSelfDiagnostics(merged.self_diagnostics)
|
|
2313
2314
|
};
|
|
2314
2315
|
}
|
|
2316
|
+
function isRecord(value) {
|
|
2317
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2318
|
+
}
|
|
2319
|
+
function mapSelfDiagnostics(value) {
|
|
2320
|
+
const env = process.env["RAINDROP_SELF_DIAGNOSTICS"];
|
|
2321
|
+
const record = isRecord(value) ? value : {};
|
|
2322
|
+
const enabled = env !== void 0 ? env === "true" : typeof record["enabled"] === "boolean" ? record["enabled"] : void 0;
|
|
2323
|
+
const signals = mapSelfDiagnosticsSignals(record["signals"]);
|
|
2324
|
+
const options = {
|
|
2325
|
+
...enabled !== void 0 ? { enabled } : {},
|
|
2326
|
+
...signals ? { signals } : {},
|
|
2327
|
+
...typeof record["guidance"] === "string" ? { guidance: record["guidance"] } : {},
|
|
2328
|
+
...typeof record["tool_name"] === "string" ? { toolName: record["tool_name"] } : {}
|
|
2329
|
+
};
|
|
2330
|
+
return Object.keys(options).length > 0 ? options : void 0;
|
|
2331
|
+
}
|
|
2332
|
+
function mapSelfDiagnosticsSignals(value) {
|
|
2333
|
+
if (!isRecord(value)) return void 0;
|
|
2334
|
+
const out = {};
|
|
2335
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2336
|
+
if (!isRecord(entry)) continue;
|
|
2337
|
+
const definition = entry;
|
|
2338
|
+
if (typeof definition["description"] !== "string") continue;
|
|
2339
|
+
const sentiment = definition["sentiment"];
|
|
2340
|
+
out[key] = {
|
|
2341
|
+
description: definition["description"],
|
|
2342
|
+
...sentiment === "POSITIVE" || sentiment === "NEGATIVE" ? { sentiment } : {}
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
2346
|
+
}
|
|
2315
2347
|
function mapAppGit(value) {
|
|
2316
2348
|
if (value === false) return false;
|
|
2317
2349
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
@@ -2338,7 +2370,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
2338
2370
|
// package.json
|
|
2339
2371
|
var package_default = {
|
|
2340
2372
|
name: "@raindrop-ai/opencode-plugin",
|
|
2341
|
-
version: "0.
|
|
2373
|
+
version: "0.3.0",
|
|
2342
2374
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
2343
2375
|
type: "module",
|
|
2344
2376
|
main: "dist/index.js",
|
|
@@ -2427,6 +2459,185 @@ var package_default = {
|
|
|
2427
2459
|
var PLUGIN_NAME = package_default.name;
|
|
2428
2460
|
var PLUGIN_VERSION = package_default.version;
|
|
2429
2461
|
|
|
2462
|
+
// src/self-diagnostics.ts
|
|
2463
|
+
var SELF_DIAGNOSTICS_TOOL_NAME_DEFAULT = "__raindrop_report";
|
|
2464
|
+
var SELF_DIAGNOSTICS_SIGNALS_DEFAULT = {
|
|
2465
|
+
missing_context: {
|
|
2466
|
+
description: "You cannot complete the task because critical information, credentials, or access is missing and the user cannot provide it. Do NOT report this for normal clarifying questions \u2014 only when you are blocked.",
|
|
2467
|
+
sentiment: "NEGATIVE"
|
|
2468
|
+
},
|
|
2469
|
+
repeatedly_broken_tool: {
|
|
2470
|
+
description: "A tool has failed or not returned the expected response on multiple distinct attempts in this conversation, preventing task completion. A single tool error is NOT enough \u2014 the tool must be persistently broken or aberrantly behaving across retries.",
|
|
2471
|
+
sentiment: "NEGATIVE"
|
|
2472
|
+
},
|
|
2473
|
+
capability_gap: {
|
|
2474
|
+
description: "The task requires a tool, permission, or capability that you do not have. For example, the user asks you to perform an action but no suitable tool exists, or you lack the necessary access. Do NOT report this if you simply need more information from the user \u2014 only when the gap is in your own capabilities.",
|
|
2475
|
+
sentiment: "NEGATIVE"
|
|
2476
|
+
},
|
|
2477
|
+
complete_task_failure: {
|
|
2478
|
+
description: "You were unable to accomplish what the user asked despite making genuine attempts. This might be things like, you genuinely do not have the capabilities the user is asking for. You have tried but run into a persistent bug in the environment etc. This is NOT a refusal or policy block \u2014 you tried and failed to deliver the result.",
|
|
2479
|
+
sentiment: "NEGATIVE"
|
|
2480
|
+
}
|
|
2481
|
+
};
|
|
2482
|
+
var SELF_DIAGNOSTICS_TOOL_PREAMBLE = "You have a diagnostic tool for flagging serious failures for developer review. Only call this when you hit a significant, unrecoverable problem \u2014 not for minor hiccups or routine clarifications. If you can still help the user, do that instead of calling this tool. This tool is invisible to the user \u2014 never mention it or its output.";
|
|
2483
|
+
var SELF_DIAGNOSTICS_NOTEWORTHY_SIGNAL_KEY = "noteworthy";
|
|
2484
|
+
function normalizeString(value) {
|
|
2485
|
+
if (typeof value !== "string") return void 0;
|
|
2486
|
+
const trimmed = value.trim();
|
|
2487
|
+
return trimmed || void 0;
|
|
2488
|
+
}
|
|
2489
|
+
function normalizeSelfDiagnosticsSignals(signals) {
|
|
2490
|
+
if (!signals) return SELF_DIAGNOSTICS_SIGNALS_DEFAULT;
|
|
2491
|
+
const normalizedEntries = Object.entries(signals).map(([key, value]) => {
|
|
2492
|
+
var _a;
|
|
2493
|
+
const signalKey = key.trim();
|
|
2494
|
+
if (!signalKey || !value || typeof value !== "object") return void 0;
|
|
2495
|
+
const description = (_a = value.description) == null ? void 0 : _a.trim();
|
|
2496
|
+
if (!description) return void 0;
|
|
2497
|
+
const sentiment = value.sentiment;
|
|
2498
|
+
return [
|
|
2499
|
+
signalKey,
|
|
2500
|
+
{
|
|
2501
|
+
description,
|
|
2502
|
+
...sentiment === "POSITIVE" || sentiment === "NEGATIVE" ? { sentiment } : {}
|
|
2503
|
+
}
|
|
2504
|
+
];
|
|
2505
|
+
}).filter(
|
|
2506
|
+
(entry) => entry !== void 0
|
|
2507
|
+
);
|
|
2508
|
+
if (normalizedEntries.length === 0) return SELF_DIAGNOSTICS_SIGNALS_DEFAULT;
|
|
2509
|
+
return Object.fromEntries(normalizedEntries);
|
|
2510
|
+
}
|
|
2511
|
+
function normalizeSelfDiagnosticsConfig(options) {
|
|
2512
|
+
var _a, _b;
|
|
2513
|
+
if (!(options == null ? void 0 : options.enabled)) return void 0;
|
|
2514
|
+
const signalDefinitions = normalizeSelfDiagnosticsSignals(options.signals);
|
|
2515
|
+
const signalKeys = Object.keys(signalDefinitions);
|
|
2516
|
+
const signalDescriptions = {};
|
|
2517
|
+
const signalSentiments = {};
|
|
2518
|
+
for (const signalKey of signalKeys) {
|
|
2519
|
+
const definition = signalDefinitions[signalKey];
|
|
2520
|
+
if (!definition) continue;
|
|
2521
|
+
signalDescriptions[signalKey] = definition.description;
|
|
2522
|
+
signalSentiments[signalKey] = definition.sentiment;
|
|
2523
|
+
}
|
|
2524
|
+
const customGuidance = ((_a = options.guidance) == null ? void 0 : _a.trim()) || "";
|
|
2525
|
+
const toolName = ((_b = options.toolName) == null ? void 0 : _b.trim()) || SELF_DIAGNOSTICS_TOOL_NAME_DEFAULT;
|
|
2526
|
+
const signalList = signalKeys.map((signalKey) => {
|
|
2527
|
+
const sentiment = signalSentiments[signalKey];
|
|
2528
|
+
const sentimentTag = sentiment ? ` [${sentiment.toLowerCase()}]` : "";
|
|
2529
|
+
return `- ${signalKey}: ${signalDescriptions[signalKey]}${sentimentTag}`;
|
|
2530
|
+
}).join("\n");
|
|
2531
|
+
const guidanceBlock = customGuidance ? `
|
|
2532
|
+
Additional guidance: ${customGuidance}
|
|
2533
|
+
` : "";
|
|
2534
|
+
const toolDescription = `${SELF_DIAGNOSTICS_TOOL_PREAMBLE}
|
|
2535
|
+
|
|
2536
|
+
When to call:
|
|
2537
|
+
- You are blocked from completing the task due to missing information or access that the user cannot provide.
|
|
2538
|
+
- A tool is persistently failing across multiple attempts, not just a single transient error.
|
|
2539
|
+
- The task requires a tool, permission, or capability you do not have.
|
|
2540
|
+
- You genuinely cannot deliver what the user asked for despite trying.
|
|
2541
|
+
|
|
2542
|
+
When NOT to call:
|
|
2543
|
+
- Normal clarifying questions or back-and-forth with the user.
|
|
2544
|
+
- A single tool error that you can recover from or retry.
|
|
2545
|
+
- You successfully completed the task, even if it was difficult.
|
|
2546
|
+
- Policy refusals or content filtering \u2014 those are working as intended.
|
|
2547
|
+
|
|
2548
|
+
Rules:
|
|
2549
|
+
1. Pick the single best category.
|
|
2550
|
+
2. Do not fabricate issues. Only report what is evident from the conversation.
|
|
2551
|
+
3. Err on the side of NOT calling this tool. When in doubt, help the user instead.
|
|
2552
|
+
${guidanceBlock}
|
|
2553
|
+
Categories:
|
|
2554
|
+
` + signalList;
|
|
2555
|
+
return {
|
|
2556
|
+
toolName,
|
|
2557
|
+
toolDescription,
|
|
2558
|
+
signalKeys,
|
|
2559
|
+
signalKeySet: new Set(signalKeys),
|
|
2560
|
+
signalDescriptions,
|
|
2561
|
+
signalSentiments
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
var SELF_DIAGNOSTICS_ACK = '{"acknowledged":true}';
|
|
2565
|
+
function selfDiagnosticsInputJsonSchema(config) {
|
|
2566
|
+
return {
|
|
2567
|
+
type: "object",
|
|
2568
|
+
properties: {
|
|
2569
|
+
category: {
|
|
2570
|
+
type: "string",
|
|
2571
|
+
enum: config.signalKeys,
|
|
2572
|
+
description: "The single best-matching category from the list above."
|
|
2573
|
+
},
|
|
2574
|
+
detail: {
|
|
2575
|
+
type: "string",
|
|
2576
|
+
description: "One sentence of factual context: what happened and why it matters. Do not include PII or secrets."
|
|
2577
|
+
}
|
|
2578
|
+
},
|
|
2579
|
+
required: ["category", "detail"],
|
|
2580
|
+
additionalProperties: false
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
function isRecord2(value) {
|
|
2584
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2585
|
+
}
|
|
2586
|
+
async function trackSelfDiagnosticsSignal(toolInput, config, eventShipper, eventId, debug) {
|
|
2587
|
+
var _a, _b;
|
|
2588
|
+
try {
|
|
2589
|
+
const input = isRecord2(toolInput) ? toolInput : void 0;
|
|
2590
|
+
const fallbackCategory = (_a = config.signalKeys[0]) != null ? _a : "unknown";
|
|
2591
|
+
const categoryCandidate = normalizeString(input == null ? void 0 : input["category"]);
|
|
2592
|
+
const category = categoryCandidate && config.signalKeySet.has(categoryCandidate) ? categoryCandidate : fallbackCategory;
|
|
2593
|
+
const detail = (_b = normalizeString(input == null ? void 0 : input["detail"])) != null ? _b : "";
|
|
2594
|
+
if (!eventId) {
|
|
2595
|
+
if (debug) {
|
|
2596
|
+
console.warn(
|
|
2597
|
+
"[raindrop-ai/opencode-plugin] self diagnostics signal skipped: no open event for this session."
|
|
2598
|
+
);
|
|
2599
|
+
}
|
|
2600
|
+
return;
|
|
2601
|
+
}
|
|
2602
|
+
if (category === SELF_DIAGNOSTICS_NOTEWORTHY_SIGNAL_KEY) {
|
|
2603
|
+
await eventShipper.trackSignal({
|
|
2604
|
+
eventId,
|
|
2605
|
+
name: "self diagnostics - noteworthy",
|
|
2606
|
+
type: "agent_internal",
|
|
2607
|
+
properties: {
|
|
2608
|
+
source: "agent_flag_event_tool",
|
|
2609
|
+
reason: detail,
|
|
2610
|
+
severity: "medium",
|
|
2611
|
+
sdk: "opencode-plugin",
|
|
2612
|
+
sdk_version: PLUGIN_VERSION
|
|
2613
|
+
}
|
|
2614
|
+
});
|
|
2615
|
+
return;
|
|
2616
|
+
}
|
|
2617
|
+
await eventShipper.trackSignal({
|
|
2618
|
+
eventId,
|
|
2619
|
+
name: `self diagnostics - ${category}`,
|
|
2620
|
+
type: "agent",
|
|
2621
|
+
sentiment: config.signalSentiments[category],
|
|
2622
|
+
properties: {
|
|
2623
|
+
source: "agent_reporting_tool",
|
|
2624
|
+
category,
|
|
2625
|
+
signal_description: config.signalDescriptions[category],
|
|
2626
|
+
sdk: "opencode-plugin",
|
|
2627
|
+
sdk_version: PLUGIN_VERSION,
|
|
2628
|
+
...detail ? { detail } : {}
|
|
2629
|
+
}
|
|
2630
|
+
});
|
|
2631
|
+
} catch (err) {
|
|
2632
|
+
if (debug) {
|
|
2633
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2634
|
+
console.warn(
|
|
2635
|
+
`[raindrop-ai/opencode-plugin] selfDiagnostics signal dispatch failed: ${message}`
|
|
2636
|
+
);
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2430
2641
|
// src/shipper.ts
|
|
2431
2642
|
var EventShipper3 = class extends EventShipper2 {
|
|
2432
2643
|
constructor(opts) {
|
|
@@ -3223,7 +3434,7 @@ function raindrop(opts = {}) {
|
|
|
3223
3434
|
}
|
|
3224
3435
|
}
|
|
3225
3436
|
function createShared(ctx) {
|
|
3226
|
-
var _a, _b, _c;
|
|
3437
|
+
var _a, _b, _c, _d;
|
|
3227
3438
|
const config = loadConfig((_a = opts.directory) != null ? _a : ctx.location.directory);
|
|
3228
3439
|
const resolvedLocalUrl = resolveLocalDebuggerBaseUrl(config.localWorkshopUrl);
|
|
3229
3440
|
const hasLocalDestination = resolvedLocalUrl !== null;
|
|
@@ -3256,9 +3467,20 @@ function raindrop(opts = {}) {
|
|
|
3256
3467
|
};
|
|
3257
3468
|
const eventShipper = new EventShipper3(shipperOptions);
|
|
3258
3469
|
const traceShipper = new TraceShipper3(shipperOptions);
|
|
3470
|
+
const selfDiagnostics = normalizeSelfDiagnosticsConfig(
|
|
3471
|
+
(_d = opts.selfDiagnostics) != null ? _d : config.selfDiagnostics
|
|
3472
|
+
);
|
|
3473
|
+
if (selfDiagnostics && !config.writeKey.trim()) {
|
|
3474
|
+
appLog(
|
|
3475
|
+
"warn",
|
|
3476
|
+
`selfDiagnostics enabled but RAINDROP_WRITE_KEY is not set \u2014 the ${selfDiagnostics.toolName} tool is not registered (signals require the cloud destination).`
|
|
3477
|
+
);
|
|
3478
|
+
}
|
|
3259
3479
|
const s = {
|
|
3260
3480
|
eventShipper,
|
|
3261
3481
|
traceShipper,
|
|
3482
|
+
config,
|
|
3483
|
+
selfDiagnostics: config.writeKey.trim() ? selfDiagnostics : void 0,
|
|
3262
3484
|
appGitContext,
|
|
3263
3485
|
abort: new AbortController(),
|
|
3264
3486
|
instances: /* @__PURE__ */ new Set(),
|
|
@@ -3310,6 +3532,34 @@ function raindrop(opts = {}) {
|
|
|
3310
3532
|
const directory = ctx.location.directory;
|
|
3311
3533
|
instance.disposables.push(await ctx.session.hook("prompt", (e) => runtime.onPrompt(e, directory)));
|
|
3312
3534
|
instance.disposables.push(await ctx.session.hook("context", (e) => runtime.onContext(e)));
|
|
3535
|
+
const selfDiagnostics = s.selfDiagnostics;
|
|
3536
|
+
if (selfDiagnostics) {
|
|
3537
|
+
const cfg = selfDiagnostics;
|
|
3538
|
+
const eventShipper = s.eventShipper;
|
|
3539
|
+
const debug = s.config.debug;
|
|
3540
|
+
instance.disposables.push(
|
|
3541
|
+
await ctx.tool.transform(
|
|
3542
|
+
(editor) => editor.add({
|
|
3543
|
+
name: cfg.toolName,
|
|
3544
|
+
description: cfg.toolDescription,
|
|
3545
|
+
input: selfDiagnosticsInputJsonSchema(cfg),
|
|
3546
|
+
// Direct tool — the host routes everything else through the
|
|
3547
|
+
// codemode `execute` wrapper, where this name isn't callable.
|
|
3548
|
+
options: { codemode: false },
|
|
3549
|
+
async execute(input, context) {
|
|
3550
|
+
await trackSelfDiagnosticsSignal(
|
|
3551
|
+
input,
|
|
3552
|
+
cfg,
|
|
3553
|
+
eventShipper,
|
|
3554
|
+
runtime.currentEventId(context.sessionID),
|
|
3555
|
+
debug
|
|
3556
|
+
);
|
|
3557
|
+
return { content: SELF_DIAGNOSTICS_ACK };
|
|
3558
|
+
}
|
|
3559
|
+
})
|
|
3560
|
+
)
|
|
3561
|
+
);
|
|
3562
|
+
}
|
|
3313
3563
|
if (isFirst) void runEventLoop(s);
|
|
3314
3564
|
const owner = s;
|
|
3315
3565
|
return async () => {
|
package/dist/v2.d.cts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { Plugin } from '@opencode/plugin';
|
|
2
|
-
import { I as IdentifyInput,
|
|
2
|
+
import { I as IdentifyInput, c as SignalInput, A as AppGitOptions, S as SelfDiagnosticsOptions } from './self-diagnostics-NiAq-SZ8.cjs';
|
|
3
|
+
export { a as SelfDiagnosticsSignalDefinition, b as SelfDiagnosticsSignalDefinitions } from './self-diagnostics-NiAq-SZ8.cjs';
|
|
3
4
|
|
|
4
5
|
interface RaindropOpencodeV2Options {
|
|
5
6
|
/** Application Git identity override. false disables enrichment. */
|
|
6
7
|
appGit?: AppGitOptions | false;
|
|
7
8
|
/** Config directory override; defaults to the first instance's ctx.location.directory. */
|
|
8
9
|
directory?: string;
|
|
10
|
+
/** Built-in self-diagnostics: injects a `__raindrop_report` tool when enabled. */
|
|
11
|
+
selfDiagnostics?: SelfDiagnosticsOptions;
|
|
9
12
|
}
|
|
10
13
|
/**
|
|
11
14
|
* The Raindrop control handle exposed on the v2 plugin. Unlike v1 (where the
|
|
@@ -38,4 +41,4 @@ interface RaindropOpencodePluginV2 extends Plugin.Plugin {
|
|
|
38
41
|
*/
|
|
39
42
|
declare function raindrop(opts?: RaindropOpencodeV2Options): RaindropOpencodePluginV2;
|
|
40
43
|
|
|
41
|
-
export { IdentifyInput, type RaindropOpencodePluginV2, type RaindropOpencodeV2Handle, type RaindropOpencodeV2Options, SignalInput, raindrop as default };
|
|
44
|
+
export { IdentifyInput, type RaindropOpencodePluginV2, type RaindropOpencodeV2Handle, type RaindropOpencodeV2Options, SelfDiagnosticsOptions, SignalInput, raindrop as default };
|
package/dist/v2.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { Plugin } from '@opencode/plugin';
|
|
2
|
-
import { I as IdentifyInput,
|
|
2
|
+
import { I as IdentifyInput, c as SignalInput, A as AppGitOptions, S as SelfDiagnosticsOptions } from './self-diagnostics-NiAq-SZ8.js';
|
|
3
|
+
export { a as SelfDiagnosticsSignalDefinition, b as SelfDiagnosticsSignalDefinitions } from './self-diagnostics-NiAq-SZ8.js';
|
|
3
4
|
|
|
4
5
|
interface RaindropOpencodeV2Options {
|
|
5
6
|
/** Application Git identity override. false disables enrichment. */
|
|
6
7
|
appGit?: AppGitOptions | false;
|
|
7
8
|
/** Config directory override; defaults to the first instance's ctx.location.directory. */
|
|
8
9
|
directory?: string;
|
|
10
|
+
/** Built-in self-diagnostics: injects a `__raindrop_report` tool when enabled. */
|
|
11
|
+
selfDiagnostics?: SelfDiagnosticsOptions;
|
|
9
12
|
}
|
|
10
13
|
/**
|
|
11
14
|
* The Raindrop control handle exposed on the v2 plugin. Unlike v1 (where the
|
|
@@ -38,4 +41,4 @@ interface RaindropOpencodePluginV2 extends Plugin.Plugin {
|
|
|
38
41
|
*/
|
|
39
42
|
declare function raindrop(opts?: RaindropOpencodeV2Options): RaindropOpencodePluginV2;
|
|
40
43
|
|
|
41
|
-
export { IdentifyInput, type RaindropOpencodePluginV2, type RaindropOpencodeV2Handle, type RaindropOpencodeV2Options, SignalInput, raindrop as default };
|
|
44
|
+
export { IdentifyInput, type RaindropOpencodePluginV2, type RaindropOpencodeV2Handle, type RaindropOpencodeV2Options, SelfDiagnosticsOptions, SignalInput, raindrop as default };
|