@raindrop-ai/opencode-plugin 0.2.0 → 0.3.1
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 +94 -0
- package/dist/{chunk-4DAI6CYK.js → chunk-ZG47FVK7.js} +460 -3
- package/dist/index.cjs +534 -28
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +107 -27
- package/dist/{index.d-vgBcEZbX.d.cts → self-diagnostics-CLW6h4zB.d.cts} +60 -1
- package/dist/{index.d-vgBcEZbX.d.ts → self-diagnostics-CLW6h4zB.d.ts} +60 -1
- package/dist/v2.cjs +527 -14
- package/dist/v2.d.cts +10 -2
- package/dist/v2.d.ts +10 -2
- package/dist/v2.js +81 -13
- package/package.json +2 -2
package/dist/v2.cjs
CHANGED
|
@@ -87,7 +87,7 @@ function evalScopeAttributes() {
|
|
|
87
87
|
return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
// ../core/dist/chunk-
|
|
90
|
+
// ../core/dist/chunk-I6TVJQEC.js
|
|
91
91
|
var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
|
|
92
92
|
function state() {
|
|
93
93
|
const holder = globalThis;
|
|
@@ -2096,6 +2096,151 @@ var TraceShipper = class {
|
|
|
2096
2096
|
}
|
|
2097
2097
|
}
|
|
2098
2098
|
};
|
|
2099
|
+
var PROMPT_TOOLS_ATTRIBUTE = "ai.prompt.tools";
|
|
2100
|
+
function isRecord(value) {
|
|
2101
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2102
|
+
}
|
|
2103
|
+
var JSON_SCHEMA_KEYS = [
|
|
2104
|
+
"type",
|
|
2105
|
+
"properties",
|
|
2106
|
+
"$schema",
|
|
2107
|
+
"$ref",
|
|
2108
|
+
"anyOf",
|
|
2109
|
+
"oneOf",
|
|
2110
|
+
"allOf",
|
|
2111
|
+
"enum",
|
|
2112
|
+
"const",
|
|
2113
|
+
"items",
|
|
2114
|
+
"additionalProperties",
|
|
2115
|
+
"required"
|
|
2116
|
+
];
|
|
2117
|
+
function isJsonSchema(value) {
|
|
2118
|
+
if (!isRecord(value)) return false;
|
|
2119
|
+
if (typeof value["~standard"] === "object") return false;
|
|
2120
|
+
if (isRecord(value._def) || isRecord(value._zod)) return false;
|
|
2121
|
+
if (Object.keys(value).length === 0) return true;
|
|
2122
|
+
return JSON_SCHEMA_KEYS.some((key) => key in value);
|
|
2123
|
+
}
|
|
2124
|
+
function toJsonSchema(value, options) {
|
|
2125
|
+
if (value === void 0 || value === null) return void 0;
|
|
2126
|
+
if (isJsonSchema(value)) return value;
|
|
2127
|
+
if (isRecord(value) && isJsonSchema(value.jsonSchema)) return value.jsonSchema;
|
|
2128
|
+
if (isRecord(value) && isJsonSchema(value.json)) return value.json;
|
|
2129
|
+
if (options == null ? void 0 : options.schema) {
|
|
2130
|
+
try {
|
|
2131
|
+
return options.schema(value);
|
|
2132
|
+
} catch (e) {
|
|
2133
|
+
return void 0;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
return void 0;
|
|
2137
|
+
}
|
|
2138
|
+
function pickString(value) {
|
|
2139
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
2140
|
+
}
|
|
2141
|
+
function functionTool(name, description, schema, options) {
|
|
2142
|
+
const tool = { type: "function", name };
|
|
2143
|
+
const desc = pickString(description);
|
|
2144
|
+
if (desc !== void 0) tool.description = desc;
|
|
2145
|
+
const inputSchema = toJsonSchema(schema, options);
|
|
2146
|
+
if (inputSchema !== void 0) tool.inputSchema = inputSchema;
|
|
2147
|
+
return tool;
|
|
2148
|
+
}
|
|
2149
|
+
function normalizeToolDeclaration(tool, options, fallbackName) {
|
|
2150
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
2151
|
+
if (!isRecord(tool)) return void 0;
|
|
2152
|
+
if (tool.type === "provider-defined" || tool.type === "provider") {
|
|
2153
|
+
const name2 = (_b = (_a = pickString(tool.name)) != null ? _a : pickString(tool.id)) != null ? _b : fallbackName;
|
|
2154
|
+
if (name2 === void 0) return void 0;
|
|
2155
|
+
const declaration = { type: "provider-defined", name: name2 };
|
|
2156
|
+
const id = pickString(tool.id);
|
|
2157
|
+
if (id !== void 0) declaration.id = id;
|
|
2158
|
+
if (isRecord(tool.args)) declaration.args = tool.args;
|
|
2159
|
+
return declaration;
|
|
2160
|
+
}
|
|
2161
|
+
if (isRecord(tool.toolSpec)) {
|
|
2162
|
+
const spec = tool.toolSpec;
|
|
2163
|
+
const name2 = (_c = pickString(spec.name)) != null ? _c : fallbackName;
|
|
2164
|
+
if (name2 === void 0) return void 0;
|
|
2165
|
+
return functionTool(name2, spec.description, spec.inputSchema, options);
|
|
2166
|
+
}
|
|
2167
|
+
if (isRecord(tool.function)) {
|
|
2168
|
+
const fn = tool.function;
|
|
2169
|
+
const name2 = (_d = pickString(fn.name)) != null ? _d : fallbackName;
|
|
2170
|
+
if (name2 === void 0) return void 0;
|
|
2171
|
+
return functionTool(name2, fn.description, (_e = fn.parameters) != null ? _e : fn.input_schema, options);
|
|
2172
|
+
}
|
|
2173
|
+
const name = (_g = (_f = pickString(tool.name)) != null ? _f : pickString(tool.id)) != null ? _g : fallbackName;
|
|
2174
|
+
if (name === void 0) return void 0;
|
|
2175
|
+
const type = pickString(tool.type);
|
|
2176
|
+
if (type !== void 0 && type !== "function" && type !== "dynamic" && type !== "custom") {
|
|
2177
|
+
if (!("input_schema" in tool) && !("inputSchema" in tool) && !("parameters" in tool)) {
|
|
2178
|
+
return { type: "provider-defined", id: type, name };
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
return functionTool(
|
|
2182
|
+
name,
|
|
2183
|
+
tool.description,
|
|
2184
|
+
(_j = (_i = (_h = tool.inputSchema) != null ? _h : tool.input_schema) != null ? _i : tool.parameters) != null ? _j : tool.schema,
|
|
2185
|
+
options
|
|
2186
|
+
);
|
|
2187
|
+
}
|
|
2188
|
+
var NOT_A_GEMINI_GROUP_KEYS = ["name", "id", "type", "function", "toolSpec", "cachePoint"];
|
|
2189
|
+
function isGeminiToolGroup(tool) {
|
|
2190
|
+
if (Array.isArray(tool.functionDeclarations)) return true;
|
|
2191
|
+
const keys = Object.keys(tool);
|
|
2192
|
+
if (keys.length === 0 || NOT_A_GEMINI_GROUP_KEYS.some((key) => key in tool)) return false;
|
|
2193
|
+
return keys.every((key) => isRecord(tool[key]));
|
|
2194
|
+
}
|
|
2195
|
+
function expandGeminiTool(tool, options) {
|
|
2196
|
+
const declarations = [];
|
|
2197
|
+
for (const [key, value] of Object.entries(tool)) {
|
|
2198
|
+
if (key === "functionDeclarations" && Array.isArray(value)) {
|
|
2199
|
+
for (const declaration of value) {
|
|
2200
|
+
const normalized = normalizeToolDeclaration(declaration, options);
|
|
2201
|
+
if (normalized) declarations.push(normalized);
|
|
2202
|
+
}
|
|
2203
|
+
} else if (isRecord(value)) {
|
|
2204
|
+
declarations.push({ type: "provider-defined", id: key, name: key, args: value });
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
return declarations;
|
|
2208
|
+
}
|
|
2209
|
+
function normalizeToolDeclarations(tools, options) {
|
|
2210
|
+
if (tools === void 0 || tools === null) return void 0;
|
|
2211
|
+
if (Array.isArray(tools)) {
|
|
2212
|
+
return tools.flatMap((tool) => {
|
|
2213
|
+
if (isRecord(tool) && isGeminiToolGroup(tool)) {
|
|
2214
|
+
return expandGeminiTool(tool, options);
|
|
2215
|
+
}
|
|
2216
|
+
const declaration = normalizeToolDeclaration(tool, options);
|
|
2217
|
+
return declaration ? [declaration] : [];
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
if (!isRecord(tools)) return void 0;
|
|
2221
|
+
return Object.entries(tools).flatMap(([key, tool]) => {
|
|
2222
|
+
const declaration = normalizeToolDeclaration(tool, options, key);
|
|
2223
|
+
return declaration ? [declaration] : [];
|
|
2224
|
+
});
|
|
2225
|
+
}
|
|
2226
|
+
function promptToolsAttribute(tools) {
|
|
2227
|
+
const values = promptToolsAttributeValue(tools);
|
|
2228
|
+
if (values === void 0) return void 0;
|
|
2229
|
+
return {
|
|
2230
|
+
key: PROMPT_TOOLS_ATTRIBUTE,
|
|
2231
|
+
value: { arrayValue: { values: values.map((stringValue) => ({ stringValue })) } }
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
function promptToolsAttributeValue(tools) {
|
|
2235
|
+
if (tools === void 0) return void 0;
|
|
2236
|
+
return tools.map((tool) => {
|
|
2237
|
+
try {
|
|
2238
|
+
return JSON.stringify(tool);
|
|
2239
|
+
} catch (e) {
|
|
2240
|
+
return JSON.stringify({ type: tool.type, name: tool.name });
|
|
2241
|
+
}
|
|
2242
|
+
});
|
|
2243
|
+
}
|
|
2099
2244
|
|
|
2100
2245
|
// ../core/dist/index.node.js
|
|
2101
2246
|
var import_async_hooks = require("async_hooks");
|
|
@@ -2309,8 +2454,41 @@ function loadConfig(projectDirectory) {
|
|
|
2309
2454
|
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
2455
|
eventMetadata,
|
|
2311
2456
|
localWorkshopUrl: resolveLocalWorkshopUrl(merged.local_workshop_url),
|
|
2312
|
-
appGit: mapAppGit(merged.app_git)
|
|
2457
|
+
appGit: mapAppGit(merged.app_git),
|
|
2458
|
+
selfDiagnostics: mapSelfDiagnostics(merged.self_diagnostics),
|
|
2459
|
+
tools: Array.isArray(merged.tools) ? merged.tools : void 0
|
|
2460
|
+
};
|
|
2461
|
+
}
|
|
2462
|
+
function isRecord2(value) {
|
|
2463
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2464
|
+
}
|
|
2465
|
+
function mapSelfDiagnostics(value) {
|
|
2466
|
+
const env = process.env["RAINDROP_SELF_DIAGNOSTICS"];
|
|
2467
|
+
const record = isRecord2(value) ? value : {};
|
|
2468
|
+
const enabled = env !== void 0 ? env === "true" : typeof record["enabled"] === "boolean" ? record["enabled"] : void 0;
|
|
2469
|
+
const signals = mapSelfDiagnosticsSignals(record["signals"]);
|
|
2470
|
+
const options = {
|
|
2471
|
+
...enabled !== void 0 ? { enabled } : {},
|
|
2472
|
+
...signals ? { signals } : {},
|
|
2473
|
+
...typeof record["guidance"] === "string" ? { guidance: record["guidance"] } : {},
|
|
2474
|
+
...typeof record["tool_name"] === "string" ? { toolName: record["tool_name"] } : {}
|
|
2313
2475
|
};
|
|
2476
|
+
return Object.keys(options).length > 0 ? options : void 0;
|
|
2477
|
+
}
|
|
2478
|
+
function mapSelfDiagnosticsSignals(value) {
|
|
2479
|
+
if (!isRecord2(value)) return void 0;
|
|
2480
|
+
const out = {};
|
|
2481
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2482
|
+
if (!isRecord2(entry)) continue;
|
|
2483
|
+
const definition = entry;
|
|
2484
|
+
if (typeof definition["description"] !== "string") continue;
|
|
2485
|
+
const sentiment = definition["sentiment"];
|
|
2486
|
+
out[key] = {
|
|
2487
|
+
description: definition["description"],
|
|
2488
|
+
...sentiment === "POSITIVE" || sentiment === "NEGATIVE" ? { sentiment } : {}
|
|
2489
|
+
};
|
|
2490
|
+
}
|
|
2491
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
2314
2492
|
}
|
|
2315
2493
|
function mapAppGit(value) {
|
|
2316
2494
|
if (value === false) return false;
|
|
@@ -2338,7 +2516,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
2338
2516
|
// package.json
|
|
2339
2517
|
var package_default = {
|
|
2340
2518
|
name: "@raindrop-ai/opencode-plugin",
|
|
2341
|
-
version: "0.
|
|
2519
|
+
version: "0.3.1",
|
|
2342
2520
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
2343
2521
|
type: "module",
|
|
2344
2522
|
main: "dist/index.js",
|
|
@@ -2427,6 +2605,185 @@ var package_default = {
|
|
|
2427
2605
|
var PLUGIN_NAME = package_default.name;
|
|
2428
2606
|
var PLUGIN_VERSION = package_default.version;
|
|
2429
2607
|
|
|
2608
|
+
// src/self-diagnostics.ts
|
|
2609
|
+
var SELF_DIAGNOSTICS_TOOL_NAME_DEFAULT = "__raindrop_report";
|
|
2610
|
+
var SELF_DIAGNOSTICS_SIGNALS_DEFAULT = {
|
|
2611
|
+
missing_context: {
|
|
2612
|
+
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.",
|
|
2613
|
+
sentiment: "NEGATIVE"
|
|
2614
|
+
},
|
|
2615
|
+
repeatedly_broken_tool: {
|
|
2616
|
+
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.",
|
|
2617
|
+
sentiment: "NEGATIVE"
|
|
2618
|
+
},
|
|
2619
|
+
capability_gap: {
|
|
2620
|
+
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.",
|
|
2621
|
+
sentiment: "NEGATIVE"
|
|
2622
|
+
},
|
|
2623
|
+
complete_task_failure: {
|
|
2624
|
+
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.",
|
|
2625
|
+
sentiment: "NEGATIVE"
|
|
2626
|
+
}
|
|
2627
|
+
};
|
|
2628
|
+
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.";
|
|
2629
|
+
var SELF_DIAGNOSTICS_NOTEWORTHY_SIGNAL_KEY = "noteworthy";
|
|
2630
|
+
function normalizeString(value) {
|
|
2631
|
+
if (typeof value !== "string") return void 0;
|
|
2632
|
+
const trimmed = value.trim();
|
|
2633
|
+
return trimmed || void 0;
|
|
2634
|
+
}
|
|
2635
|
+
function normalizeSelfDiagnosticsSignals(signals) {
|
|
2636
|
+
if (!signals) return SELF_DIAGNOSTICS_SIGNALS_DEFAULT;
|
|
2637
|
+
const normalizedEntries = Object.entries(signals).map(([key, value]) => {
|
|
2638
|
+
var _a;
|
|
2639
|
+
const signalKey = key.trim();
|
|
2640
|
+
if (!signalKey || !value || typeof value !== "object") return void 0;
|
|
2641
|
+
const description = (_a = value.description) == null ? void 0 : _a.trim();
|
|
2642
|
+
if (!description) return void 0;
|
|
2643
|
+
const sentiment = value.sentiment;
|
|
2644
|
+
return [
|
|
2645
|
+
signalKey,
|
|
2646
|
+
{
|
|
2647
|
+
description,
|
|
2648
|
+
...sentiment === "POSITIVE" || sentiment === "NEGATIVE" ? { sentiment } : {}
|
|
2649
|
+
}
|
|
2650
|
+
];
|
|
2651
|
+
}).filter(
|
|
2652
|
+
(entry) => entry !== void 0
|
|
2653
|
+
);
|
|
2654
|
+
if (normalizedEntries.length === 0) return SELF_DIAGNOSTICS_SIGNALS_DEFAULT;
|
|
2655
|
+
return Object.fromEntries(normalizedEntries);
|
|
2656
|
+
}
|
|
2657
|
+
function normalizeSelfDiagnosticsConfig(options) {
|
|
2658
|
+
var _a, _b;
|
|
2659
|
+
if (!(options == null ? void 0 : options.enabled)) return void 0;
|
|
2660
|
+
const signalDefinitions = normalizeSelfDiagnosticsSignals(options.signals);
|
|
2661
|
+
const signalKeys = Object.keys(signalDefinitions);
|
|
2662
|
+
const signalDescriptions = {};
|
|
2663
|
+
const signalSentiments = {};
|
|
2664
|
+
for (const signalKey of signalKeys) {
|
|
2665
|
+
const definition = signalDefinitions[signalKey];
|
|
2666
|
+
if (!definition) continue;
|
|
2667
|
+
signalDescriptions[signalKey] = definition.description;
|
|
2668
|
+
signalSentiments[signalKey] = definition.sentiment;
|
|
2669
|
+
}
|
|
2670
|
+
const customGuidance = ((_a = options.guidance) == null ? void 0 : _a.trim()) || "";
|
|
2671
|
+
const toolName = ((_b = options.toolName) == null ? void 0 : _b.trim()) || SELF_DIAGNOSTICS_TOOL_NAME_DEFAULT;
|
|
2672
|
+
const signalList = signalKeys.map((signalKey) => {
|
|
2673
|
+
const sentiment = signalSentiments[signalKey];
|
|
2674
|
+
const sentimentTag = sentiment ? ` [${sentiment.toLowerCase()}]` : "";
|
|
2675
|
+
return `- ${signalKey}: ${signalDescriptions[signalKey]}${sentimentTag}`;
|
|
2676
|
+
}).join("\n");
|
|
2677
|
+
const guidanceBlock = customGuidance ? `
|
|
2678
|
+
Additional guidance: ${customGuidance}
|
|
2679
|
+
` : "";
|
|
2680
|
+
const toolDescription = `${SELF_DIAGNOSTICS_TOOL_PREAMBLE}
|
|
2681
|
+
|
|
2682
|
+
When to call:
|
|
2683
|
+
- You are blocked from completing the task due to missing information or access that the user cannot provide.
|
|
2684
|
+
- A tool is persistently failing across multiple attempts, not just a single transient error.
|
|
2685
|
+
- The task requires a tool, permission, or capability you do not have.
|
|
2686
|
+
- You genuinely cannot deliver what the user asked for despite trying.
|
|
2687
|
+
|
|
2688
|
+
When NOT to call:
|
|
2689
|
+
- Normal clarifying questions or back-and-forth with the user.
|
|
2690
|
+
- A single tool error that you can recover from or retry.
|
|
2691
|
+
- You successfully completed the task, even if it was difficult.
|
|
2692
|
+
- Policy refusals or content filtering \u2014 those are working as intended.
|
|
2693
|
+
|
|
2694
|
+
Rules:
|
|
2695
|
+
1. Pick the single best category.
|
|
2696
|
+
2. Do not fabricate issues. Only report what is evident from the conversation.
|
|
2697
|
+
3. Err on the side of NOT calling this tool. When in doubt, help the user instead.
|
|
2698
|
+
${guidanceBlock}
|
|
2699
|
+
Categories:
|
|
2700
|
+
` + signalList;
|
|
2701
|
+
return {
|
|
2702
|
+
toolName,
|
|
2703
|
+
toolDescription,
|
|
2704
|
+
signalKeys,
|
|
2705
|
+
signalKeySet: new Set(signalKeys),
|
|
2706
|
+
signalDescriptions,
|
|
2707
|
+
signalSentiments
|
|
2708
|
+
};
|
|
2709
|
+
}
|
|
2710
|
+
var SELF_DIAGNOSTICS_ACK = '{"acknowledged":true}';
|
|
2711
|
+
function selfDiagnosticsInputJsonSchema(config) {
|
|
2712
|
+
return {
|
|
2713
|
+
type: "object",
|
|
2714
|
+
properties: {
|
|
2715
|
+
category: {
|
|
2716
|
+
type: "string",
|
|
2717
|
+
enum: config.signalKeys,
|
|
2718
|
+
description: "The single best-matching category from the list above."
|
|
2719
|
+
},
|
|
2720
|
+
detail: {
|
|
2721
|
+
type: "string",
|
|
2722
|
+
description: "One sentence of factual context: what happened and why it matters. Do not include PII or secrets."
|
|
2723
|
+
}
|
|
2724
|
+
},
|
|
2725
|
+
required: ["category", "detail"],
|
|
2726
|
+
additionalProperties: false
|
|
2727
|
+
};
|
|
2728
|
+
}
|
|
2729
|
+
function isRecord3(value) {
|
|
2730
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2731
|
+
}
|
|
2732
|
+
async function trackSelfDiagnosticsSignal(toolInput, config, eventShipper, eventId, debug) {
|
|
2733
|
+
var _a, _b;
|
|
2734
|
+
try {
|
|
2735
|
+
const input = isRecord3(toolInput) ? toolInput : void 0;
|
|
2736
|
+
const fallbackCategory = (_a = config.signalKeys[0]) != null ? _a : "unknown";
|
|
2737
|
+
const categoryCandidate = normalizeString(input == null ? void 0 : input["category"]);
|
|
2738
|
+
const category = categoryCandidate && config.signalKeySet.has(categoryCandidate) ? categoryCandidate : fallbackCategory;
|
|
2739
|
+
const detail = (_b = normalizeString(input == null ? void 0 : input["detail"])) != null ? _b : "";
|
|
2740
|
+
if (!eventId) {
|
|
2741
|
+
if (debug) {
|
|
2742
|
+
console.warn(
|
|
2743
|
+
"[raindrop-ai/opencode-plugin] self diagnostics signal skipped: no open event for this session."
|
|
2744
|
+
);
|
|
2745
|
+
}
|
|
2746
|
+
return;
|
|
2747
|
+
}
|
|
2748
|
+
if (category === SELF_DIAGNOSTICS_NOTEWORTHY_SIGNAL_KEY) {
|
|
2749
|
+
await eventShipper.trackSignal({
|
|
2750
|
+
eventId,
|
|
2751
|
+
name: "self diagnostics - noteworthy",
|
|
2752
|
+
type: "agent_internal",
|
|
2753
|
+
properties: {
|
|
2754
|
+
source: "agent_flag_event_tool",
|
|
2755
|
+
reason: detail,
|
|
2756
|
+
severity: "medium",
|
|
2757
|
+
sdk: "opencode-plugin",
|
|
2758
|
+
sdk_version: PLUGIN_VERSION
|
|
2759
|
+
}
|
|
2760
|
+
});
|
|
2761
|
+
return;
|
|
2762
|
+
}
|
|
2763
|
+
await eventShipper.trackSignal({
|
|
2764
|
+
eventId,
|
|
2765
|
+
name: `self diagnostics - ${category}`,
|
|
2766
|
+
type: "agent",
|
|
2767
|
+
sentiment: config.signalSentiments[category],
|
|
2768
|
+
properties: {
|
|
2769
|
+
source: "agent_reporting_tool",
|
|
2770
|
+
category,
|
|
2771
|
+
signal_description: config.signalDescriptions[category],
|
|
2772
|
+
sdk: "opencode-plugin",
|
|
2773
|
+
sdk_version: PLUGIN_VERSION,
|
|
2774
|
+
...detail ? { detail } : {}
|
|
2775
|
+
}
|
|
2776
|
+
});
|
|
2777
|
+
} catch (err) {
|
|
2778
|
+
if (debug) {
|
|
2779
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2780
|
+
console.warn(
|
|
2781
|
+
`[raindrop-ai/opencode-plugin] selfDiagnostics signal dispatch failed: ${message}`
|
|
2782
|
+
);
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2430
2787
|
// src/shipper.ts
|
|
2431
2788
|
var EventShipper3 = class extends EventShipper2 {
|
|
2432
2789
|
constructor(opts) {
|
|
@@ -2462,6 +2819,100 @@ var TraceShipper3 = class extends TraceShipper2 {
|
|
|
2462
2819
|
}
|
|
2463
2820
|
};
|
|
2464
2821
|
|
|
2822
|
+
// src/tool-catalog.ts
|
|
2823
|
+
var PROMPT_TOOLS_SOURCE_ATTRIBUTE = "ai.prompt.tools.source";
|
|
2824
|
+
var OPENCODE_TOOL_LIST_SOURCE = "opencode.tool.list";
|
|
2825
|
+
var TOOL_CATALOG_TIMEOUT_MS = 2e3;
|
|
2826
|
+
function isRecord4(value) {
|
|
2827
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2828
|
+
}
|
|
2829
|
+
function catalogItems(raw) {
|
|
2830
|
+
var _a;
|
|
2831
|
+
const list = Array.isArray(raw) ? raw : isRecord4(raw) && Array.isArray(raw.data) ? raw.data : void 0;
|
|
2832
|
+
if (!list) return void 0;
|
|
2833
|
+
const items = [];
|
|
2834
|
+
for (const item of list) {
|
|
2835
|
+
if (!isRecord4(item)) continue;
|
|
2836
|
+
const name = typeof item.id === "string" ? item.id : typeof item.name === "string" ? item.name : void 0;
|
|
2837
|
+
if (!name) continue;
|
|
2838
|
+
const schema = (_a = item.parameters) != null ? _a : item.input;
|
|
2839
|
+
items.push({
|
|
2840
|
+
type: "function",
|
|
2841
|
+
name,
|
|
2842
|
+
...typeof item.description === "string" ? { description: item.description } : {},
|
|
2843
|
+
...isJsonSchema(schema) ? { inputSchema: schema } : {}
|
|
2844
|
+
});
|
|
2845
|
+
}
|
|
2846
|
+
return items;
|
|
2847
|
+
}
|
|
2848
|
+
function withTimeout(promise, ms) {
|
|
2849
|
+
return new Promise((resolve2) => {
|
|
2850
|
+
const timer = setTimeout(() => resolve2(void 0), ms);
|
|
2851
|
+
if (typeof timer === "object" && "unref" in timer) timer.unref();
|
|
2852
|
+
promise.then(
|
|
2853
|
+
(value) => {
|
|
2854
|
+
clearTimeout(timer);
|
|
2855
|
+
resolve2(value);
|
|
2856
|
+
},
|
|
2857
|
+
() => {
|
|
2858
|
+
clearTimeout(timer);
|
|
2859
|
+
resolve2(void 0);
|
|
2860
|
+
}
|
|
2861
|
+
);
|
|
2862
|
+
});
|
|
2863
|
+
}
|
|
2864
|
+
function createToolCatalog(options = {}) {
|
|
2865
|
+
var _a, _b;
|
|
2866
|
+
const override = options.override !== void 0 ? { tools: (_a = normalizeToolDeclarations(options.override)) != null ? _a : [] } : void 0;
|
|
2867
|
+
const timeoutMs = (_b = options.timeoutMs) != null ? _b : TOOL_CATALOG_TIMEOUT_MS;
|
|
2868
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2869
|
+
async function lookup(query) {
|
|
2870
|
+
const fetch2 = options.fetch;
|
|
2871
|
+
if (!fetch2) return void 0;
|
|
2872
|
+
let raw;
|
|
2873
|
+
try {
|
|
2874
|
+
raw = await withTimeout(Promise.resolve().then(() => fetch2(query)), timeoutMs);
|
|
2875
|
+
} catch (error) {
|
|
2876
|
+
if (options.debug) console.log(`[raindrop-ai/opencode-plugin] tool catalog lookup failed: ${String(error)}`);
|
|
2877
|
+
return void 0;
|
|
2878
|
+
}
|
|
2879
|
+
const tools = normalizeToolDeclarations(catalogItems(raw));
|
|
2880
|
+
if (!tools) {
|
|
2881
|
+
if (options.debug) console.log("[raindrop-ai/opencode-plugin] tool catalog lookup returned no tool list");
|
|
2882
|
+
return void 0;
|
|
2883
|
+
}
|
|
2884
|
+
return { tools, source: OPENCODE_TOOL_LIST_SOURCE };
|
|
2885
|
+
}
|
|
2886
|
+
return {
|
|
2887
|
+
resolve(sessionID, query) {
|
|
2888
|
+
if (override) return Promise.resolve(override);
|
|
2889
|
+
if (!options.fetch) return Promise.resolve(void 0);
|
|
2890
|
+
let perSession = cache.get(sessionID);
|
|
2891
|
+
if (!perSession) {
|
|
2892
|
+
perSession = /* @__PURE__ */ new Map();
|
|
2893
|
+
cache.set(sessionID, perSession);
|
|
2894
|
+
}
|
|
2895
|
+
const key = `${query.provider}\0${query.model}`;
|
|
2896
|
+
let pending = perSession.get(key);
|
|
2897
|
+
if (!pending) {
|
|
2898
|
+
pending = lookup(query).catch(() => void 0);
|
|
2899
|
+
perSession.set(key, pending);
|
|
2900
|
+
}
|
|
2901
|
+
return pending;
|
|
2902
|
+
},
|
|
2903
|
+
forget(sessionID) {
|
|
2904
|
+
cache.delete(sessionID);
|
|
2905
|
+
}
|
|
2906
|
+
};
|
|
2907
|
+
}
|
|
2908
|
+
function promptToolsAttributes(resolved) {
|
|
2909
|
+
if (!resolved) return [];
|
|
2910
|
+
return [
|
|
2911
|
+
promptToolsAttribute(resolved.tools),
|
|
2912
|
+
resolved.source !== void 0 ? attrString(PROMPT_TOOLS_SOURCE_ATTRIBUTE, resolved.source) : void 0
|
|
2913
|
+
];
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2465
2916
|
// src/tracing-v2.ts
|
|
2466
2917
|
var import_node_os2 = require("os");
|
|
2467
2918
|
|
|
@@ -2635,7 +3086,7 @@ function errorString(error) {
|
|
|
2635
3086
|
|
|
2636
3087
|
type: ${error.type}`;
|
|
2637
3088
|
}
|
|
2638
|
-
function createV2Tracing(config, eventShipper, traceShipper, sessionLookup) {
|
|
3089
|
+
function createV2Tracing(config, eventShipper, traceShipper, sessionLookup, toolCatalog) {
|
|
2639
3090
|
const sessions = /* @__PURE__ */ new Map();
|
|
2640
3091
|
const admittedPrompts = /* @__PURE__ */ new Map();
|
|
2641
3092
|
const subagentLinks = /* @__PURE__ */ new Map();
|
|
@@ -2675,7 +3126,7 @@ function createV2Tracing(config, eventShipper, traceShipper, sessionLookup) {
|
|
|
2675
3126
|
async function finalizeTurn(state2, error) {
|
|
2676
3127
|
var _a, _b, _c;
|
|
2677
3128
|
for (const step of state2.stepSpans.values()) {
|
|
2678
|
-
traceShipper.endSpan(step.span);
|
|
3129
|
+
traceShipper.endSpan(step.span, { attributes: promptToolsAttributes(await step.promptTools) });
|
|
2679
3130
|
}
|
|
2680
3131
|
state2.stepSpans.clear();
|
|
2681
3132
|
if (state2.compactionSpan) {
|
|
@@ -2930,7 +3381,13 @@ function createV2Tracing(config, eventShipper, traceShipper, sessionLookup) {
|
|
|
2930
3381
|
state2.stepSpans.set(data.assistantMessageID, {
|
|
2931
3382
|
span,
|
|
2932
3383
|
providerID: data.model.providerID,
|
|
2933
|
-
modelID: data.model.id
|
|
3384
|
+
modelID: data.model.id,
|
|
3385
|
+
// Kicked off here, awaited when the step closes: the lookup (bounded
|
|
3386
|
+
// by its timeout) never delays the events streamed during the step.
|
|
3387
|
+
promptTools: toolCatalog ? toolCatalog.resolve(data.sessionID, {
|
|
3388
|
+
provider: data.model.providerID,
|
|
3389
|
+
model: data.model.id
|
|
3390
|
+
}) : Promise.resolve(void 0)
|
|
2934
3391
|
});
|
|
2935
3392
|
break;
|
|
2936
3393
|
}
|
|
@@ -2961,6 +3418,7 @@ function createV2Tracing(config, eventShipper, traceShipper, sessionLookup) {
|
|
|
2961
3418
|
if (reasoning !== void 0) {
|
|
2962
3419
|
attrs.push(attrString("ai.reasoning.text", reasoning));
|
|
2963
3420
|
}
|
|
3421
|
+
attrs.push(...promptToolsAttributes(await step.promptTools));
|
|
2964
3422
|
traceShipper.endSpan(step.span, { attributes: attrs });
|
|
2965
3423
|
break;
|
|
2966
3424
|
}
|
|
@@ -2970,7 +3428,10 @@ function createV2Tracing(config, eventShipper, traceShipper, sessionLookup) {
|
|
|
2970
3428
|
const step = state2 == null ? void 0 : state2.stepSpans.get(data.assistantMessageID);
|
|
2971
3429
|
if (!state2 || !step) return;
|
|
2972
3430
|
state2.stepSpans.delete(data.assistantMessageID);
|
|
2973
|
-
traceShipper.endSpan(step.span, {
|
|
3431
|
+
traceShipper.endSpan(step.span, {
|
|
3432
|
+
error: errorString(data.error),
|
|
3433
|
+
attributes: promptToolsAttributes(await step.promptTools)
|
|
3434
|
+
});
|
|
2974
3435
|
break;
|
|
2975
3436
|
}
|
|
2976
3437
|
case "session.text.ended": {
|
|
@@ -3146,6 +3607,7 @@ function createV2Tracing(config, eventShipper, traceShipper, sessionLookup) {
|
|
|
3146
3607
|
}
|
|
3147
3608
|
}
|
|
3148
3609
|
sessions.delete(event.data.sessionID);
|
|
3610
|
+
toolCatalog == null ? void 0 : toolCatalog.forget(event.data.sessionID);
|
|
3149
3611
|
break;
|
|
3150
3612
|
}
|
|
3151
3613
|
}
|
|
@@ -3223,7 +3685,7 @@ function raindrop(opts = {}) {
|
|
|
3223
3685
|
}
|
|
3224
3686
|
}
|
|
3225
3687
|
function createShared(ctx) {
|
|
3226
|
-
var _a, _b, _c;
|
|
3688
|
+
var _a, _b, _c, _d, _e;
|
|
3227
3689
|
const config = loadConfig((_a = opts.directory) != null ? _a : ctx.location.directory);
|
|
3228
3690
|
const resolvedLocalUrl = resolveLocalDebuggerBaseUrl(config.localWorkshopUrl);
|
|
3229
3691
|
const hasLocalDestination = resolvedLocalUrl !== null;
|
|
@@ -3256,18 +3718,41 @@ function raindrop(opts = {}) {
|
|
|
3256
3718
|
};
|
|
3257
3719
|
const eventShipper = new EventShipper3(shipperOptions);
|
|
3258
3720
|
const traceShipper = new TraceShipper3(shipperOptions);
|
|
3721
|
+
const selfDiagnostics = normalizeSelfDiagnosticsConfig(
|
|
3722
|
+
(_d = opts.selfDiagnostics) != null ? _d : config.selfDiagnostics
|
|
3723
|
+
);
|
|
3724
|
+
if (selfDiagnostics && !config.writeKey.trim()) {
|
|
3725
|
+
appLog(
|
|
3726
|
+
"warn",
|
|
3727
|
+
`selfDiagnostics enabled but RAINDROP_WRITE_KEY is not set \u2014 the ${selfDiagnostics.toolName} tool is not registered (signals require the cloud destination).`
|
|
3728
|
+
);
|
|
3729
|
+
}
|
|
3259
3730
|
const s = {
|
|
3260
3731
|
eventShipper,
|
|
3261
3732
|
traceShipper,
|
|
3733
|
+
config,
|
|
3734
|
+
selfDiagnostics: config.writeKey.trim() ? selfDiagnostics : void 0,
|
|
3262
3735
|
appGitContext,
|
|
3263
3736
|
abort: new AbortController(),
|
|
3264
3737
|
instances: /* @__PURE__ */ new Set(),
|
|
3265
|
-
runtime: createV2Tracing(
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3738
|
+
runtime: createV2Tracing(
|
|
3739
|
+
config,
|
|
3740
|
+
eventShipper,
|
|
3741
|
+
traceShipper,
|
|
3742
|
+
async (sessionID) => {
|
|
3743
|
+
const instance = firstInstance(s);
|
|
3744
|
+
if (!instance) return void 0;
|
|
3745
|
+
const info = await instance.ctx.session.get({ sessionID });
|
|
3746
|
+
return { parentID: info.parentID };
|
|
3747
|
+
},
|
|
3748
|
+
createToolCatalog({
|
|
3749
|
+
override: (_e = opts.tools) != null ? _e : config.tools,
|
|
3750
|
+
// v2 exposes no per-model catalog endpoint; the registered tool list
|
|
3751
|
+
// (after every plugin transform) is the closest observable catalog.
|
|
3752
|
+
fetch: () => ctx.tool.list(),
|
|
3753
|
+
debug: config.debug
|
|
3754
|
+
})
|
|
3755
|
+
)
|
|
3271
3756
|
};
|
|
3272
3757
|
return s;
|
|
3273
3758
|
}
|
|
@@ -3310,6 +3795,34 @@ function raindrop(opts = {}) {
|
|
|
3310
3795
|
const directory = ctx.location.directory;
|
|
3311
3796
|
instance.disposables.push(await ctx.session.hook("prompt", (e) => runtime.onPrompt(e, directory)));
|
|
3312
3797
|
instance.disposables.push(await ctx.session.hook("context", (e) => runtime.onContext(e)));
|
|
3798
|
+
const selfDiagnostics = s.selfDiagnostics;
|
|
3799
|
+
if (selfDiagnostics) {
|
|
3800
|
+
const cfg = selfDiagnostics;
|
|
3801
|
+
const eventShipper = s.eventShipper;
|
|
3802
|
+
const debug = s.config.debug;
|
|
3803
|
+
instance.disposables.push(
|
|
3804
|
+
await ctx.tool.transform(
|
|
3805
|
+
(editor) => editor.add({
|
|
3806
|
+
name: cfg.toolName,
|
|
3807
|
+
description: cfg.toolDescription,
|
|
3808
|
+
input: selfDiagnosticsInputJsonSchema(cfg),
|
|
3809
|
+
// Direct tool — the host routes everything else through the
|
|
3810
|
+
// codemode `execute` wrapper, where this name isn't callable.
|
|
3811
|
+
options: { codemode: false },
|
|
3812
|
+
async execute(input, context) {
|
|
3813
|
+
await trackSelfDiagnosticsSignal(
|
|
3814
|
+
input,
|
|
3815
|
+
cfg,
|
|
3816
|
+
eventShipper,
|
|
3817
|
+
runtime.currentEventId(context.sessionID),
|
|
3818
|
+
debug
|
|
3819
|
+
);
|
|
3820
|
+
return { content: SELF_DIAGNOSTICS_ACK };
|
|
3821
|
+
}
|
|
3822
|
+
})
|
|
3823
|
+
)
|
|
3824
|
+
);
|
|
3825
|
+
}
|
|
3313
3826
|
if (isFirst) void runEventLoop(s);
|
|
3314
3827
|
const owner = s;
|
|
3315
3828
|
return async () => {
|
package/dist/v2.d.cts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
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, P as PromptToolsOverride } from './self-diagnostics-CLW6h4zB.cjs';
|
|
3
|
+
export { a as SelfDiagnosticsSignalDefinition, b as SelfDiagnosticsSignalDefinitions } from './self-diagnostics-CLW6h4zB.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;
|
|
12
|
+
/**
|
|
13
|
+
* Overrides the tool catalog recorded as `ai.prompt.tools` on every model
|
|
14
|
+
* span; skips the OpenCode `ctx.tool.list()` lookup entirely.
|
|
15
|
+
*/
|
|
16
|
+
tools?: PromptToolsOverride;
|
|
9
17
|
}
|
|
10
18
|
/**
|
|
11
19
|
* The Raindrop control handle exposed on the v2 plugin. Unlike v1 (where the
|
|
@@ -38,4 +46,4 @@ interface RaindropOpencodePluginV2 extends Plugin.Plugin {
|
|
|
38
46
|
*/
|
|
39
47
|
declare function raindrop(opts?: RaindropOpencodeV2Options): RaindropOpencodePluginV2;
|
|
40
48
|
|
|
41
|
-
export { IdentifyInput, type RaindropOpencodePluginV2, type RaindropOpencodeV2Handle, type RaindropOpencodeV2Options, SignalInput, raindrop as default };
|
|
49
|
+
export { IdentifyInput, type RaindropOpencodePluginV2, type RaindropOpencodeV2Handle, type RaindropOpencodeV2Options, SelfDiagnosticsOptions, SignalInput, raindrop as default };
|