@raindrop-ai/opencode-plugin 0.1.4 → 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 +62 -2
- package/dist/chunk-FCORH4YS.js +2788 -0
- package/dist/index.cjs +287 -25
- package/dist/index.d.cts +7 -78
- package/dist/index.d.ts +7 -78
- package/dist/index.js +229 -2697
- package/dist/self-diagnostics-NiAq-SZ8.d.cts +122 -0
- package/dist/self-diagnostics-NiAq-SZ8.d.ts +122 -0
- package/dist/v2.cjs +3577 -0
- package/dist/v2.d.cts +44 -0
- package/dist/v2.d.ts +44 -0
- package/dist/v2.js +804 -0
- package/package.json +22 -3
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,7 @@ __export(index_exports, {
|
|
|
23
23
|
default: () => plugin
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
var import_plugin = require("@opencode-ai/plugin");
|
|
26
27
|
|
|
27
28
|
// ../core/dist/chunk-BYBMRLZM.js
|
|
28
29
|
var EVAL_CORRELATION_ID_ATTRIBUTE = "raindrop.eval_correlation_id";
|
|
@@ -2308,9 +2309,41 @@ function loadConfig(projectDirectory) {
|
|
|
2308
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,
|
|
2309
2310
|
eventMetadata,
|
|
2310
2311
|
localWorkshopUrl: resolveLocalWorkshopUrl(merged.local_workshop_url),
|
|
2311
|
-
appGit: mapAppGit(merged.app_git)
|
|
2312
|
+
appGit: mapAppGit(merged.app_git),
|
|
2313
|
+
selfDiagnostics: mapSelfDiagnostics(merged.self_diagnostics)
|
|
2312
2314
|
};
|
|
2313
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
|
+
}
|
|
2314
2347
|
function mapAppGit(value) {
|
|
2315
2348
|
if (value === false) return false;
|
|
2316
2349
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
@@ -2337,7 +2370,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
2337
2370
|
// package.json
|
|
2338
2371
|
var package_default = {
|
|
2339
2372
|
name: "@raindrop-ai/opencode-plugin",
|
|
2340
|
-
version: "0.
|
|
2373
|
+
version: "0.3.0",
|
|
2341
2374
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
2342
2375
|
type: "module",
|
|
2343
2376
|
main: "dist/index.js",
|
|
@@ -2353,6 +2386,11 @@ var package_default = {
|
|
|
2353
2386
|
types: "./dist/index.d.ts",
|
|
2354
2387
|
import: "./dist/index.js",
|
|
2355
2388
|
require: "./dist/index.cjs"
|
|
2389
|
+
},
|
|
2390
|
+
"./v2": {
|
|
2391
|
+
types: "./dist/v2.d.ts",
|
|
2392
|
+
import: "./dist/v2.js",
|
|
2393
|
+
require: "./dist/v2.cjs"
|
|
2356
2394
|
}
|
|
2357
2395
|
},
|
|
2358
2396
|
sideEffects: false,
|
|
@@ -2367,24 +2405,38 @@ var package_default = {
|
|
|
2367
2405
|
},
|
|
2368
2406
|
peerDependencies: {
|
|
2369
2407
|
"@opencode-ai/plugin": ">=1.3.0",
|
|
2370
|
-
"@opencode-ai/sdk": ">=1.3.0"
|
|
2408
|
+
"@opencode-ai/sdk": ">=1.3.0",
|
|
2409
|
+
"@opencode/plugin": ">=2.0.0",
|
|
2410
|
+
"@opencode/sdk": ">=2.0.0"
|
|
2371
2411
|
},
|
|
2372
2412
|
peerDependenciesMeta: {
|
|
2413
|
+
"@opencode-ai/plugin": {
|
|
2414
|
+
optional: true
|
|
2415
|
+
},
|
|
2373
2416
|
"@opencode-ai/sdk": {
|
|
2374
2417
|
optional: true
|
|
2418
|
+
},
|
|
2419
|
+
"@opencode/plugin": {
|
|
2420
|
+
optional: true
|
|
2421
|
+
},
|
|
2422
|
+
"@opencode/sdk": {
|
|
2423
|
+
optional: true
|
|
2375
2424
|
}
|
|
2376
2425
|
},
|
|
2377
2426
|
devDependencies: {
|
|
2378
2427
|
"@raindrop-ai/core": "workspace:*",
|
|
2379
2428
|
"@opencode-ai/plugin": "^1.3.3",
|
|
2380
2429
|
"@opencode-ai/sdk": "^1.3.3",
|
|
2430
|
+
"@opencode/plugin": "^2.0.10",
|
|
2431
|
+
"@opencode/sdk": "^2.0.10",
|
|
2381
2432
|
"@types/node": "^20.11.17",
|
|
2382
2433
|
tsup: "^8.4.0",
|
|
2383
2434
|
typescript: "^5.3.3"
|
|
2384
2435
|
},
|
|
2385
2436
|
tsup: {
|
|
2386
2437
|
entry: [
|
|
2387
|
-
"src/index.ts"
|
|
2438
|
+
"src/index.ts",
|
|
2439
|
+
"src/v2.ts"
|
|
2388
2440
|
],
|
|
2389
2441
|
format: [
|
|
2390
2442
|
"cjs",
|
|
@@ -2407,6 +2459,167 @@ var package_default = {
|
|
|
2407
2459
|
var PLUGIN_NAME = package_default.name;
|
|
2408
2460
|
var PLUGIN_VERSION = package_default.version;
|
|
2409
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 isRecord2(value) {
|
|
2566
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2567
|
+
}
|
|
2568
|
+
async function trackSelfDiagnosticsSignal(toolInput, config, eventShipper, eventId, debug) {
|
|
2569
|
+
var _a, _b;
|
|
2570
|
+
try {
|
|
2571
|
+
const input = isRecord2(toolInput) ? toolInput : void 0;
|
|
2572
|
+
const fallbackCategory = (_a = config.signalKeys[0]) != null ? _a : "unknown";
|
|
2573
|
+
const categoryCandidate = normalizeString(input == null ? void 0 : input["category"]);
|
|
2574
|
+
const category = categoryCandidate && config.signalKeySet.has(categoryCandidate) ? categoryCandidate : fallbackCategory;
|
|
2575
|
+
const detail = (_b = normalizeString(input == null ? void 0 : input["detail"])) != null ? _b : "";
|
|
2576
|
+
if (!eventId) {
|
|
2577
|
+
if (debug) {
|
|
2578
|
+
console.warn(
|
|
2579
|
+
"[raindrop-ai/opencode-plugin] self diagnostics signal skipped: no open event for this session."
|
|
2580
|
+
);
|
|
2581
|
+
}
|
|
2582
|
+
return;
|
|
2583
|
+
}
|
|
2584
|
+
if (category === SELF_DIAGNOSTICS_NOTEWORTHY_SIGNAL_KEY) {
|
|
2585
|
+
await eventShipper.trackSignal({
|
|
2586
|
+
eventId,
|
|
2587
|
+
name: "self diagnostics - noteworthy",
|
|
2588
|
+
type: "agent_internal",
|
|
2589
|
+
properties: {
|
|
2590
|
+
source: "agent_flag_event_tool",
|
|
2591
|
+
reason: detail,
|
|
2592
|
+
severity: "medium",
|
|
2593
|
+
sdk: "opencode-plugin",
|
|
2594
|
+
sdk_version: PLUGIN_VERSION
|
|
2595
|
+
}
|
|
2596
|
+
});
|
|
2597
|
+
return;
|
|
2598
|
+
}
|
|
2599
|
+
await eventShipper.trackSignal({
|
|
2600
|
+
eventId,
|
|
2601
|
+
name: `self diagnostics - ${category}`,
|
|
2602
|
+
type: "agent",
|
|
2603
|
+
sentiment: config.signalSentiments[category],
|
|
2604
|
+
properties: {
|
|
2605
|
+
source: "agent_reporting_tool",
|
|
2606
|
+
category,
|
|
2607
|
+
signal_description: config.signalDescriptions[category],
|
|
2608
|
+
sdk: "opencode-plugin",
|
|
2609
|
+
sdk_version: PLUGIN_VERSION,
|
|
2610
|
+
...detail ? { detail } : {}
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
} catch (err) {
|
|
2614
|
+
if (debug) {
|
|
2615
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2616
|
+
console.warn(
|
|
2617
|
+
`[raindrop-ai/opencode-plugin] selfDiagnostics signal dispatch failed: ${message}`
|
|
2618
|
+
);
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2410
2623
|
// src/shipper.ts
|
|
2411
2624
|
var EventShipper3 = class extends EventShipper2 {
|
|
2412
2625
|
constructor(opts) {
|
|
@@ -2678,7 +2891,7 @@ function markTaskCallFinished(sessionID, callID) {
|
|
|
2678
2891
|
running.delete(callID);
|
|
2679
2892
|
if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
|
|
2680
2893
|
}
|
|
2681
|
-
function endPendingToolSpanWithError(state2, callID,
|
|
2894
|
+
function endPendingToolSpanWithError(state2, callID, tool2, toolCallArgs, errorMessage, traceShipper) {
|
|
2682
2895
|
const startInfo = state2.toolSpanStarts.get(callID);
|
|
2683
2896
|
if (!startInfo) return false;
|
|
2684
2897
|
state2.toolSpanStarts.delete(callID);
|
|
@@ -2700,7 +2913,7 @@ function endPendingToolSpanWithError(state2, callID, tool, toolCallArgs, errorMe
|
|
|
2700
2913
|
startTimeUnixNano: startInfo.startTimeUnixNano,
|
|
2701
2914
|
attributes: [
|
|
2702
2915
|
attrString("ai.operationId", "ai.toolCall"),
|
|
2703
|
-
attrString("ai.toolCall.name",
|
|
2916
|
+
attrString("ai.toolCall.name", tool2),
|
|
2704
2917
|
attrString("ai.toolCall.id", callID)
|
|
2705
2918
|
]
|
|
2706
2919
|
});
|
|
@@ -2737,7 +2950,7 @@ function getHostname() {
|
|
|
2737
2950
|
return h;
|
|
2738
2951
|
}
|
|
2739
2952
|
}
|
|
2740
|
-
function
|
|
2953
|
+
function createTracing(config, worktree, directory, eventShipper, traceShipper) {
|
|
2741
2954
|
function log(msg, data) {
|
|
2742
2955
|
if (!config.debug) return;
|
|
2743
2956
|
const prefix = `[raindrop-ai/opencode-plugin] [info] ${msg}`;
|
|
@@ -2763,7 +2976,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
2763
2976
|
callKey,
|
|
2764
2977
|
log
|
|
2765
2978
|
});
|
|
2766
|
-
|
|
2979
|
+
const hooks = {
|
|
2767
2980
|
// ------------------------------------------------------------------
|
|
2768
2981
|
// event — session lifecycle + streaming parts
|
|
2769
2982
|
// ------------------------------------------------------------------
|
|
@@ -2808,14 +3021,14 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
2808
3021
|
if (partObj["type"] === "text" && typeof partObj["text"] === "string") {
|
|
2809
3022
|
state2.outputParts.set(messageId, capText2(partObj["text"]));
|
|
2810
3023
|
} else if (partObj["type"] === "tool" && messageId) {
|
|
2811
|
-
const
|
|
3024
|
+
const tool2 = partObj["tool"];
|
|
2812
3025
|
const callID = partObj["callID"];
|
|
2813
3026
|
const partState = partObj["state"];
|
|
2814
3027
|
if (callID == null || callID === "")
|
|
2815
3028
|
throw new Error("message.part.updated: part.callID is required for tool part");
|
|
2816
3029
|
if (partState == null || typeof partState !== "object")
|
|
2817
3030
|
throw new Error("message.part.updated: part.state is required for tool part");
|
|
2818
|
-
if (
|
|
3031
|
+
if (tool2 === "task") {
|
|
2819
3032
|
const metadata = partState["metadata"];
|
|
2820
3033
|
const childSessionId = metadata != null && typeof metadata === "object" ? metadata["sessionId"] : void 0;
|
|
2821
3034
|
if (childSessionId != null && childSessionId !== "") {
|
|
@@ -2840,11 +3053,11 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
2840
3053
|
if (endPendingToolSpanWithError(
|
|
2841
3054
|
state2,
|
|
2842
3055
|
callID,
|
|
2843
|
-
|
|
3056
|
+
tool2,
|
|
2844
3057
|
toolCallArgs,
|
|
2845
3058
|
errorMessage,
|
|
2846
3059
|
traceShipper
|
|
2847
|
-
) &&
|
|
3060
|
+
) && tool2 === "task") {
|
|
2848
3061
|
markTaskCallFinished(partSessionID, callID);
|
|
2849
3062
|
}
|
|
2850
3063
|
}
|
|
@@ -3221,20 +3434,20 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
3221
3434
|
"tool.execute.before": async (toolInput, _output) => {
|
|
3222
3435
|
var _a, _b, _c;
|
|
3223
3436
|
try {
|
|
3224
|
-
const { tool, sessionID, callID } = toolInput;
|
|
3437
|
+
const { tool: tool2, sessionID, callID } = toolInput;
|
|
3225
3438
|
const state2 = sessions.get(sessionID);
|
|
3226
3439
|
if (!state2 || !state2.currentEventId) return;
|
|
3227
3440
|
const spanParent = state2.parentId && state2.parentTaskSpanIds ? state2.parentTaskSpanIds : (_a = state2.currentRootSpan) == null ? void 0 : _a.ids;
|
|
3228
3441
|
if (!spanParent) return;
|
|
3229
3442
|
const startTimeUnixNano = nowUnixNanoString();
|
|
3230
|
-
if (
|
|
3443
|
+
if (tool2 === "task") {
|
|
3231
3444
|
const liveTaskSpan = traceShipper.startSpan({
|
|
3232
3445
|
name: "ai.toolCall",
|
|
3233
3446
|
parent: spanParent,
|
|
3234
3447
|
eventId: state2.currentEventId,
|
|
3235
3448
|
attributes: [
|
|
3236
3449
|
attrString("ai.operationId", "ai.toolCall"),
|
|
3237
|
-
attrString("ai.toolCall.name",
|
|
3450
|
+
attrString("ai.toolCall.name", tool2),
|
|
3238
3451
|
attrString("ai.toolCall.id", callID)
|
|
3239
3452
|
]
|
|
3240
3453
|
});
|
|
@@ -3259,7 +3472,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
3259
3472
|
startTimeUnixNano,
|
|
3260
3473
|
parent: spanParent,
|
|
3261
3474
|
eventId: state2.currentEventId,
|
|
3262
|
-
name:
|
|
3475
|
+
name: tool2,
|
|
3263
3476
|
liveSpan: liveTaskSpan
|
|
3264
3477
|
});
|
|
3265
3478
|
} else {
|
|
@@ -3267,7 +3480,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
3267
3480
|
startTimeUnixNano,
|
|
3268
3481
|
parent: spanParent,
|
|
3269
3482
|
eventId: state2.currentEventId,
|
|
3270
|
-
name:
|
|
3483
|
+
name: tool2
|
|
3271
3484
|
});
|
|
3272
3485
|
}
|
|
3273
3486
|
} catch (err) {
|
|
@@ -3282,7 +3495,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
3282
3495
|
// ------------------------------------------------------------------
|
|
3283
3496
|
"tool.execute.after": async (toolInput, result) => {
|
|
3284
3497
|
try {
|
|
3285
|
-
const { tool, sessionID, callID, args } = toolInput;
|
|
3498
|
+
const { tool: tool2, sessionID, callID, args } = toolInput;
|
|
3286
3499
|
const state2 = sessions.get(sessionID);
|
|
3287
3500
|
if (!state2 || !state2.currentEventId) return;
|
|
3288
3501
|
if (args === void 0 || args === null)
|
|
@@ -3292,7 +3505,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
3292
3505
|
state2.toolSpanStarts.delete(callID);
|
|
3293
3506
|
const resultMetadata = result.metadata;
|
|
3294
3507
|
const childSessionId = resultMetadata == null ? void 0 : resultMetadata["sessionId"];
|
|
3295
|
-
if (
|
|
3508
|
+
if (tool2 === "task" && childSessionId) {
|
|
3296
3509
|
mapChildSessionToParent.set(childSessionId, { parentId: sessionID });
|
|
3297
3510
|
attachChildSessionToParentTask(childSessionId, sessionID, callID, "tool.execute.after");
|
|
3298
3511
|
}
|
|
@@ -3317,7 +3530,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
3317
3530
|
endTimeUnixNano,
|
|
3318
3531
|
attributes: [
|
|
3319
3532
|
attrString("ai.operationId", "ai.toolCall"),
|
|
3320
|
-
attrString("ai.toolCall.name",
|
|
3533
|
+
attrString("ai.toolCall.name", tool2),
|
|
3321
3534
|
attrString("ai.toolCall.id", callID),
|
|
3322
3535
|
attrString("ai.toolCall.args", toolCallArgs),
|
|
3323
3536
|
attrString("ai.toolCall.result", toolResult)
|
|
@@ -3325,7 +3538,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
3325
3538
|
});
|
|
3326
3539
|
}
|
|
3327
3540
|
}
|
|
3328
|
-
if (
|
|
3541
|
+
if (tool2 === "task") {
|
|
3329
3542
|
markTaskCallFinished(sessionID, callID);
|
|
3330
3543
|
}
|
|
3331
3544
|
} catch (err) {
|
|
@@ -3378,16 +3591,24 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
|
|
|
3378
3591
|
}
|
|
3379
3592
|
}
|
|
3380
3593
|
};
|
|
3594
|
+
return {
|
|
3595
|
+
hooks,
|
|
3596
|
+
currentEventId(sessionID) {
|
|
3597
|
+
var _a;
|
|
3598
|
+
return (_a = sessions.get(sessionID)) == null ? void 0 : _a.currentEventId;
|
|
3599
|
+
}
|
|
3600
|
+
};
|
|
3381
3601
|
}
|
|
3382
3602
|
|
|
3383
3603
|
// src/index.ts
|
|
3384
|
-
function createRaindropHandle(eventShipper, traceShipper, appGitContext) {
|
|
3604
|
+
function createRaindropHandle(eventShipper, traceShipper, appGitContext, currentEventId) {
|
|
3385
3605
|
return {
|
|
3386
3606
|
users: {
|
|
3387
3607
|
async identify(users) {
|
|
3388
3608
|
await eventShipper.identify(users);
|
|
3389
3609
|
}
|
|
3390
3610
|
},
|
|
3611
|
+
currentEventId,
|
|
3391
3612
|
async flush() {
|
|
3392
3613
|
await Promise.all([eventShipper.flush(), traceShipper.flush()]);
|
|
3393
3614
|
},
|
|
@@ -3404,7 +3625,7 @@ function createRaindropHandle(eventShipper, traceShipper, appGitContext) {
|
|
|
3404
3625
|
};
|
|
3405
3626
|
}
|
|
3406
3627
|
async function plugin(input, opts = {}) {
|
|
3407
|
-
var _a, _b, _c;
|
|
3628
|
+
var _a, _b, _c, _d;
|
|
3408
3629
|
const config = loadConfig(input.directory);
|
|
3409
3630
|
function appLog(level, message) {
|
|
3410
3631
|
console.log(`[raindrop-ai/opencode-plugin] [${level}] ${message}`);
|
|
@@ -3448,12 +3669,53 @@ async function plugin(input, opts = {}) {
|
|
|
3448
3669
|
appGitContext
|
|
3449
3670
|
});
|
|
3450
3671
|
const worktree = (_c = input.worktree) != null ? _c : input.directory;
|
|
3451
|
-
const
|
|
3672
|
+
const tracing = createTracing(
|
|
3452
3673
|
config,
|
|
3453
3674
|
worktree,
|
|
3454
3675
|
input.directory,
|
|
3455
3676
|
eventShipper,
|
|
3456
3677
|
traceShipper
|
|
3457
3678
|
);
|
|
3458
|
-
|
|
3679
|
+
const hooks = tracing.hooks;
|
|
3680
|
+
const selfDiagnostics = normalizeSelfDiagnosticsConfig(
|
|
3681
|
+
(_d = opts.selfDiagnostics) != null ? _d : config.selfDiagnostics
|
|
3682
|
+
);
|
|
3683
|
+
if (selfDiagnostics && !config.writeKey.trim()) {
|
|
3684
|
+
appLog(
|
|
3685
|
+
"warn",
|
|
3686
|
+
`selfDiagnostics enabled but RAINDROP_WRITE_KEY is not set \u2014 the ${selfDiagnostics.toolName} tool is not registered (signals require the cloud destination).`
|
|
3687
|
+
);
|
|
3688
|
+
}
|
|
3689
|
+
const [firstSignalKey, ...restSignalKeys] = config.writeKey.trim() && selfDiagnostics ? selfDiagnostics.signalKeys : [];
|
|
3690
|
+
if (selfDiagnostics && firstSignalKey !== void 0) {
|
|
3691
|
+
const cfg = selfDiagnostics;
|
|
3692
|
+
hooks.tool = {
|
|
3693
|
+
[cfg.toolName]: (0, import_plugin.tool)({
|
|
3694
|
+
description: cfg.toolDescription,
|
|
3695
|
+
args: {
|
|
3696
|
+
category: import_plugin.tool.schema.enum([firstSignalKey, ...restSignalKeys]),
|
|
3697
|
+
detail: import_plugin.tool.schema.string()
|
|
3698
|
+
},
|
|
3699
|
+
async execute(args, ctx) {
|
|
3700
|
+
await trackSelfDiagnosticsSignal(
|
|
3701
|
+
args,
|
|
3702
|
+
cfg,
|
|
3703
|
+
eventShipper,
|
|
3704
|
+
tracing.currentEventId(ctx.sessionID),
|
|
3705
|
+
config.debug
|
|
3706
|
+
);
|
|
3707
|
+
return SELF_DIAGNOSTICS_ACK;
|
|
3708
|
+
}
|
|
3709
|
+
})
|
|
3710
|
+
};
|
|
3711
|
+
}
|
|
3712
|
+
return {
|
|
3713
|
+
...hooks,
|
|
3714
|
+
raindrop: createRaindropHandle(
|
|
3715
|
+
eventShipper,
|
|
3716
|
+
traceShipper,
|
|
3717
|
+
appGitContext,
|
|
3718
|
+
tracing.currentEventId
|
|
3719
|
+
)
|
|
3720
|
+
};
|
|
3459
3721
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,85 +1,12 @@
|
|
|
1
|
+
import { I as IdentifyInput, A as AppGitOptions, S as SelfDiagnosticsOptions } from './self-diagnostics-NiAq-SZ8.cjs';
|
|
2
|
+
export { a as SelfDiagnosticsSignalDefinition, b as SelfDiagnosticsSignalDefinitions } from './self-diagnostics-NiAq-SZ8.cjs';
|
|
1
3
|
import { Hooks, PluginInput } from '@opencode-ai/plugin';
|
|
2
4
|
|
|
3
|
-
/** Kept structural so public integration declarations do not depend on private schemas. */
|
|
4
|
-
type AppGitOptions = {
|
|
5
|
-
commitSha?: string | null;
|
|
6
|
-
commitDirty?: boolean | null;
|
|
7
|
-
branch?: string | null;
|
|
8
|
-
detectBranch?: boolean;
|
|
9
|
-
sourceDirectory?: string;
|
|
10
|
-
autoDetect?: boolean;
|
|
11
|
-
};
|
|
12
|
-
type IdentifyInput = {
|
|
13
|
-
userId: string;
|
|
14
|
-
traits?: Record<string, unknown>;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Run telemetry egress with OpenTelemetry tracing suppressed.
|
|
19
|
-
*
|
|
20
|
-
* Why this exists
|
|
21
|
-
* ---------------
|
|
22
|
-
* Raindrop integrations ship spans/events over HTTP with the global `fetch`
|
|
23
|
-
* (see {@link ../http.ts `postJson`}). When the host app also runs an OTel
|
|
24
|
-
* fetch/undici instrumentation — e.g. `@vercel/otel`'s `registerOTel`, which
|
|
25
|
-
* every Eve agent installs — that instrumentation wraps *our own* telemetry
|
|
26
|
-
* POSTs in a `fetch POST <endpoint>` span. Those spans are then handed to the
|
|
27
|
-
* very exporter that issued the request, so they get shipped right back to
|
|
28
|
-
* Raindrop and Workshop as standalone "runs" (and, because each export issues
|
|
29
|
-
* another fetch, they feed back on themselves). The result is a run list
|
|
30
|
-
* flooded with `fetch POST .../v1/traces`, `.../events/track_partial` and
|
|
31
|
-
* `.../live` entries that drown out the real agent turns — especially with
|
|
32
|
-
* sub-agents, where every sandbox runs its own instrumentation.
|
|
33
|
-
*
|
|
34
|
-
* The OTel-blessed fix is to mark the active context as "tracing suppressed"
|
|
35
|
-
* around the request; instrumentations check `isTracingSuppressed` and return
|
|
36
|
-
* a no-op span instead of recording one. We do this through a hook stashed on
|
|
37
|
-
* `globalThis` by the Node entrypoint ({@link ../index.node.ts}) so that:
|
|
38
|
-
* - `@opentelemetry/api` / `@opentelemetry/core` stay *optional* — core never
|
|
39
|
-
* hard-depends on them, and the hook is simply absent when they (and thus
|
|
40
|
-
* any instrumentation to suppress) are not installed; and
|
|
41
|
-
* - the browser bundle never pulls in `node:module`, mirroring how core
|
|
42
|
-
* injects `AsyncLocalStorage` via `RAINDROP_ASYNC_LOCAL_STORAGE`.
|
|
43
|
-
*
|
|
44
|
-
* When no hook is present the callback runs unchanged, so suppression is a
|
|
45
|
-
* best-effort no-op rather than a hard requirement.
|
|
46
|
-
*/
|
|
47
|
-
/** Hook signature: run `fn` with OTel tracing suppressed, returning its value. */
|
|
48
|
-
type SuppressTracingHook = <T>(fn: () => T) => T;
|
|
49
|
-
declare global {
|
|
50
|
-
var RAINDROP_SUPPRESS_TRACING: SuppressTracingHook | undefined;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
type ParentSpanContext = {
|
|
54
|
-
traceIdB64: string;
|
|
55
|
-
spanIdB64: string;
|
|
56
|
-
eventId: string;
|
|
57
|
-
};
|
|
58
|
-
interface ContextSpan {
|
|
59
|
-
readonly traceIdB64: string;
|
|
60
|
-
readonly spanIdB64: string;
|
|
61
|
-
readonly eventId: string;
|
|
62
|
-
log?(data: Record<string, unknown>): void;
|
|
63
|
-
}
|
|
64
|
-
interface AsyncLocalStorageLike<T> {
|
|
65
|
-
getStore(): T | undefined;
|
|
66
|
-
run<R>(store: T, callback: () => R): R;
|
|
67
|
-
enterWith?(store: T): void;
|
|
68
|
-
}
|
|
69
|
-
declare abstract class ContextManager {
|
|
70
|
-
abstract getParentSpanIds(): ParentSpanContext | undefined;
|
|
71
|
-
abstract runInContext<R>(span: ContextSpan, callback: () => R): R;
|
|
72
|
-
abstract getCurrentSpan(): ContextSpan | undefined;
|
|
73
|
-
abstract isReady(): boolean;
|
|
74
|
-
}
|
|
75
|
-
declare global {
|
|
76
|
-
var RAINDROP_CONTEXT_MANAGER: (new () => ContextManager) | undefined;
|
|
77
|
-
var RAINDROP_ASYNC_LOCAL_STORAGE: (new <T>() => AsyncLocalStorageLike<T>) | undefined;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
5
|
interface RaindropOpencodeOptions {
|
|
81
6
|
/** Application Git identity override. false disables enrichment. */
|
|
82
7
|
appGit?: AppGitOptions | false;
|
|
8
|
+
/** Built-in self-diagnostics: injects a `__raindrop_report` tool when enabled. */
|
|
9
|
+
selfDiagnostics?: SelfDiagnosticsOptions;
|
|
83
10
|
}
|
|
84
11
|
/**
|
|
85
12
|
* The Raindrop control handle exposed alongside the OpenCode hooks.
|
|
@@ -101,6 +28,8 @@ interface RaindropOpencodeHandle {
|
|
|
101
28
|
*/
|
|
102
29
|
identify(users: IdentifyInput | IdentifyInput[]): Promise<void>;
|
|
103
30
|
};
|
|
31
|
+
/** Raindrop eventId of the turn currently open for this OpenCode session, if any. */
|
|
32
|
+
currentEventId(sessionID: string): string | undefined;
|
|
104
33
|
/** Drain the buffered event + trace shippers. */
|
|
105
34
|
flush(): Promise<void>;
|
|
106
35
|
/** Flush and tear down the shippers (also wired into session teardown). */
|
|
@@ -129,4 +58,4 @@ type RaindropOpencodeHooks = Hooks & {
|
|
|
129
58
|
*/
|
|
130
59
|
declare function plugin(input: PluginInput, opts?: RaindropOpencodeOptions): Promise<RaindropOpencodeHooks>;
|
|
131
60
|
|
|
132
|
-
export {
|
|
61
|
+
export { IdentifyInput, type RaindropOpencodeHandle, type RaindropOpencodeHooks, type RaindropOpencodeOptions, SelfDiagnosticsOptions, plugin as default };
|